From 4661da673c5f8db7d2a65032b5d1f41c9c62373f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sat, 7 Sep 2024 14:23:08 +0200 Subject: [PATCH 01/71] Rewrite module --- OrchardCore.sln | 7 ++ .../OrchardCore.Rewrite/AdminMenu.cs | 38 +++++++ .../Drivers/RewriteSettingsDisplayDriver.cs | 99 +++++++++++++++++++ .../OrchardCore.Rewrite/Manifest.cs | 10 ++ .../Models/RewriteSettings.cs | 6 ++ .../OrchardCore.Rewrite.csproj | 24 +++++ .../OrchardCore.Rewrite/Permissions.cs | 25 +++++ .../Rules/ExcludeAdminUIRule.cs | 26 +++++ .../OrchardCore.Rewrite/Startup.cs | 58 +++++++++++ .../ViewModels/RewriteSettingsViewModel.cs | 6 ++ .../Views/RewriteSettings.Edit.cshtml | 9 ++ .../Views/_ViewImports.cshtml | 10 ++ ...rdCore.Application.Cms.Core.Targets.csproj | 1 + 13 files changed, 319 insertions(+) create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml diff --git a/OrchardCore.sln b/OrchardCore.sln index e2b57d6f876..9b5f6a9562c 100644 --- a/OrchardCore.sln +++ b/OrchardCore.sln @@ -525,6 +525,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Queries.Core", "src\OrchardCore\OrchardCore.Queries.Core\OrchardCore.Queries.Core.csproj", "{61B358F2-702C-40AA-9DF7-7121248FE6DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.Rewrite", "src\OrchardCore.Modules\OrchardCore.Rewrite\OrchardCore.Rewrite.csproj", "{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1389,6 +1391,10 @@ Global {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.Build.0 = Release|Any CPU + {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1629,6 +1635,7 @@ Global {E8A1097D-A65A-4B17-A3A2-F50D79552732} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} {61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} + {D0F8B342-BDA8-44CB-AA43-7A65C79636A2} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs new file mode 100644 index 00000000000..6cb54f3a3cc --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Localization; +using OrchardCore.Navigation; +using OrchardCore.Rewrite.Drivers; + +namespace OrchardCore.Rewrite; + +public sealed class AdminMenu : AdminNavigationProvider +{ + private static readonly RouteValueDictionary _routeValues = new() + { + { "area", "OrchardCore.Settings" }, + { "groupId", RewriteSettingsDisplayDriver.GroupId }, + }; + + public readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } + + protected override ValueTask BuildAsync(NavigationBuilder builder) + { + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Rewrite"], S["Rewrite"].PrefixPosition(), seo => seo + .Permission(Permissions.ManageRewrites) + .Action("Index", "Admin", _routeValues) + .LocalNav() + ) + ) + ); + + return ValueTask.CompletedTask; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs new file mode 100644 index 00000000000..70534ebe7b3 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs @@ -0,0 +1,99 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; +using Microsoft.Extensions.Localization; +using OrchardCore.DisplayManagement.Entities; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Views; +using OrchardCore.Environment.Shell; +using OrchardCore.Rewrite.Models; +using OrchardCore.Rewrite.ViewModels; +using OrchardCore.Settings; + +namespace OrchardCore.Rewrite.Drivers; + +internal class RewriteSettingsDisplayDriver : SiteDisplayDriver +{ + public const string GroupId = "Rewrite"; + + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IStringLocalizer S; + + public RewriteSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor, + IShellReleaseManager shellReleaseManager, + IStringLocalizer stringLocalizer) + { + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + _shellReleaseManager = shellReleaseManager; + S = stringLocalizer; + } + + protected override string SettingsGroupId => GroupId; + + public override async Task EditAsync(ISite site, RewriteSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageRewrites)) + { + return null; + } + + context.AddTenantReloadWarningWrapper(); + + return Initialize("RewriteSettings_Edit", model => + { + model.ApacheModRewrite = settings.ApacheModRewrite; + }).Location("Content:1") + .OnGroup(SettingsGroupId); + } + + public override async Task UpdateAsync(ISite site, RewriteSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageRewrites)) + { + return null; + } + + var viewModel = new RewriteSettingsViewModel(); + + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + + settings.ApacheModRewrite = viewModel.ApacheModRewrite; + + ValidateUrls(settings, context.Updater); + + if (context.Updater.ModelState.IsValid) + { + _shellReleaseManager.RequestRelease(); + } + + return await EditAsync(site, settings, context); + } + + private void ValidateUrls(RewriteSettings settings, IUpdateModel updater) + { + try + { + var rewriteOptions = new RewriteOptions(); + using var apacheModRewrite = new StringReader(settings.ApacheModRewrite); + rewriteOptions.AddApacheModRewrite(apacheModRewrite); + } + catch (FormatException ex) + { + updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), "Parsing error: " + ex.Message); + } + catch (NotImplementedException ex) + { + updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), "Parsing error: " + ex.Message); + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs new file mode 100644 index 00000000000..3db81b45551 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs @@ -0,0 +1,10 @@ +using OrchardCore.Modules.Manifest; + +[assembly: Module( + Name = "Rewrite", + Author = ManifestConstants.OrchardCoreTeam, + Website = ManifestConstants.OrchardCoreWebsite, + Version = ManifestConstants.OrchardCoreVersion, + Description = "The Rewrites module provides a way to rewrite URLs.", + Category = "Infrastructure" +)] diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs new file mode 100644 index 00000000000..5a63c51224d --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs @@ -0,0 +1,6 @@ +namespace OrchardCore.Rewrite.Models; + +public class RewriteSettings +{ + public string ApacheModRewrite { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj b/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj new file mode 100644 index 00000000000..e1f6e88787d --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj @@ -0,0 +1,24 @@ + + + + true + + OrchardCore Rewrite + + $(OCCMSDescription) + + Provides url rewrite capabilities. + + $(PackageTags) OrchardCoreCMS + + + + + + + + + + + + \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs new file mode 100644 index 00000000000..3d357d8c993 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs @@ -0,0 +1,25 @@ +using OrchardCore.Security.Permissions; + +namespace OrchardCore.Rewrite; + +public sealed class Permissions : IPermissionProvider +{ + public static readonly Permission ManageRewrites = new Permission("ManageRewrites", "Manage rewrites"); + + private readonly IEnumerable _allPermissions = + [ + ManageRewrites, + ]; + + public Task> GetPermissionsAsync() + => Task.FromResult(_allPermissions); + + public IEnumerable GetDefaultStereotypes() => + [ + new PermissionStereotype + { + Name = OrchardCoreConstants.Roles.Administrator, + Permissions = _allPermissions, + } + ]; +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs new file mode 100644 index 00000000000..f65226d4862 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace OrchardCore.Rewrite.Rules; + +internal class ExcludeAdminUIRule : IRule +{ + private readonly PathString _adminUrlPrefix; + + public ExcludeAdminUIRule(string adminUrlPrefix) + { + _adminUrlPrefix = new PathString("/" + adminUrlPrefix); + } + + public void ApplyRule(RewriteContext context) + { + if (context.HttpContext.Request.Path.StartsWithSegments(_adminUrlPrefix)) + { + context.Result = RuleResult.SkipRemainingRules; + } + else + { + context.Result = RuleResult.ContinueRules; + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs new file mode 100644 index 00000000000..6b1bea15e03 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Rewrite; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OrchardCore.Admin; +using OrchardCore.Navigation; +using OrchardCore.Security.Permissions; +using OrchardCore.Settings; +using OrchardCore.Rewrite.Rules; +using OrchardCore.Modules; +using OrchardCore.Rewrite.Models; +using OrchardCore.Rewrite.Drivers; +using OrchardCore.DisplayManagement.Handlers; + +namespace OrchardCore.Rewrite; + +public sealed class Startup : StartupBase +{ + public override int Order + => OrchardCoreConstants.ConfigureOrder.InfrastructureService; + + private readonly AdminOptions _adminOptions; + + public Startup(IOptions adminOptions) + { + _adminOptions = adminOptions.Value; + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddSiteDisplayDriver(); + services.AddScoped(); + } + + public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var siteService = app.ApplicationServices.GetRequiredService(); + var modRewriteSettings = await siteService.GetSettingsAsync(); + + var rewriteSettings = modRewriteSettings?.ApacheModRewrite; + if (rewriteSettings == null) + return; + + using var apacheModRewrite = new StringReader(rewriteSettings); + + var options = new RewriteOptions() + .AddApacheModRewrite(apacheModRewrite); + + if (options.Rules.Count > 0) + { + // Exclude admin ui requests to prevent accidental access bricking by provided rules + options.Rules.Insert(0, new ExcludeAdminUIRule(_adminOptions.AdminUrlPrefix)); + app.UseRewriter(options); + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs new file mode 100644 index 00000000000..81c3d42a235 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs @@ -0,0 +1,6 @@ +namespace OrchardCore.Rewrite.ViewModels; + +public class RewriteSettingsViewModel +{ + public string ApacheModRewrite { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml new file mode 100644 index 00000000000..12c2bfd5610 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml @@ -0,0 +1,9 @@ +@model OrchardCore.Rewrite.ViewModels.RewriteSettingsViewModel + +
+ + + +
+ +
\ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml new file mode 100644 index 00000000000..1fe15a604ba --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml @@ -0,0 +1,10 @@ +@* + For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 + +*@ + +@inherits OrchardCore.DisplayManagement.Razor.RazorPage + +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, OrchardCore.DisplayManagement +@addTagHelper *, OrchardCore.ResourceManagement diff --git a/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj b/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj index 156ba96f7a6..ecf03348396 100644 --- a/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj +++ b/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj @@ -99,6 +99,7 @@ + From 49f39397c7e1c59ee498d2befd93d4923d8c2e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sat, 7 Sep 2024 15:19:49 +0200 Subject: [PATCH 02/71] Fix warning as errors --- .../Drivers/RewriteSettingsDisplayDriver.cs | 6 +++--- .../OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs | 2 +- src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs index 70534ebe7b3..734e4e06e42 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs @@ -13,7 +13,7 @@ namespace OrchardCore.Rewrite.Drivers; -internal class RewriteSettingsDisplayDriver : SiteDisplayDriver +internal sealed class RewriteSettingsDisplayDriver : SiteDisplayDriver { public const string GroupId = "Rewrite"; @@ -89,11 +89,11 @@ private void ValidateUrls(RewriteSettings settings, IUpdateModel updater) } catch (FormatException ex) { - updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), "Parsing error: " + ex.Message); + updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } catch (NotImplementedException ex) { - updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), "Parsing error: " + ex.Message); + updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs index f65226d4862..44de2679aed 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs @@ -3,7 +3,7 @@ namespace OrchardCore.Rewrite.Rules; -internal class ExcludeAdminUIRule : IRule +internal sealed class ExcludeAdminUIRule : IRule { private readonly PathString _adminUrlPrefix; diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs index 6b1bea15e03..c2114683842 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs @@ -41,7 +41,9 @@ public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpoin var rewriteSettings = modRewriteSettings?.ApacheModRewrite; if (rewriteSettings == null) + { return; + } using var apacheModRewrite = new StringReader(rewriteSettings); From d7e6f6138f151b04fea2919c01681b9e95f34abd Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Mon, 9 Sep 2024 13:11:59 -0700 Subject: [PATCH 03/71] Update OrchardCore.Rewrite.csproj --- .../OrchardCore.Rewrite/OrchardCore.Rewrite.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj b/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj index e1f6e88787d..0ad3cc838ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj @@ -21,4 +21,4 @@ - \ No newline at end of file + From 171c170127648615fb36752fcbe609fdff1c8a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Tue, 10 Sep 2024 20:26:20 +0200 Subject: [PATCH 04/71] Code cleanup after code review --- .../Drivers/RewriteSettingsDisplayDriver.cs | 13 +++++++------ .../OrchardCore.Rewrite/Startup.cs | 12 +++--------- .../Views/RewriteSettings.Edit.cshtml | 2 -- .../OrchardCore.Rewrite/Views/_ViewImports.cshtml | 5 ----- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs index 734e4e06e42..1b21443bb70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs @@ -10,6 +10,7 @@ using OrchardCore.Rewrite.Models; using OrchardCore.Rewrite.ViewModels; using OrchardCore.Settings; +using OrchardCore.Mvc.ModelBinding; namespace OrchardCore.Rewrite.Drivers; @@ -20,7 +21,7 @@ internal sealed class RewriteSettingsDisplayDriver : SiteDisplayDriver UpdateAsync(ISite site, RewriteSettin return null; } - var viewModel = new RewriteSettingsViewModel(); + var model = new RewriteSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.ApacheModRewrite = viewModel.ApacheModRewrite; + settings.ApacheModRewrite = model.ApacheModRewrite; ValidateUrls(settings, context.Updater); @@ -89,11 +90,11 @@ private void ValidateUrls(RewriteSettings settings, IUpdateModel updater) } catch (FormatException ex) { - updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); + updater.ModelState.AddModelError(Prefix, nameof(RewriteSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } catch (NotImplementedException ex) { - updater.ModelState.AddModelError(nameof(settings.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); + updater.ModelState.AddModelError(Prefix, nameof(RewriteSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs index c2114683842..67407133dff 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs @@ -29,9 +29,9 @@ public Startup(IOptions adminOptions) public override void ConfigureServices(IServiceCollection services) { - services.AddScoped(); + services.AddNavigationProvider(); services.AddSiteDisplayDriver(); - services.AddScoped(); + services.AddPermissionProvider(); } public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) @@ -39,13 +39,7 @@ public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpoin var siteService = app.ApplicationServices.GetRequiredService(); var modRewriteSettings = await siteService.GetSettingsAsync(); - var rewriteSettings = modRewriteSettings?.ApacheModRewrite; - if (rewriteSettings == null) - { - return; - } - - using var apacheModRewrite = new StringReader(rewriteSettings); + using var apacheModRewrite = new StringReader(modRewriteSettings.ApacheModRewrite ?? string.Empty); var options = new RewriteOptions() .AddApacheModRewrite(apacheModRewrite); diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml index 12c2bfd5610..10d2d9903c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml @@ -5,5 +5,3 @@ - -
\ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml index 1fe15a604ba..252fd654bb8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml @@ -1,8 +1,3 @@ -@* - For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 - -*@ - @inherits OrchardCore.DisplayManagement.Razor.RazorPage @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers From 0f2914ada039b7ec01d7b568e35a5186d13f9b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 11 Sep 2024 19:49:28 +0200 Subject: [PATCH 05/71] Rename module --- OrchardCore.sln | 2 +- .../AdminMenu.cs | 8 ++++---- .../Drivers/RewriteSettingsDisplayDriver.cs | 12 ++++++------ .../Manifest.cs | 4 ++-- .../Models/RewriteSettings.cs | 2 +- .../OrchardCore.UrlRewriting.csproj} | 0 .../Permissions.cs | 6 +++--- .../Rules/ExcludeAdminUIRule.cs | 2 +- .../Startup.cs | 8 ++++---- .../ViewModels/RewriteSettingsViewModel.cs | 2 +- .../Views/RewriteSettings.Edit.cshtml | 2 +- .../Views/_ViewImports.cshtml | 0 .../OrchardCore.Application.Cms.Core.Targets.csproj | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/AdminMenu.cs (79%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Drivers/RewriteSettingsDisplayDriver.cs (93%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Manifest.cs (65%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Models/RewriteSettings.cs (65%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite/OrchardCore.Rewrite.csproj => OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj} (100%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Permissions.cs (74%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Rules/ExcludeAdminUIRule.cs (93%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Startup.cs (92%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/ViewModels/RewriteSettingsViewModel.cs (66%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Views/RewriteSettings.Edit.cshtml (80%) rename src/OrchardCore.Modules/{OrchardCore.Rewrite => OrchardCore.UrlRewriting}/Views/_ViewImports.cshtml (100%) diff --git a/OrchardCore.sln b/OrchardCore.sln index 9b5f6a9562c..4a6f17ce68e 100644 --- a/OrchardCore.sln +++ b/OrchardCore.sln @@ -525,7 +525,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Queries.Core", "src\OrchardCore\OrchardCore.Queries.Core\OrchardCore.Queries.Core.csproj", "{61B358F2-702C-40AA-9DF7-7121248FE6DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.Rewrite", "src\OrchardCore.Modules\OrchardCore.Rewrite\OrchardCore.Rewrite.csproj", "{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.UrlRewriting", "src\OrchardCore.Modules\OrchardCore.UrlRewriting\OrchardCore.UrlRewriting.csproj", "{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs similarity index 79% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index 6cb54f3a3cc..16d4fa2a001 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -using OrchardCore.Rewrite.Drivers; +using OrchardCore.UrlRewriting.Drivers; -namespace OrchardCore.Rewrite; +namespace OrchardCore.UrlRewriting; public sealed class AdminMenu : AdminNavigationProvider { @@ -25,8 +25,8 @@ protected override ValueTask BuildAsync(NavigationBuilder builder) builder .Add(S["Configuration"], configuration => configuration .Add(S["Settings"], settings => settings - .Add(S["Rewrite"], S["Rewrite"].PrefixPosition(), seo => seo - .Permission(Permissions.ManageRewrites) + .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), seo => seo + .Permission(Permissions.ManageUrlRewriting) .Action("Index", "Admin", _routeValues) .LocalNav() ) diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs similarity index 93% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs index 1b21443bb70..23fe5939395 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Drivers/RewriteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs @@ -7,16 +7,16 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; using OrchardCore.Environment.Shell; -using OrchardCore.Rewrite.Models; -using OrchardCore.Rewrite.ViewModels; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.ViewModels; using OrchardCore.Settings; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Rewrite.Drivers; +namespace OrchardCore.UrlRewriting.Drivers; internal sealed class RewriteSettingsDisplayDriver : SiteDisplayDriver { - public const string GroupId = "Rewrite"; + public const string GroupId = "UrlRewriting"; private readonly IAuthorizationService _authorizationService; private readonly IHttpContextAccessor _httpContextAccessor; @@ -41,7 +41,7 @@ public override async Task EditAsync(ISite site, RewriteSettings { var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageRewrites)) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUrlRewriting)) { return null; } @@ -59,7 +59,7 @@ public override async Task UpdateAsync(ISite site, RewriteSettin { var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageRewrites)) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUrlRewriting)) { return null; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs similarity index 65% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs index 3db81b45551..9678e3a626d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs @@ -1,10 +1,10 @@ using OrchardCore.Modules.Manifest; [assembly: Module( - Name = "Rewrite", + Name = "UrlRewriting", Author = ManifestConstants.OrchardCoreTeam, Website = ManifestConstants.OrchardCoreWebsite, Version = ManifestConstants.OrchardCoreVersion, - Description = "The Rewrites module provides a way to rewrite URLs.", + Description = "The UrlRewriting module enables URL rewrites and redirects for incoming requests.", Category = "Infrastructure" )] diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs similarity index 65% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs index 5a63c51224d..1b2ff22039f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Models/RewriteSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.Rewrite.Models; +namespace OrchardCore.UrlRewriting.Models; public class RewriteSettings { diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj similarity index 100% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/OrchardCore.Rewrite.csproj rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs similarity index 74% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs index 3d357d8c993..a9917dd365d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Permissions.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs @@ -1,14 +1,14 @@ using OrchardCore.Security.Permissions; -namespace OrchardCore.Rewrite; +namespace OrchardCore.UrlRewriting; public sealed class Permissions : IPermissionProvider { - public static readonly Permission ManageRewrites = new Permission("ManageRewrites", "Manage rewrites"); + public static readonly Permission ManageUrlRewriting = new Permission("ManageUrlRewriting", "Manage URLs rewrites"); private readonly IEnumerable _allPermissions = [ - ManageRewrites, + ManageUrlRewriting, ]; public Task> GetPermissionsAsync() diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs similarity index 93% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs index 44de2679aed..02bc0b2d4ab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Rules/ExcludeAdminUIRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite; -namespace OrchardCore.Rewrite.Rules; +namespace OrchardCore.UrlRewriting.Rules; internal sealed class ExcludeAdminUIRule : IRule { diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs similarity index 92% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 67407133dff..5f13cec82f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -7,13 +7,13 @@ using OrchardCore.Navigation; using OrchardCore.Security.Permissions; using OrchardCore.Settings; -using OrchardCore.Rewrite.Rules; using OrchardCore.Modules; -using OrchardCore.Rewrite.Models; -using OrchardCore.Rewrite.Drivers; +using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Drivers; using OrchardCore.DisplayManagement.Handlers; -namespace OrchardCore.Rewrite; +namespace OrchardCore.UrlRewriting; public sealed class Startup : StartupBase { diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs similarity index 66% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs index 81c3d42a235..733e19da494 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/ViewModels/RewriteSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.Rewrite.ViewModels; +namespace OrchardCore.UrlRewriting.ViewModels; public class RewriteSettingsViewModel { diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml similarity index 80% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml index 10d2d9903c9..894d383d905 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/RewriteSettings.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml @@ -1,4 +1,4 @@ -@model OrchardCore.Rewrite.ViewModels.RewriteSettingsViewModel +@model OrchardCore.UrlRewriting.ViewModels.RewriteSettingsViewModel
diff --git a/src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml similarity index 100% rename from src/OrchardCore.Modules/OrchardCore.Rewrite/Views/_ViewImports.cshtml rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml diff --git a/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj b/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj index ecf03348396..9d7741ed57d 100644 --- a/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj +++ b/src/OrchardCore/OrchardCore.Application.Cms.Core.Targets/OrchardCore.Application.Cms.Core.Targets.csproj @@ -99,7 +99,7 @@ - + From 3cbccfb2726352debfd9d8bd418fecf371d6bbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Fri, 13 Sep 2024 07:51:45 +0200 Subject: [PATCH 06/71] Rename classes to match module name --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 2 +- ...s => UrlRewritingSettingsDisplayDriver.cs} | 25 ++++++++----------- .../OrchardCore.UrlRewriting/Manifest.cs | 4 +-- ...iteSettings.cs => UrlRewritingSettings.cs} | 2 +- .../OrchardCore.UrlRewriting/Startup.cs | 4 +-- ...el.cs => UrlRewritingSettingsViewModel.cs} | 2 +- ...shtml => UrlRewritingSettings.Edit.cshtml} | 2 +- 7 files changed, 19 insertions(+), 22 deletions(-) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/{RewriteSettingsDisplayDriver.cs => UrlRewritingSettingsDisplayDriver.cs} (73%) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/{RewriteSettings.cs => UrlRewritingSettings.cs} (74%) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/{RewriteSettingsViewModel.cs => UrlRewritingSettingsViewModel.cs} (70%) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/{RewriteSettings.Edit.cshtml => UrlRewritingSettings.Edit.cshtml} (79%) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index 16d4fa2a001..57f27c1a6e3 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -10,7 +10,7 @@ public sealed class AdminMenu : AdminNavigationProvider private static readonly RouteValueDictionary _routeValues = new() { { "area", "OrchardCore.Settings" }, - { "groupId", RewriteSettingsDisplayDriver.GroupId }, + { "groupId", UrlRewritingSettingsDisplayDriver.GroupId }, }; public readonly IStringLocalizer S; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs similarity index 73% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs index 23fe5939395..cd41710c5c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs @@ -14,7 +14,7 @@ namespace OrchardCore.UrlRewriting.Drivers; -internal sealed class RewriteSettingsDisplayDriver : SiteDisplayDriver +internal sealed class UrlRewritingSettingsDisplayDriver : SiteDisplayDriver { public const string GroupId = "UrlRewriting"; @@ -23,11 +23,11 @@ internal sealed class RewriteSettingsDisplayDriver : SiteDisplayDriver stringLocalizer) + IStringLocalizer stringLocalizer) { _authorizationService = authorizationService; _httpContextAccessor = httpContextAccessor; @@ -37,7 +37,7 @@ public RewriteSettingsDisplayDriver( protected override string SettingsGroupId => GroupId; - public override async Task EditAsync(ISite site, RewriteSettings settings, BuildEditorContext context) + public override async Task EditAsync(ISite site, UrlRewritingSettings settings, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; @@ -48,14 +48,14 @@ public override async Task EditAsync(ISite site, RewriteSettings context.AddTenantReloadWarningWrapper(); - return Initialize("RewriteSettings_Edit", model => + return Initialize("UrlRewritingSettings_Edit", model => { model.ApacheModRewrite = settings.ApacheModRewrite; }).Location("Content:1") .OnGroup(SettingsGroupId); } - public override async Task UpdateAsync(ISite site, RewriteSettings settings, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, UrlRewritingSettings settings, UpdateEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; @@ -64,7 +64,7 @@ public override async Task UpdateAsync(ISite site, RewriteSettin return null; } - var model = new RewriteSettingsViewModel(); + var model = new UrlRewritingSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); @@ -72,15 +72,12 @@ public override async Task UpdateAsync(ISite site, RewriteSettin ValidateUrls(settings, context.Updater); - if (context.Updater.ModelState.IsValid) - { - _shellReleaseManager.RequestRelease(); - } + _shellReleaseManager.RequestRelease(); return await EditAsync(site, settings, context); } - private void ValidateUrls(RewriteSettings settings, IUpdateModel updater) + private void ValidateUrls(UrlRewritingSettings settings, IUpdateModel updater) { try { @@ -90,11 +87,11 @@ private void ValidateUrls(RewriteSettings settings, IUpdateModel updater) } catch (FormatException ex) { - updater.ModelState.AddModelError(Prefix, nameof(RewriteSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); + updater.ModelState.AddModelError(Prefix, nameof(UrlRewritingSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } catch (NotImplementedException ex) { - updater.ModelState.AddModelError(Prefix, nameof(RewriteSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); + updater.ModelState.AddModelError(Prefix, nameof(UrlRewritingSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs index 9678e3a626d..3feadfb2583 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs @@ -1,10 +1,10 @@ using OrchardCore.Modules.Manifest; [assembly: Module( - Name = "UrlRewriting", + Name = "URL Rewriting", Author = ManifestConstants.OrchardCoreTeam, Website = ManifestConstants.OrchardCoreWebsite, Version = ManifestConstants.OrchardCoreVersion, - Description = "The UrlRewriting module enables URL rewrites and redirects for incoming requests.", + Description = "Enables URL rewrites and redirects for incoming requests.", Category = "Infrastructure" )] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs similarity index 74% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs index 1b2ff22039f..453d20be301 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.Models; -public class RewriteSettings +public class UrlRewritingSettings { public string ApacheModRewrite { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 5f13cec82f1..97bb936b553 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -30,14 +30,14 @@ public Startup(IOptions adminOptions) public override void ConfigureServices(IServiceCollection services) { services.AddNavigationProvider(); - services.AddSiteDisplayDriver(); + services.AddSiteDisplayDriver(); services.AddPermissionProvider(); } public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { var siteService = app.ApplicationServices.GetRequiredService(); - var modRewriteSettings = await siteService.GetSettingsAsync(); + var modRewriteSettings = await siteService.GetSettingsAsync(); using var apacheModRewrite = new StringReader(modRewriteSettings.ApacheModRewrite ?? string.Empty); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs similarity index 70% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs index 733e19da494..75ab6e42f82 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public class RewriteSettingsViewModel +public class UrlRewritingSettingsViewModel { public string ApacheModRewrite { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml similarity index 79% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml index 894d383d905..43e8b83ce04 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteSettings.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml @@ -1,4 +1,4 @@ -@model OrchardCore.UrlRewriting.ViewModels.RewriteSettingsViewModel +@model OrchardCore.UrlRewriting.ViewModels.UrlRewritingSettingsViewModel
From 2eeefea406aa1690b07a3bfa60617037bc515b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sun, 15 Sep 2024 12:21:42 +0200 Subject: [PATCH 07/71] - Constant for module order - Permissions refactor work --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 4 ++-- .../Drivers/UrlRewritingSettingsDisplayDriver.cs | 5 +++-- .../OrchardCore.UrlRewriting/Permissions.cs | 4 +--- .../OrchardCore.UrlRewriting/Startup.cs | 2 +- .../OrchardCore.UrlRewriting/UrlRewritingPermissions.cs | 8 ++++++++ 5 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissions.cs diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index 57f27c1a6e3..c3955c9cc30 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -25,8 +25,8 @@ protected override ValueTask BuildAsync(NavigationBuilder builder) builder .Add(S["Configuration"], configuration => configuration .Add(S["Settings"], settings => settings - .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), seo => seo - .Permission(Permissions.ManageUrlRewriting) + .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting + .Permission(UrlRewritingPermissions.ManageUrlRewriting) .Action("Index", "Admin", _routeValues) .LocalNav() ) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs index cd41710c5c2..ab8423abfb0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs @@ -21,6 +21,7 @@ internal sealed class UrlRewritingSettingsDisplayDriver : SiteDisplayDriver EditAsync(ISite site, UrlRewritingSet { var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUrlRewriting)) + if (!await _authorizationService.AuthorizeAsync(user, UrlRewritingPermissions.ManageUrlRewriting)) { return null; } @@ -59,7 +60,7 @@ public override async Task UpdateAsync(ISite site, UrlRewritingS { var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageUrlRewriting)) + if (!await _authorizationService.AuthorizeAsync(user, UrlRewritingPermissions.ManageUrlRewriting)) { return null; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs index a9917dd365d..587f91126cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs @@ -4,11 +4,9 @@ namespace OrchardCore.UrlRewriting; public sealed class Permissions : IPermissionProvider { - public static readonly Permission ManageUrlRewriting = new Permission("ManageUrlRewriting", "Manage URLs rewrites"); - private readonly IEnumerable _allPermissions = [ - ManageUrlRewriting, + UrlRewritingPermissions.ManageUrlRewriting, ]; public Task> GetPermissionsAsync() diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 97bb936b553..43ee767858b 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -18,7 +18,7 @@ namespace OrchardCore.UrlRewriting; public sealed class Startup : StartupBase { public override int Order - => OrchardCoreConstants.ConfigureOrder.InfrastructureService; + => OrchardCoreConstants.ConfigureOrder.UrlRewriting; private readonly AdminOptions _adminOptions; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissions.cs new file mode 100644 index 00000000000..3d3271da7a3 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissions.cs @@ -0,0 +1,8 @@ +using OrchardCore.Security.Permissions; + +namespace OrchardCore.UrlRewriting; + +public static class UrlRewritingPermissions +{ + public static readonly Permission ManageUrlRewriting = new Permission("ManageUrlRewriting", "Manage URLs rewrites"); +} From 40bf27b4bb980934999d2a04914e5e40a4da188f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sun, 15 Sep 2024 17:04:13 +0200 Subject: [PATCH 08/71] - Introduce ConfigureOptions - Rename Permissions class to UrlRewritingPermissionProvider --- .../Options/RewriteOptionsConfiguration.cs | 38 +++++++++++++++++++ .../OrchardCore.UrlRewriting/Startup.cs | 33 +++------------- ...s.cs => UrlRewritingPermissionProvider.cs} | 2 +- .../OrchardCoreConstants.cs | 3 ++ 4 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/{Permissions.cs => UrlRewritingPermissionProvider.cs} (88%) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs new file mode 100644 index 00000000000..3fd31cd742b --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Rewrite; +using Microsoft.Extensions.Options; +using OrchardCore.Admin; +using OrchardCore.Settings; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Rules; + +namespace OrchardCore.UrlRewriting.Options; + +public sealed class RewriteOptionsConfiguration : IConfigureOptions +{ + private readonly ISiteService _siteService; + + private readonly AdminOptions _adminOptions; + + public RewriteOptionsConfiguration(ISiteService siteService, IOptions adminOptions) + { + _siteService = siteService; + _adminOptions = adminOptions.Value; + } + + public void Configure(RewriteOptions options) + { + var settings = _siteService.GetSettingsAsync() + .GetAwaiter() + .GetResult(); + + using var apacheModRewrite = new StringReader(settings.ApacheModRewrite ?? string.Empty); + + options.AddApacheModRewrite(apacheModRewrite); + + if (options.Rules.Count > 0) + { + // Exclude admin ui requests to prevent accidental access bricking by provided rules + options.Rules.Insert(0, new ExcludeAdminUIRule(_adminOptions.AdminUrlPrefix)); + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 43ee767858b..bb18ddfec49 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -3,15 +3,12 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using OrchardCore.Admin; using OrchardCore.Navigation; using OrchardCore.Security.Permissions; -using OrchardCore.Settings; using OrchardCore.Modules; -using OrchardCore.UrlRewriting.Rules; -using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Drivers; using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.UrlRewriting.Options; namespace OrchardCore.UrlRewriting; @@ -20,35 +17,17 @@ public sealed class Startup : StartupBase public override int Order => OrchardCoreConstants.ConfigureOrder.UrlRewriting; - private readonly AdminOptions _adminOptions; - - public Startup(IOptions adminOptions) - { - _adminOptions = adminOptions.Value; - } - public override void ConfigureServices(IServiceCollection services) { services.AddNavigationProvider(); services.AddSiteDisplayDriver(); - services.AddPermissionProvider(); + services.AddPermissionProvider(); + services.AddTransient, RewriteOptionsConfiguration>(); } - public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - var siteService = app.ApplicationServices.GetRequiredService(); - var modRewriteSettings = await siteService.GetSettingsAsync(); - - using var apacheModRewrite = new StringReader(modRewriteSettings.ApacheModRewrite ?? string.Empty); - - var options = new RewriteOptions() - .AddApacheModRewrite(apacheModRewrite); - - if (options.Rules.Count > 0) - { - // Exclude admin ui requests to prevent accidental access bricking by provided rules - options.Rules.Insert(0, new ExcludeAdminUIRule(_adminOptions.AdminUrlPrefix)); - app.UseRewriter(options); - } + var rewriteOptions = serviceProvider.GetRequiredService>().Value; + app.UseRewriter(rewriteOptions); } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs similarity index 88% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs index 587f91126cc..6d78c5b0f8d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Permissions.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs @@ -2,7 +2,7 @@ namespace OrchardCore.UrlRewriting; -public sealed class Permissions : IPermissionProvider +public sealed class UrlRewritingPermissionProvider : IPermissionProvider { private readonly IEnumerable _allPermissions = [ diff --git a/src/OrchardCore/OrchardCore.Abstractions/OrchardCoreConstants.cs b/src/OrchardCore/OrchardCore.Abstractions/OrchardCoreConstants.cs index 3e18723abe3..6ccb9b9fab1 100644 --- a/src/OrchardCore/OrchardCore.Abstractions/OrchardCoreConstants.cs +++ b/src/OrchardCore/OrchardCore.Abstractions/OrchardCoreConstants.cs @@ -71,6 +71,9 @@ public static class ConfigureOrder public const int AdminPages = 1000; + // The UrlRewriting module should be registered before any other module that deals with URLs. + public const int UrlRewriting = InfrastructureService + 100; + // Services that should always be registered before everything else. public const int InfrastructureService = int.MinValue + 100; } From 6d1f4b4a812806c6c64a058c5b6d20d1cc01562a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 18 Sep 2024 23:58:26 +0200 Subject: [PATCH 09/71] UrlRewriting UI - part 1 --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 3 +- .../Controllers/AdminController.cs | 56 ++++++++++ .../Rules/RuleBuilder.cs | 11 ++ .../CreateUrlRewriteRuleViewModel.cs | 50 +++++++++ .../Views/Admin/CreateRule.cshtml | 103 ++++++++++++++++++ .../Views/Admin/Index.cshtml | 17 +++ .../NavigationItemText-urlrewriting.Id.cshtml | 1 + .../Views/_ViewImports.cshtml | 1 + 8 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index c3955c9cc30..b471ed24c13 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -26,8 +26,9 @@ protected override ValueTask BuildAsync(NavigationBuilder builder) .Add(S["Configuration"], configuration => configuration .Add(S["Settings"], settings => settings .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting + .AddClass("urlrewriting").Id("urlrewriting") .Permission(UrlRewritingPermissions.ManageUrlRewriting) - .Action("Index", "Admin", _routeValues) + .Action("Index", "Admin", "OrchardCore.UrlRewriting") .LocalNav() ) ) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs new file mode 100644 index 00000000000..caffa337bdc --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Localization; +using OrchardCore.Admin; +using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.UrlRewriting.Controllers; + +[Admin("UrlRewriting/{action}/{id?}", "UrlRewriting{action}")] +public sealed class AdminController : Controller +{ + private readonly IAuthorizationService _authorizationService; + + internal readonly IStringLocalizer S; + + public AdminController( + IAuthorizationService authorizationService, + IStringLocalizer stringLocalizer) + { + _authorizationService = authorizationService; + S = stringLocalizer; + } + + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + return View(); + } + + [Admin("UrlRewriting/Create", "CreateRule")] + public async Task CreateRule() + { + var model = new CreateUrlRewriteRuleViewModel(); + + BuildViewModel(model); + + return View(model); + } + + private void BuildViewModel(CreateUrlRewriteRuleViewModel model) + { + model.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); + model.AvailableActions.Add(new SelectListItem() { Text = S["Redirect"], Value = ((int)RuleAction.Redirect).ToString() }); + + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Moved Permanently (301)"], Value = ((int)RedirectType.MovedPermanently301).ToString() }); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Found (302)"], Value = ((int)RedirectType.Found302).ToString() }); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Temporary Redirect (307)"], Value = ((int)RedirectType.TemporaryRedirect307).ToString() }); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Pernament Redirect (308)"], Value = ((int)RedirectType.PernamentRedirect308).ToString() }); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs new file mode 100644 index 00000000000..b10c0eb5f1a --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs @@ -0,0 +1,11 @@ +namespace OrchardCore.UrlRewriting.Rules; + +public enum RuleAction +{ + Rewrite, + Redirect +} + +internal class RuleBuilder +{ +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs new file mode 100644 index 00000000000..57d03a9af2a --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Rendering; +using OrchardCore.UrlRewriting.Rules; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class CreateUrlRewriteRuleViewModel +{ + public string DisplayName { get; set; } + + public string Pattern { get; set; } + + public bool IgnoreCase { get; set; } + + public RuleAction RuleAction { get; set; } + + public RewriteActionViewModel RewriteAction { get; set; } = new RewriteActionViewModel(); + + public RedirectActionViewModel RedirectAction { get; set; } = new RedirectActionViewModel(); + + [BindNever] + public List AvailableActions { get; set; } = []; +} + +public enum RedirectType +{ + MovedPermanently301, + Found302, + TemporaryRedirect307, + PernamentRedirect308 +} + +public class RedirectActionViewModel +{ + public string RedirectUrl { get; set; } + + public bool AppendQueryString { get; set; } = true; + + public RedirectType RedirectType { get; set; } = RedirectType.TemporaryRedirect307; + + [BindNever] + public List AvailableRedirectTypes { get; set; } = []; +} + +public class RewriteActionViewModel +{ + public string RewriteUrl { get; set; } + + public bool AppendQueryString { get; set; } = true; +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml new file mode 100644 index 00000000000..4f231891a00 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml @@ -0,0 +1,103 @@ +@model CreateUrlRewriteRuleViewModel + +

@RenderTitleSegments(T["New Rule"])

+ +
+ @Html.ValidationSummary() + +
+ + + @T["Rule name."] +
+ +
+ + + @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] +
+ +
+
+ + +
+ @T["When checked, the pattern will be case insensitive."] +
+ +
+
+ + +
+ @T["Action type for matched URL."] +
+ +
+
+ + +
+ +
+
+ + +
+
+
+ +
+
+ + +
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+ +
+ + + @T["Cancel"] +
+
+ + \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml new file mode 100644 index 00000000000..d4110ff366e --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -0,0 +1,17 @@ +

@RenderTitleSegments(T["URL Rewriting"])

+ +
+
+
+
+ +
+ +
+
+
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml new file mode 100644 index 00000000000..9272feaea6f --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml @@ -0,0 +1 @@ +@T["URL Rewriting"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml index 252fd654bb8..fd169e3d7c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml @@ -3,3 +3,4 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, OrchardCore.DisplayManagement @addTagHelper *, OrchardCore.ResourceManagement +@using OrchardCore.UrlRewriting.ViewModels \ No newline at end of file From 8080821dceec2e8b8e32711b869e528a2700622a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sun, 22 Sep 2024 08:33:26 +0200 Subject: [PATCH 10/71] UrlRewriting UI - part 2 --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 6 -- .../Controllers/AdminController.cs | 86 ++++++++++++++++++- .../Models/UrlRewritingSettings.cs | 13 +++ .../Rules/RuleBuilder.cs | 71 +++++++++++++-- .../CreateUrlRewriteRuleViewModel.cs | 11 ++- .../Views/Admin/CreateRule.cshtml | 4 +- 6 files changed, 174 insertions(+), 17 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index b471ed24c13..cf6c05cf402 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -7,12 +7,6 @@ namespace OrchardCore.UrlRewriting; public sealed class AdminMenu : AdminNavigationProvider { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", UrlRewritingSettingsDisplayDriver.GroupId }, - }; - public readonly IStringLocalizer S; public AdminMenu(IStringLocalizer stringLocalizer) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index caffa337bdc..7ce2da3ea71 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -1,8 +1,14 @@ +using System.Text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Localization; using OrchardCore.Admin; +using OrchardCore.DisplayManagement; +using OrchardCore.DisplayManagement.Notify; +using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Rules; using OrchardCore.UrlRewriting.ViewModels; @@ -12,15 +18,24 @@ namespace OrchardCore.UrlRewriting.Controllers; public sealed class AdminController : Controller { private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; + //private readonly IDisplayManager _urlRewritingSettingsDisplayManager; internal readonly IStringLocalizer S; + internal readonly IHtmlLocalizer H; public AdminController( + //IDisplayManager urlRewritingSettingsDisplayManager, IAuthorizationService authorizationService, - IStringLocalizer stringLocalizer) + INotifier notifier, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) { + //_urlRewritingSettingsDisplayManager = urlRewritingSettingsDisplayManager; _authorizationService = authorizationService; + _notifier = notifier; S = stringLocalizer; + H = htmlLocalizer; } public async Task Index() @@ -36,6 +51,11 @@ public async Task Index() [Admin("UrlRewriting/Create", "CreateRule")] public async Task CreateRule() { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + var model = new CreateUrlRewriteRuleViewModel(); BuildViewModel(model); @@ -43,6 +63,70 @@ public async Task CreateRule() return View(model); } + [HttpPost, ActionName("CreateRule")] + public async Task CreateRulePOST(CreateUrlRewriteRuleViewModel viewModel) + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; + viewModel.Pattern = viewModel.Pattern ?? string.Empty; + viewModel.RewriteAction.RewriteUrl = viewModel.RewriteAction.RewriteUrl ?? string.Empty; + viewModel.RedirectAction.RedirectUrl = viewModel.RedirectAction.RedirectUrl ?? string.Empty; + + if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) + { + ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); + } + if (string.IsNullOrWhiteSpace(viewModel.Pattern)) + { + ModelState.AddModelError("Pattern", S["The Pattern can't be empty."]); + } + if (viewModel.RuleAction == RuleAction.Rewrite && string.IsNullOrEmpty(viewModel.RewriteAction.RewriteUrl)) + { + ModelState.AddModelError("RewriteAction.RewriteUrl", S["The Rewrite URL can't be empty."]); + } + if (viewModel.RuleAction == RuleAction.Redirect && string.IsNullOrEmpty(viewModel.RedirectAction.RedirectUrl)) + { + ModelState.AddModelError("RedirectAction.RedirectUrl", S["The Redirect URL can't be empty."]); + } + + // If basic validation is ok, do final check + if (ModelState.IsValid) + { + var rewriteRule = ApacheRuleBuilder.FromViewModel(viewModel, true); + + try + { + using var apacheModRewrite = new StringReader(rewriteRule); + var rewriteOptions = new RewriteOptions(); + rewriteOptions.AddApacheModRewrite(apacheModRewrite); + } + catch (FormatException ex) + { + ModelState.AddModelError("Pattern", S["Parsing error: {0}", ex.Message]); + } + catch (NotImplementedException ex) + { + ModelState.AddModelError("Pattern", S["Parsing error: {0}", ex.Message]); + } + } + + if (ModelState.IsValid) + { + // TODO - save settings/reload rules + await _notifier.SuccessAsync(H["URL rewriting rule created successfully."]); + + return RedirectToAction(nameof(Index)); + } + + BuildViewModel(viewModel); + + return View(viewModel); + } + private void BuildViewModel(CreateUrlRewriteRuleViewModel model) { model.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs index 453d20be301..4e7062f4192 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs @@ -3,4 +3,17 @@ namespace OrchardCore.UrlRewriting.Models; public class UrlRewritingSettings { public string ApacheModRewrite { get; set; } + + public List Rules { get; set; } +} + +public class Rule +{ + public string Name { get; set; } + + public string Pattern { get; set; } + + public string Substitution { get; set; } + + public string Flags { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs index b10c0eb5f1a..5a4c3450249 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs @@ -1,11 +1,70 @@ +using OrchardCore.UrlRewriting.ViewModels; +using System.Text; + namespace OrchardCore.UrlRewriting.Rules; -public enum RuleAction +public class ApacheRuleBuilder { - Rewrite, - Redirect -} + public static string FromViewModel(CreateUrlRewriteRuleViewModel viewModel, bool includeFlags = true) + { + var sbFlags = new StringBuilder(); -internal class RuleBuilder -{ + if (viewModel.IgnoreCase) + { + sbFlags.Append("NC,"); + }; + + if (viewModel.RuleAction == RuleAction.Rewrite) + { + if (viewModel.RewriteAction.AppendQueryString) + { + sbFlags.Append("QSA,"); + } + if (viewModel.RewriteAction.SkipFurtherRules == true) + { + sbFlags.Append("L,"); + } + } + + if (viewModel.RuleAction == RuleAction.Redirect) + { + if (viewModel.RedirectAction.AppendQueryString) + { + sbFlags.Append("QSA,"); + } + sbFlags.Append($"R={RedirectTypeToStatusCode(viewModel.RedirectAction.RedirectType)},"); + } + + if (sbFlags.Length > 0 && sbFlags[sbFlags.Length - 1] == ',') + { + sbFlags.Remove(sbFlags.Length - 1, 1); + } + + var sbRewrite = new StringBuilder(); + var replaceUrl = viewModel.RuleAction == RuleAction.Rewrite ? viewModel.RewriteAction.RewriteUrl : viewModel.RedirectAction.RedirectUrl; + + var flags = sbFlags.Length > 0 ? $"[{sbFlags}]" : ""; + if (flags.Length > 0 && includeFlags) + { + sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flags}"); + } + else + { + sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\""); + } + + return sbRewrite.ToString(); + } + + private static int RedirectTypeToStatusCode(RedirectType redirectType) + { + return redirectType switch + { + RedirectType.MovedPermanently301 => 301, + RedirectType.Found302 => 302, + RedirectType.TemporaryRedirect307 => 307, + RedirectType.PernamentRedirect308 => 308, + _ => 302, + }; + } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs index 57d03a9af2a..5cbb5059e0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -using OrchardCore.UrlRewriting.Rules; namespace OrchardCore.UrlRewriting.ViewModels; @@ -22,6 +21,12 @@ public class CreateUrlRewriteRuleViewModel public List AvailableActions { get; set; } = []; } +public enum RuleAction +{ + Rewrite, + Redirect +} + public enum RedirectType { MovedPermanently301, @@ -36,7 +41,7 @@ public class RedirectActionViewModel public bool AppendQueryString { get; set; } = true; - public RedirectType RedirectType { get; set; } = RedirectType.TemporaryRedirect307; + public RedirectType RedirectType { get; set; } = RedirectType.Found302; [BindNever] public List AvailableRedirectTypes { get; set; } = []; @@ -47,4 +52,6 @@ public class RewriteActionViewModel public string RewriteUrl { get; set; } public bool AppendQueryString { get; set; } = true; + + public bool SkipFurtherRules { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml index 4f231891a00..58cb5362b96 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml @@ -2,11 +2,11 @@

@RenderTitleSegments(T["New Rule"])

-
+ @Html.ValidationSummary()
- + @T["Rule name."]
From 23546cb85f7eb2bf8a848c593f35f8f1c0e50761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 25 Sep 2024 20:33:10 +0200 Subject: [PATCH 11/71] UrlRewriting UI - part 3 --- .../Controllers/AdminController.cs | 108 +++++++++-- .../Drivers/RewriteRuleDisplayDriver.cs | 122 +++++++++++++ .../Models/RewriteRule.cs | 28 +++ .../Models/RewriteRulesDocument.cs | 8 + .../Models/UrlRewritingSettings.cs | 13 +- .../OrchardCore.UrlRewriting.csproj | 1 + .../Rules/RuleBuilder.cs | 15 +- .../Rules/RuleValidator.cs | 32 ++++ .../Services/RewriteRulesStore.cs | 68 +++++++ .../OrchardCore.UrlRewriting/Startup.cs | 4 + ...leViewModel.cs => RewriteRuleViewModel.cs} | 2 +- .../ViewModels/RewriteRulesViewModel.cs | 8 + .../Views/Admin/CreateRule.cshtml | 101 +---------- .../Views/Admin/Index.cshtml | 169 +++++++++++++++++- .../Views/RewriteRule.Edit.cshtml | 10 ++ .../Views/RewriteRuleFields.Edit.cshtml | 97 ++++++++++ 16 files changed, 651 insertions(+), 135 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/{CreateUrlRewriteRuleViewModel.cs => RewriteRuleViewModel.cs} (96%) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 7ce2da3ea71..57db0633fc8 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -1,15 +1,15 @@ -using System.Text; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Localization; using OrchardCore.Admin; using OrchardCore.DisplayManagement; +using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Notify; using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.Services; using OrchardCore.UrlRewriting.ViewModels; namespace OrchardCore.UrlRewriting.Controllers; @@ -19,21 +19,27 @@ public sealed class AdminController : Controller { private readonly IAuthorizationService _authorizationService; private readonly INotifier _notifier; - //private readonly IDisplayManager _urlRewritingSettingsDisplayManager; + private readonly RewriteRulesStore _rewriteRulesStore; + private readonly IDisplayManager _rewriteRuleDisplayManager; + private readonly IUpdateModelAccessor _updateModelAccessor; internal readonly IStringLocalizer S; internal readonly IHtmlLocalizer H; public AdminController( - //IDisplayManager urlRewritingSettingsDisplayManager, + IDisplayManager rewriteRuleDisplayManager, IAuthorizationService authorizationService, + RewriteRulesStore rewriteRulesStore, INotifier notifier, IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) + IHtmlLocalizer htmlLocalizer, + IUpdateModelAccessor updateModelAccessor) { - //_urlRewritingSettingsDisplayManager = urlRewritingSettingsDisplayManager; + _rewriteRuleDisplayManager = rewriteRuleDisplayManager; _authorizationService = authorizationService; + _rewriteRulesStore = rewriteRulesStore; _notifier = notifier; + _updateModelAccessor = updateModelAccessor; S = stringLocalizer; H = htmlLocalizer; } @@ -45,9 +51,83 @@ public async Task Index() return Forbid(); } - return View(); + var rules = await _rewriteRulesStore.GetRewriteRulesAsync(); + + var model = new RewriteRulesViewModel + { + Rules = rules.Rules.ToList() + }; + + model.Rules.Add(new RewriteRule() { Name = "Rule One" }); + model.Rules.Add(new RewriteRule() { Name = "Rule Two" }); + model.Rules.Add(new RewriteRule() { Name = "Rule Three" }); + model.Rules.Add(new RewriteRule() { Name = "Rule Four" }); + + return View(model); } + [Admin("UrlRewriting/CreateRule", nameof(CreateRule))] + public async Task CreateRule() + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + var rule = new RewriteRule(); + + var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); + + return View(shape); + } + + [HttpPost, ActionName(nameof(CreateRule))] + public async Task CreateRulePOST() + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + var rule = new RewriteRule(); + + var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); + + if (!ModelState.IsValid) + { + return View(shape); + } + + await _rewriteRulesStore.CreateAsync(rule); + + await _notifier.SuccessAsync(H["Rule created successfully."]); + + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + public async Task DeleteRule(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + var currentRule = await _rewriteRulesStore.FindByIdAsync(id); + + if (currentRule == null) + { + return NotFound(); + } + + await _rewriteRulesStore.DeleteAsync(currentRule); + + await _notifier.SuccessAsync(H["Rule deleted successfully."]); + + return RedirectToAction(nameof(Index)); + } + + /* [Admin("UrlRewriting/Create", "CreateRule")] public async Task CreateRule() { @@ -126,15 +206,5 @@ public async Task CreateRulePOST(CreateUrlRewriteRuleViewModel vie return View(viewModel); } - - private void BuildViewModel(CreateUrlRewriteRuleViewModel model) - { - model.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); - model.AvailableActions.Add(new SelectListItem() { Text = S["Redirect"], Value = ((int)RuleAction.Redirect).ToString() }); - - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Moved Permanently (301)"], Value = ((int)RedirectType.MovedPermanently301).ToString() }); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Found (302)"], Value = ((int)RedirectType.Found302).ToString() }); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Temporary Redirect (307)"], Value = ((int)RedirectType.TemporaryRedirect307).ToString() }); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Pernament Redirect (308)"], Value = ((int)RedirectType.PernamentRedirect308).ToString() }); - } + */ } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs new file mode 100644 index 00000000000..6f3392e94c2 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs @@ -0,0 +1,122 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; +using Microsoft.Extensions.Localization; +using OrchardCore.DisplayManagement.Entities; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Views; +using OrchardCore.Environment.Shell; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.ViewModels; +using OrchardCore.Settings; +using OrchardCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Rendering; +using OrchardCore.UrlRewriting.Services; +using OrchardCore.UrlRewriting.Rules; + +namespace OrchardCore.UrlRewriting.Drivers; + +internal sealed class RewriteRuleDisplayDriver : DisplayDriver +{ + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IShellReleaseManager _shellReleaseManager; + private readonly RewriteRulesStore _rewriteRulesStore; + + internal readonly IStringLocalizer S; + + public RewriteRuleDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor, + IShellReleaseManager shellReleaseManager, + RewriteRulesStore rewriteRulesStore, + IStringLocalizer stringLocalizer) + { + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + _shellReleaseManager = shellReleaseManager; + _rewriteRulesStore = rewriteRulesStore; + S = stringLocalizer; + } + + public override async Task EditAsync(RewriteRule rule, BuildEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return null; + } + + context.AddTenantReloadWarningWrapper(); + + return Initialize("RewriteRuleFields_Edit", viewModel => BuildViewModel(rule, viewModel)) + .Location("Content:1"); + } + + public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return null; + } + + var model = new RewriteRuleViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix); + + var rules = await _rewriteRulesStore.LoadRewriteRulesAsync(); + + if (string.IsNullOrWhiteSpace(model.DisplayName)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.DisplayName), "The rule name is required."); + } + else if (context.IsNew && rules.Rules.Any(x => string.Equals(x.Name, model.DisplayName, StringComparison.OrdinalIgnoreCase))) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.DisplayName), "The rule name already exists."); + } + if (string.IsNullOrWhiteSpace(model.Pattern)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Pattern), "The url match pattern is required."); + } + if (model.RuleAction == RuleAction.Rewrite && string.IsNullOrWhiteSpace(model.RewriteAction.RewriteUrl)) + { + context.Updater.ModelState.AddModelError(Prefix, "RewriteAction.RewriteUrl", "The rewrite url substitition is required."); + } + if (model.RuleAction == RuleAction.Redirect && string.IsNullOrWhiteSpace(model.RedirectAction.RedirectUrl)) + { + context.Updater.ModelState.AddModelError(Prefix, "RedirectAction.RedirectUrl", "The redirect url substitition is required."); + } + + if (context.Updater.ModelState.IsValid) + { + if (ApacheRuleValidator.ValidateRule(model, out var message) == false) + { + context.Updater.ModelState.AddModelError(Prefix, "RuleError", S["Rule error: {0}", message]); + } + } + + rule.Name = model.DisplayName; + rule.Pattern = model.Pattern; + rule.Substitution = model.RuleAction == RuleAction.Rewrite ? model.RewriteAction.RewriteUrl : model.RedirectAction.RedirectUrl; + rule.Flags = ApacheRuleBuilder.FlagsFromViewModel(model); + + return await EditAsync(rule, context); + } + + private void BuildViewModel(RewriteRule model, RewriteRuleViewModel viewModel) + { + var flags = model.GetFlagsCollection(); + + viewModel.DisplayName = model.Name; + viewModel.Pattern = model.Pattern; + viewModel.IgnoreCase = flags.Contains("NC"); + + viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); + viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Redirect"], Value = ((int)RuleAction.Redirect).ToString() }); + + viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Moved Permanently (301)"], Value = ((int)RedirectType.MovedPermanently301).ToString() }); + viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Found (302)"], Value = ((int)RedirectType.Found302).ToString() }); + viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Temporary Redirect (307)"], Value = ((int)RedirectType.TemporaryRedirect307).ToString() }); + viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Pernament Redirect (308)"], Value = ((int)RedirectType.PernamentRedirect308).ToString() }); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs new file mode 100644 index 00000000000..2602a083cba --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs @@ -0,0 +1,28 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class RewriteRule +{ + public string Name { get; set; } + + public string Pattern { get; set; } + + public string Substitution { get; set; } + + public string Flags { get; set; } + + public string[] GetFlagsCollection() + { + return Flags?.Split(',') ?? []; + } + + public RewriteRule Clone() + { + return new RewriteRule + { + Name = Name, + Pattern = Pattern, + Substitution = Substitution, + Flags = Flags + }; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs new file mode 100644 index 00000000000..8dc9171efbb --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs @@ -0,0 +1,8 @@ +using OrchardCore.Data.Documents; + +namespace OrchardCore.UrlRewriting.Models; + +public sealed class RewriteRulesDocument : Document +{ + public List Rules { get; set; } = []; +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs index 4e7062f4192..b93b1c2b21e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs @@ -4,16 +4,5 @@ public class UrlRewritingSettings { public string ApacheModRewrite { get; set; } - public List Rules { get; set; } -} - -public class Rule -{ - public string Name { get; set; } - - public string Pattern { get; set; } - - public string Substitution { get; set; } - - public string Flags { get; set; } + public List Rules { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj index 0ad3cc838ea..07c3426a5bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj @@ -19,6 +19,7 @@ + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs index 5a4c3450249..ce1904cf3b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs @@ -5,7 +5,7 @@ namespace OrchardCore.UrlRewriting.Rules; public class ApacheRuleBuilder { - public static string FromViewModel(CreateUrlRewriteRuleViewModel viewModel, bool includeFlags = true) + public static string FlagsFromViewModel(RewriteRuleViewModel viewModel) { var sbFlags = new StringBuilder(); @@ -40,13 +40,20 @@ public static string FromViewModel(CreateUrlRewriteRuleViewModel viewModel, bool sbFlags.Remove(sbFlags.Length - 1, 1); } + return sbFlags.ToString(); + } + + public static string FromViewModel(RewriteRuleViewModel viewModel, bool includeFlags = true) + { + var flags = FlagsFromViewModel(viewModel); + var sbRewrite = new StringBuilder(); var replaceUrl = viewModel.RuleAction == RuleAction.Rewrite ? viewModel.RewriteAction.RewriteUrl : viewModel.RedirectAction.RedirectUrl; - var flags = sbFlags.Length > 0 ? $"[{sbFlags}]" : ""; - if (flags.Length > 0 && includeFlags) + var flagsStr = flags.Length > 0 ? $"[{flags}]" : ""; + if (flagsStr.Length > 0 && includeFlags) { - sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flags}"); + sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flagsStr}"); } else { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs new file mode 100644 index 00000000000..185151991c5 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Rewrite; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.UrlRewriting.Rules; + +public class ApacheRuleValidator +{ + public static bool ValidateRule(RewriteRuleViewModel viewModel, out string validationError) + { + var apacheRule = ApacheRuleBuilder.FromViewModel(viewModel, true); + + try + { + var rewriteOptions = new RewriteOptions(); + using var apacheModRewrite = new StringReader(apacheRule); + rewriteOptions.AddApacheModRewrite(apacheModRewrite); + } + catch (FormatException ex) + { + validationError = ex.Message; + return false; + } + catch (Exception ex) + { + validationError = ex.Message; + return false; + } + + validationError = null; + return true; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs new file mode 100644 index 00000000000..62b7609e339 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs @@ -0,0 +1,68 @@ +using OrchardCore.Documents; +using OrchardCore.Security; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.Services; + +public class RewriteRulesStore +{ + private readonly IDocumentManager _documentManager; + + private bool _updating; + + public RewriteRulesStore(IDocumentManager documentManager) + { + _documentManager = documentManager; + } + + /// + /// Loads the rewrite rules document from the store for updating and that should not be cached. + /// + public Task LoadRewriteRulesAsync() => _documentManager.GetOrCreateMutableAsync(); + + /// + /// Gets the rewrite rules document from the cache for sharing and that should not be updated. + /// + public Task GetRewriteRulesAsync() => _documentManager.GetOrCreateImmutableAsync(); + + /// + /// Updates the store with the provided rewrite rules document and then updates the cache. + /// + private Task UpdateRolesAsync(RewriteRulesDocument rules) + { + _updating = true; + + return _documentManager.UpdateAsync(rules); + } + + public async Task CreateAsync(RewriteRule rule) + { + var rules = await LoadRewriteRulesAsync(); + rules.Rules.Add(rule); + await UpdateRolesAsync(rules); + } + + public async Task DeleteAsync(RewriteRule rule) + { + var rules = await LoadRewriteRulesAsync(); + var ruleToRemove = rules.Rules.FirstOrDefault(r => string.Equals(r.Name, rule.Name, StringComparison.OrdinalIgnoreCase)); + rules.Rules.Remove(ruleToRemove); + + await UpdateRolesAsync(rules); + } + + public async Task FindByIdAsync(string ruleId) + { + // While updating find a rule from the loaded document being mutated. + var rules = _updating ? await LoadRewriteRulesAsync() : await GetRewriteRulesAsync(); + + var rule = rules.Rules.FirstOrDefault(x => string.Equals(x.Name, ruleId, StringComparison.OrdinalIgnoreCase)); + + if (rule == null) + { + return null; + } + + return _updating ? rule : rule.Clone(); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index bb18ddfec49..f17cd14fdd3 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -9,6 +9,8 @@ using OrchardCore.UrlRewriting.Drivers; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.UrlRewriting.Options; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Services; namespace OrchardCore.UrlRewriting; @@ -19,8 +21,10 @@ public override int Order public override void ConfigureServices(IServiceCollection services) { + services.AddScoped(); services.AddNavigationProvider(); services.AddSiteDisplayDriver(); + services.AddScoped, RewriteRuleDisplayDriver>(); services.AddPermissionProvider(); services.AddTransient, RewriteOptionsConfiguration>(); } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs similarity index 96% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs index 5cbb5059e0c..1987a82fbd7 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/CreateUrlRewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -3,7 +3,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public class CreateUrlRewriteRuleViewModel +public class RewriteRuleViewModel { public string DisplayName { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs new file mode 100644 index 00000000000..07c7f521705 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs @@ -0,0 +1,8 @@ +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class RewriteRulesViewModel +{ + public List Rules { get; set; } = []; +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml index 58cb5362b96..30fa5779c01 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml @@ -1,103 +1,8 @@ -@model CreateUrlRewriteRuleViewModel +@model dynamic

@RenderTitleSegments(T["New Rule"])

- + @Html.ValidationSummary() - -
- - - @T["Rule name."] -
- -
- - - @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] -
- -
-
- - -
- @T["When checked, the pattern will be case insensitive."] -
- -
-
- - -
- @T["Action type for matched URL."] -
- -
-
- - -
- -
-
- - -
-
-
- -
-
- - -
- -
-
- - -
-
- -
-
- - -
-
-
- -
- - - @T["Cancel"] -
+ @await DisplayAsync(Model) - - \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index d4110ff366e..951c4606f80 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -1,3 +1,5 @@ +@model RewriteRulesViewModel +

@RenderTitleSegments(T["URL Rewriting"])

@@ -10,8 +12,173 @@
+ +
+
    + @foreach (var rule in Model.Rules) + { + var message = T["Are you sure you want to delete this rule?"]; + +
  • +
    + + @rule.Name + @rule.Pattern->@rule.Pattern +
    +
  • + } +
+ + + + + + + + + + + + + + @foreach (var rule in Model.Rules) + { + var message = T["Are you sure you want to delete this rule?"]; + + + + + + + + + } + +
#Rule namePatternSubsitution
2@rule.Name@rule.Pattern@rule.Substitution + +
+
+ + \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml new file mode 100644 index 00000000000..6c1ea05e8f2 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml @@ -0,0 +1,10 @@ +
+
+ @if (Model.Content != null) + { +
+ @await DisplayAsync(Model.Content) +
+ } +
+
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml new file mode 100644 index 00000000000..b45d8f9677b --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml @@ -0,0 +1,97 @@ +@model RewriteRuleViewModel + +
+ + + @T["Rule name."] +
+ +
+ + + @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] +
+ +
+
+ + +
+ @T["When checked, the pattern will be case insensitive."] +
+ +
+
+ + +
+ @T["Action type for matched URL."] +
+ +
+
+ + +
+ +
+
+ + +
+
+
+ +
+
+ + +
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+ +
+ + + @T["Cancel"] +
+ + \ No newline at end of file From 060a19c19ad470d6ad0fe82e9d48a84a5d3f878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Mon, 30 Sep 2024 20:34:49 +0200 Subject: [PATCH 12/71] UrlRewriting UI - part 4 --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 16 +-- .../Controllers/AdminController.cs | 130 ++++++++---------- .../Drivers/RewriteRuleDisplayDriver.cs | 35 ++--- .../UrlRewritingSettingsDisplayDriver.cs | 98 ------------- .../Helpers/RewriteRuleViewModelHelper.cs | 34 +++++ .../Options/RewriteOptionsConfiguration.cs | 14 +- .../Rules/{RuleBuilder.cs => ApacheRules.cs} | 37 ++++- .../Rules/RuleValidator.cs | 2 +- .../Services/RewriteRulesStore.cs | 37 +++-- .../OrchardCore.UrlRewriting/Startup.cs | 1 - .../ViewModels/RewriteRuleViewModel.cs | 8 +- .../ViewModels/RewriteRulesViewModel.cs | 4 +- .../UrlRewritingSettingsViewModel.cs | 6 - .../Views/Admin/CreateRule.cshtml | 2 +- .../Views/Admin/EditRule.cshtml | 8 ++ .../Views/Admin/Index.cshtml | 99 +++---------- .../Views/RewriteRuleFields.Edit.cshtml | 40 ++++-- .../Views/UrlRewritingSettings.Edit.cshtml | 7 - 18 files changed, 236 insertions(+), 342 deletions(-) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/{RuleBuilder.cs => ApacheRules.cs} (68%) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index cf6c05cf402..5281e20995e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -1,7 +1,5 @@ -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -using OrchardCore.UrlRewriting.Drivers; namespace OrchardCore.UrlRewriting; @@ -18,14 +16,12 @@ protected override ValueTask BuildAsync(NavigationBuilder builder) { builder .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting - .AddClass("urlrewriting").Id("urlrewriting") - .Permission(UrlRewritingPermissions.ManageUrlRewriting) - .Action("Index", "Admin", "OrchardCore.UrlRewriting") - .LocalNav() - ) - ) + .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting + .AddClass("urlrewriting").Id("urlrewriting") + .Permission(UrlRewritingPermissions.ManageUrlRewriting) + .Action("Index", "Admin", "OrchardCore.UrlRewriting") + .LocalNav() + ) ); return ValueTask.CompletedTask; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 57db0633fc8..c449936e8bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; -using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Localization; using OrchardCore.Admin; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Notify; +using OrchardCore.Environment.Shell; +using OrchardCore.UrlRewriting.Helpers; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Services; using OrchardCore.UrlRewriting.ViewModels; @@ -22,6 +22,7 @@ public sealed class AdminController : Controller private readonly RewriteRulesStore _rewriteRulesStore; private readonly IDisplayManager _rewriteRuleDisplayManager; private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IShellReleaseManager _shellReleaseManager; internal readonly IStringLocalizer S; internal readonly IHtmlLocalizer H; @@ -31,6 +32,7 @@ public AdminController( IAuthorizationService authorizationService, RewriteRulesStore rewriteRulesStore, INotifier notifier, + IShellReleaseManager shellReleaseManager, IStringLocalizer stringLocalizer, IHtmlLocalizer htmlLocalizer, IUpdateModelAccessor updateModelAccessor) @@ -39,6 +41,7 @@ public AdminController( _authorizationService = authorizationService; _rewriteRulesStore = rewriteRulesStore; _notifier = notifier; + _shellReleaseManager = shellReleaseManager; _updateModelAccessor = updateModelAccessor; S = stringLocalizer; H = htmlLocalizer; @@ -55,14 +58,11 @@ public async Task Index() var model = new RewriteRulesViewModel { - Rules = rules.Rules.ToList() + Rules = rules.Rules + .Select(BuildViewModel) + .ToList() }; - model.Rules.Add(new RewriteRule() { Name = "Rule One" }); - model.Rules.Add(new RewriteRule() { Name = "Rule Two" }); - model.Rules.Add(new RewriteRule() { Name = "Rule Three" }); - model.Rules.Add(new RewriteRule() { Name = "Rule Four" }); - return View(model); } @@ -76,7 +76,7 @@ public async Task CreateRule() var rule = new RewriteRule(); - var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); + var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true); return View(shape); } @@ -91,22 +91,21 @@ public async Task CreateRulePOST() var rule = new RewriteRule(); - var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); + var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true); if (!ModelState.IsValid) { return View(shape); } - await _rewriteRulesStore.CreateAsync(rule); + await _rewriteRulesStore.SaveAsync(rule); await _notifier.SuccessAsync(H["Rule created successfully."]); return RedirectToAction(nameof(Index)); } - [HttpPost] - public async Task DeleteRule(string id) + public async Task EditRule(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { @@ -120,91 +119,82 @@ public async Task DeleteRule(string id) return NotFound(); } - await _rewriteRulesStore.DeleteAsync(currentRule); + var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(currentRule, updater: _updateModelAccessor.ModelUpdater, isNew: false); - await _notifier.SuccessAsync(H["Rule deleted successfully."]); - - return RedirectToAction(nameof(Index)); + return View(shape); } - /* - [Admin("UrlRewriting/Create", "CreateRule")] - public async Task CreateRule() + [HttpPost, ActionName(nameof(EditRule))] + public async Task EditRulePOST(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var model = new CreateUrlRewriteRuleViewModel(); + var currentRule = await _rewriteRulesStore.FindByIdAsync(id); + + if (currentRule == null) + { + return NotFound(); + } + + var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(currentRule, updater: _updateModelAccessor.ModelUpdater, isNew: false); - BuildViewModel(model); + if (ModelState.IsValid) + { + await _rewriteRulesStore.SaveAsync(currentRule); - return View(model); + await _notifier.SuccessAsync(H["Rule updated successfully."]); + + return RedirectToAction(nameof(Index)); + } + + return View(shape); } - [HttpPost, ActionName("CreateRule")] - public async Task CreateRulePOST(CreateUrlRewriteRuleViewModel viewModel) + [HttpPost] + public async Task DeleteRule(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - viewModel.Pattern = viewModel.Pattern ?? string.Empty; - viewModel.RewriteAction.RewriteUrl = viewModel.RewriteAction.RewriteUrl ?? string.Empty; - viewModel.RedirectAction.RedirectUrl = viewModel.RedirectAction.RedirectUrl ?? string.Empty; + var currentRule = await _rewriteRulesStore.FindByIdAsync(id); - if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) - { - ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); - } - if (string.IsNullOrWhiteSpace(viewModel.Pattern)) - { - ModelState.AddModelError("Pattern", S["The Pattern can't be empty."]); - } - if (viewModel.RuleAction == RuleAction.Rewrite && string.IsNullOrEmpty(viewModel.RewriteAction.RewriteUrl)) - { - ModelState.AddModelError("RewriteAction.RewriteUrl", S["The Rewrite URL can't be empty."]); - } - if (viewModel.RuleAction == RuleAction.Redirect && string.IsNullOrEmpty(viewModel.RedirectAction.RedirectUrl)) + if (currentRule == null) { - ModelState.AddModelError("RedirectAction.RedirectUrl", S["The Redirect URL can't be empty."]); + return NotFound(); } - // If basic validation is ok, do final check - if (ModelState.IsValid) + await _rewriteRulesStore.DeleteAsync(currentRule); + + await _notifier.SuccessAsync(H["Rule deleted successfully."]); + + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + public async Task ReloadWebsite() + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { - var rewriteRule = ApacheRuleBuilder.FromViewModel(viewModel, true); - - try - { - using var apacheModRewrite = new StringReader(rewriteRule); - var rewriteOptions = new RewriteOptions(); - rewriteOptions.AddApacheModRewrite(apacheModRewrite); - } - catch (FormatException ex) - { - ModelState.AddModelError("Pattern", S["Parsing error: {0}", ex.Message]); - } - catch (NotImplementedException ex) - { - ModelState.AddModelError("Pattern", S["Parsing error: {0}", ex.Message]); - } + return Forbid(); } - if (ModelState.IsValid) - { - // TODO - save settings/reload rules - await _notifier.SuccessAsync(H["URL rewriting rule created successfully."]); + _shellReleaseManager.RequestRelease(); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); + } + + + private static RewriteRuleViewModel BuildViewModel(RewriteRule rule) + { + var viewModel = new RewriteRuleViewModel(); - BuildViewModel(viewModel); + viewModel.FromModel(rule); - return View(viewModel); + return viewModel; } - */ } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs index 6f3392e94c2..a420e1091b1 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs @@ -1,19 +1,15 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Localization; -using OrchardCore.DisplayManagement.Entities; using OrchardCore.DisplayManagement.Handlers; -using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; -using OrchardCore.Environment.Shell; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.ViewModels; -using OrchardCore.Settings; using OrchardCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.UrlRewriting.Services; using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.Helpers; namespace OrchardCore.UrlRewriting.Drivers; @@ -21,7 +17,6 @@ internal sealed class RewriteRuleDisplayDriver : DisplayDriver { private readonly IAuthorizationService _authorizationService; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IShellReleaseManager _shellReleaseManager; private readonly RewriteRulesStore _rewriteRulesStore; internal readonly IStringLocalizer S; @@ -29,13 +24,11 @@ internal sealed class RewriteRuleDisplayDriver : DisplayDriver public RewriteRuleDisplayDriver( IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor, - IShellReleaseManager shellReleaseManager, RewriteRulesStore rewriteRulesStore, - IStringLocalizer stringLocalizer) + IStringLocalizer stringLocalizer) { _authorizationService = authorizationService; _httpContextAccessor = httpContextAccessor; - _shellReleaseManager = shellReleaseManager; _rewriteRulesStore = rewriteRulesStore; S = stringLocalizer; } @@ -47,9 +40,7 @@ public override async Task EditAsync(RewriteRule rule, BuildEdit return null; } - context.AddTenantReloadWarningWrapper(); - - return Initialize("RewriteRuleFields_Edit", viewModel => BuildViewModel(rule, viewModel)) + return Initialize("RewriteRuleFields_Edit", viewModel => BuildViewModel(rule, viewModel, context.IsNew)) .Location("Content:1"); } @@ -66,13 +57,13 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE var rules = await _rewriteRulesStore.LoadRewriteRulesAsync(); - if (string.IsNullOrWhiteSpace(model.DisplayName)) + if (string.IsNullOrWhiteSpace(model.Name)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.DisplayName), "The rule name is required."); + context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Name), "The rule name is required."); } - else if (context.IsNew && rules.Rules.Any(x => string.Equals(x.Name, model.DisplayName, StringComparison.OrdinalIgnoreCase))) + else if (context.IsNew && rules.Rules.Any(x => string.Equals(x.Name, model.Name, StringComparison.OrdinalIgnoreCase))) { - context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.DisplayName), "The rule name already exists."); + context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Name), "The rule name already exists."); } if (string.IsNullOrWhiteSpace(model.Pattern)) { @@ -95,21 +86,19 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE } } - rule.Name = model.DisplayName; + rule.Name = model.Name; rule.Pattern = model.Pattern; rule.Substitution = model.RuleAction == RuleAction.Rewrite ? model.RewriteAction.RewriteUrl : model.RedirectAction.RedirectUrl; - rule.Flags = ApacheRuleBuilder.FlagsFromViewModel(model); + rule.Flags = ApacheRules.FlagsFromViewModel(model); return await EditAsync(rule, context); } - private void BuildViewModel(RewriteRule model, RewriteRuleViewModel viewModel) + private void BuildViewModel(RewriteRule model, RewriteRuleViewModel viewModel, bool isNew) { - var flags = model.GetFlagsCollection(); + viewModel.FromModel(model); - viewModel.DisplayName = model.Name; - viewModel.Pattern = model.Pattern; - viewModel.IgnoreCase = flags.Contains("NC"); + viewModel.IsNew = isNew; viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Redirect"], Value = ((int)RuleAction.Redirect).ToString() }); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs deleted file mode 100644 index ab8423abfb0..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewritingSettingsDisplayDriver.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite; -using Microsoft.Extensions.Localization; -using OrchardCore.DisplayManagement.Entities; -using OrchardCore.DisplayManagement.Handlers; -using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Views; -using OrchardCore.Environment.Shell; -using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.ViewModels; -using OrchardCore.Settings; -using OrchardCore.Mvc.ModelBinding; - -namespace OrchardCore.UrlRewriting.Drivers; - -internal sealed class UrlRewritingSettingsDisplayDriver : SiteDisplayDriver -{ - public const string GroupId = "UrlRewriting"; - - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IShellReleaseManager _shellReleaseManager; - - internal readonly IStringLocalizer S; - - public UrlRewritingSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor, - IShellReleaseManager shellReleaseManager, - IStringLocalizer stringLocalizer) - { - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - _shellReleaseManager = shellReleaseManager; - S = stringLocalizer; - } - - protected override string SettingsGroupId => GroupId; - - public override async Task EditAsync(ISite site, UrlRewritingSettings settings, BuildEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, UrlRewritingPermissions.ManageUrlRewriting)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); - - return Initialize("UrlRewritingSettings_Edit", model => - { - model.ApacheModRewrite = settings.ApacheModRewrite; - }).Location("Content:1") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, UrlRewritingSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, UrlRewritingPermissions.ManageUrlRewriting)) - { - return null; - } - - var model = new UrlRewritingSettingsViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - - settings.ApacheModRewrite = model.ApacheModRewrite; - - ValidateUrls(settings, context.Updater); - - _shellReleaseManager.RequestRelease(); - - return await EditAsync(site, settings, context); - } - - private void ValidateUrls(UrlRewritingSettings settings, IUpdateModel updater) - { - try - { - var rewriteOptions = new RewriteOptions(); - using var apacheModRewrite = new StringReader(settings.ApacheModRewrite); - rewriteOptions.AddApacheModRewrite(apacheModRewrite); - } - catch (FormatException ex) - { - updater.ModelState.AddModelError(Prefix, nameof(UrlRewritingSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); - } - catch (NotImplementedException ex) - { - updater.ModelState.AddModelError(Prefix, nameof(UrlRewritingSettingsViewModel.ApacheModRewrite), S["Parsing error: {0}", ex.Message]); - } - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs new file mode 100644 index 00000000000..8e96a817132 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs @@ -0,0 +1,34 @@ +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.UrlRewriting.Helpers; + +internal static class RewriteRuleViewModelHelper +{ + public static void FromModel(this RewriteRuleViewModel viewModel, RewriteRule model) + { + var flags = model.GetFlagsCollection(); + bool isRedirect = flags.Where(f => f.StartsWith("R=")).Any(); + + viewModel.Name = model.Name; + viewModel.Pattern = model.Pattern; + viewModel.IgnoreCase = flags.Contains("NC"); + viewModel.RuleAction = isRedirect ? RuleAction.Redirect : RuleAction.Rewrite; + + if (isRedirect) + { + var redirectFlag = flags.Where(f => f.StartsWith("R=")).FirstOrDefault() ?? ""; + + viewModel.RedirectAction.RedirectUrl = model.Substitution; + viewModel.RedirectAction.AppendQueryString = flags.Contains("QSA"); + viewModel.RedirectAction.RedirectType = ApacheRules.GetRedirectType(redirectFlag); + } + else + { + viewModel.RewriteAction.RewriteUrl = model.Substitution; + viewModel.RewriteAction.AppendQueryString = flags.Contains("QSA"); + viewModel.RewriteAction.SkipFurtherRules = flags.Contains("L"); + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs index 3fd31cd742b..174e65c61c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Options; using OrchardCore.Admin; -using OrchardCore.Settings; +using OrchardCore.Documents; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Rules; @@ -9,23 +9,25 @@ namespace OrchardCore.UrlRewriting.Options; public sealed class RewriteOptionsConfiguration : IConfigureOptions { - private readonly ISiteService _siteService; + private readonly IDocumentManager _documentManager; private readonly AdminOptions _adminOptions; - public RewriteOptionsConfiguration(ISiteService siteService, IOptions adminOptions) + public RewriteOptionsConfiguration(IDocumentManager documentManager, IOptions adminOptions) { - _siteService = siteService; + _documentManager = documentManager; _adminOptions = adminOptions.Value; } public void Configure(RewriteOptions options) { - var settings = _siteService.GetSettingsAsync() + var rules = _documentManager.GetOrCreateMutableAsync() .GetAwaiter() .GetResult(); - using var apacheModRewrite = new StringReader(settings.ApacheModRewrite ?? string.Empty); + var apacheRules = ApacheRules.FromModels(rules.Rules); + + using var apacheModRewrite = new StringReader(apacheRules); options.AddApacheModRewrite(apacheModRewrite); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs similarity index 68% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs index ce1904cf3b4..11ce56bb6ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs @@ -1,9 +1,10 @@ +using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.ViewModels; using System.Text; namespace OrchardCore.UrlRewriting.Rules; -public class ApacheRuleBuilder +public class ApacheRules { public static string FlagsFromViewModel(RewriteRuleViewModel viewModel) { @@ -63,6 +64,40 @@ public static string FromViewModel(RewriteRuleViewModel viewModel, bool includeF return sbRewrite.ToString(); } + public static string FromModel(RewriteRule model) + { + if (string.IsNullOrWhiteSpace(model.Flags)) + { + return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\""; + } + else + { + return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\" [{model.Flags}]"; + } + } + + public static string FromModels(IEnumerable models) + { + var sb = new StringBuilder(); + foreach (var model in models) + { + sb.AppendLine(FromModel(model)); + } + return sb.ToString(); + } + + public static RedirectType GetRedirectType(string flag) + { + return flag switch + { + "R=301" => RedirectType.MovedPermanently301, + "R=302" => RedirectType.Found302, + "R=307" => RedirectType.TemporaryRedirect307, + "R=308" => RedirectType.PernamentRedirect308, + _ => RedirectType.Found302 + }; + } + private static int RedirectTypeToStatusCode(RedirectType redirectType) { return redirectType switch diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs index 185151991c5..344d292b5ec 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs @@ -7,7 +7,7 @@ public class ApacheRuleValidator { public static bool ValidateRule(RewriteRuleViewModel viewModel, out string validationError) { - var apacheRule = ApacheRuleBuilder.FromViewModel(viewModel, true); + var apacheRule = ApacheRules.FromViewModel(viewModel, true); try { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs index 62b7609e339..0a5a0b80ba7 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs @@ -1,5 +1,4 @@ using OrchardCore.Documents; -using OrchardCore.Security; using OrchardCore.UrlRewriting.Models; namespace OrchardCore.UrlRewriting.Services; @@ -8,8 +7,6 @@ public class RewriteRulesStore { private readonly IDocumentManager _documentManager; - private bool _updating; - public RewriteRulesStore(IDocumentManager documentManager) { _documentManager = documentManager; @@ -25,21 +22,24 @@ public RewriteRulesStore(IDocumentManager documentManager) /// public Task GetRewriteRulesAsync() => _documentManager.GetOrCreateImmutableAsync(); - /// - /// Updates the store with the provided rewrite rules document and then updates the cache. - /// - private Task UpdateRolesAsync(RewriteRulesDocument rules) + public async Task SaveAsync(RewriteRule rule) { - _updating = true; + var rules = await LoadRewriteRulesAsync(); - return _documentManager.UpdateAsync(rules); - } + var preexisting = rules.Rules.FirstOrDefault(x => string.Equals(x.Name, rule.Name, StringComparison.OrdinalIgnoreCase)); - public async Task CreateAsync(RewriteRule rule) - { - var rules = await LoadRewriteRulesAsync(); - rules.Rules.Add(rule); - await UpdateRolesAsync(rules); + // it's new? add it + if (preexisting == null) + { + rules.Rules.Add(rule); + } + else // not new: replace it + { + var index = rules.Rules.IndexOf(preexisting); + rules.Rules[index] = rule; + } + + await _documentManager.UpdateAsync(rules); } public async Task DeleteAsync(RewriteRule rule) @@ -48,13 +48,12 @@ public async Task DeleteAsync(RewriteRule rule) var ruleToRemove = rules.Rules.FirstOrDefault(r => string.Equals(r.Name, rule.Name, StringComparison.OrdinalIgnoreCase)); rules.Rules.Remove(ruleToRemove); - await UpdateRolesAsync(rules); + await _documentManager.UpdateAsync(rules); } public async Task FindByIdAsync(string ruleId) { - // While updating find a rule from the loaded document being mutated. - var rules = _updating ? await LoadRewriteRulesAsync() : await GetRewriteRulesAsync(); + var rules = await GetRewriteRulesAsync(); var rule = rules.Rules.FirstOrDefault(x => string.Equals(x.Name, ruleId, StringComparison.OrdinalIgnoreCase)); @@ -63,6 +62,6 @@ public async Task FindByIdAsync(string ruleId) return null; } - return _updating ? rule : rule.Clone(); + return rule.Clone(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index f17cd14fdd3..c9a3499cb99 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -23,7 +23,6 @@ public override void ConfigureServices(IServiceCollection services) { services.AddScoped(); services.AddNavigationProvider(); - services.AddSiteDisplayDriver(); services.AddScoped, RewriteRuleDisplayDriver>(); services.AddPermissionProvider(); services.AddTransient, RewriteOptionsConfiguration>(); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs index 1987a82fbd7..733521625c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -5,7 +5,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; public class RewriteRuleViewModel { - public string DisplayName { get; set; } + public string Name { get; set; } public string Pattern { get; set; } @@ -17,6 +17,12 @@ public class RewriteRuleViewModel public RedirectActionViewModel RedirectAction { get; set; } = new RedirectActionViewModel(); + [BindNever] + public string Substitution => RuleAction == RuleAction.Rewrite ? RewriteAction.RewriteUrl : RedirectAction.RedirectUrl; + + [BindNever] + public bool IsNew { get; set; } + [BindNever] public List AvailableActions { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs index 07c7f521705..3133433aef0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs @@ -1,8 +1,6 @@ -using OrchardCore.UrlRewriting.Models; - namespace OrchardCore.UrlRewriting.ViewModels; public class RewriteRulesViewModel { - public List Rules { get; set; } = []; + public List Rules { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs deleted file mode 100644 index 75ab6e42f82..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewritingSettingsViewModel.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OrchardCore.UrlRewriting.ViewModels; - -public class UrlRewritingSettingsViewModel -{ - public string ApacheModRewrite { get; set; } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml index 30fa5779c01..4a51031d155 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml @@ -2,7 +2,7 @@

@RenderTitleSegments(T["New Rule"])

-
+ @Html.ValidationSummary() @await DisplayAsync(Model)
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml new file mode 100644 index 00000000000..d51943c2fbc --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml @@ -0,0 +1,8 @@ +@model dynamic + +

@RenderTitleSegments(T["Edit Rule"])

+ +
+ @Html.ValidationSummary() + @await DisplayAsync(Model) +
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 951c4606f80..c4ba32501db 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -2,6 +2,13 @@

@RenderTitleSegments(T["URL Rewriting"])

+
+
+

@T["The website needs to be restarted for any changes to the rules to take effect."]

+ @T["Restart Website"] +
+
+
@@ -18,35 +25,14 @@
-
-
    - @foreach (var rule in Model.Rules) - { - var message = T["Are you sure you want to delete this rule?"]; - -
  • -
    - - @rule.Name - @rule.Pattern->@rule.Pattern -
    -
  • - } -
- - + - + @@ -56,13 +42,13 @@ var message = T["Are you sure you want to delete this rule?"]; - - + + @@ -70,64 +56,12 @@ }
# Rule name Pattern SubsitutionAction
2@rule.Name@rule.Name @rule.Pattern @rule.SubstitutionRewrite
+
\ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml deleted file mode 100644 index 43e8b83ce04..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewritingSettings.Edit.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@model OrchardCore.UrlRewriting.ViewModels.UrlRewritingSettingsViewModel - -
- - - -
From 2963137aeb7ab52bc6b92e4e5300ac8805249e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 2 Oct 2024 20:51:04 +0200 Subject: [PATCH 13/71] Add recipe step --- .../OrchardCore.UrlRewriting.csproj | 1 + .../Recipes/UrlRewritingStep.cs | 58 +++++++++++++++++++ .../OrchardCore.UrlRewriting/Startup.cs | 3 + 3 files changed, 62 insertions(+) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj index 07c3426a5bd..d1e3f93ec2e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj @@ -19,6 +19,7 @@ + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs new file mode 100644 index 00000000000..d10bb2e5e6f --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs @@ -0,0 +1,58 @@ +using System.Text.Json.Nodes; +using Microsoft.Extensions.Localization; +using OrchardCore.Recipes.Models; +using OrchardCore.Recipes.Services; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Services; + +namespace OrchardCore.UrlRewriting.Recipes; + +/// +/// This recipe step creates a set of url rewrites. +/// +public sealed class UrlRewritingStep : IRecipeStepHandler +{ + private readonly RewriteRulesStore _rewriteRulesStore; + + internal readonly IStringLocalizer S; + + public UrlRewritingStep( + RewriteRulesStore rewriteRulesStore, + IStringLocalizer stringLocalizer) + { + _rewriteRulesStore = rewriteRulesStore; + S = stringLocalizer; + } + + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "UrlRewriting", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + var model = context.Step.ToObject(); + + foreach (var importedRule in model.Rules) + { + if (string.IsNullOrWhiteSpace(importedRule.Name)) + { + context.Errors.Add(S["Unable to add or update url rewriting rule. The rule name cannot be null or empty."]); + continue; + } + + if (string.IsNullOrEmpty(importedRule.Substitution)) + { + context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule substitution cannot be null or empty.", importedRule.Name]); + continue; + } + + await _rewriteRulesStore.SaveAsync(importedRule); + } + } +} + +public sealed class UrlRewritingStepModel +{ + public RewriteRule[] Rules { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index c9a3499cb99..d707aa97209 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -11,6 +11,8 @@ using OrchardCore.UrlRewriting.Options; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Services; +using OrchardCore.Recipes; +using OrchardCore.UrlRewriting.Recipes; namespace OrchardCore.UrlRewriting; @@ -26,6 +28,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped, RewriteRuleDisplayDriver>(); services.AddPermissionProvider(); services.AddTransient, RewriteOptionsConfiguration>(); + services.AddRecipeExecutionStep(); } public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) From 7ffe4213d6bbb0a2928d24b62e75fb7006aec742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 2 Oct 2024 20:53:21 +0200 Subject: [PATCH 14/71] Mark classes as sealed --- .../OrchardCore.UrlRewriting/Models/RewriteRule.cs | 2 +- .../OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs | 2 +- .../OrchardCore.UrlRewriting/Rules/ApacheRules.cs | 2 +- .../OrchardCore.UrlRewriting/Rules/RuleValidator.cs | 2 +- .../OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs | 2 +- .../ViewModels/RewriteRuleViewModel.cs | 6 +++--- .../ViewModels/RewriteRulesViewModel.cs | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs index 2602a083cba..03d0d8fddba 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.Models; -public class RewriteRule +public sealed class RewriteRule { public string Name { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs index b93b1c2b21e..50eb46ebdd6 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.Models; -public class UrlRewritingSettings +public sealed class UrlRewritingSettings { public string ApacheModRewrite { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs index 11ce56bb6ee..02e7537acdb 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs @@ -4,7 +4,7 @@ namespace OrchardCore.UrlRewriting.Rules; -public class ApacheRules +public sealed class ApacheRules { public static string FlagsFromViewModel(RewriteRuleViewModel viewModel) { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs index 344d292b5ec..57e5282b108 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs @@ -3,7 +3,7 @@ namespace OrchardCore.UrlRewriting.Rules; -public class ApacheRuleValidator +public sealed class ApacheRuleValidator { public static bool ValidateRule(RewriteRuleViewModel viewModel, out string validationError) { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs index 0a5a0b80ba7..9ada58e3cd4 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs @@ -3,7 +3,7 @@ namespace OrchardCore.UrlRewriting.Services; -public class RewriteRulesStore +public sealed class RewriteRulesStore { private readonly IDocumentManager _documentManager; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs index 733521625c0..62eff2fe922 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -3,7 +3,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public class RewriteRuleViewModel +public sealed class RewriteRuleViewModel { public string Name { get; set; } @@ -41,7 +41,7 @@ public enum RedirectType PernamentRedirect308 } -public class RedirectActionViewModel +public sealed class RedirectActionViewModel { public string RedirectUrl { get; set; } @@ -53,7 +53,7 @@ public class RedirectActionViewModel public List AvailableRedirectTypes { get; set; } = []; } -public class RewriteActionViewModel +public sealed class RewriteActionViewModel { public string RewriteUrl { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs index 3133433aef0..7ac00c134e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public class RewriteRulesViewModel +public sealed class RewriteRulesViewModel { public List Rules { get; set; } = []; } From 806be179d0f1fee069ece69640bcadd6933f3c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 2 Oct 2024 21:09:23 +0200 Subject: [PATCH 15/71] Bug fixes --- .../OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs | 8 +++++++- .../ViewModels/RewriteRuleViewModel.cs | 6 +++--- .../ViewModels/RewriteRulesViewModel.cs | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs index d10bb2e5e6f..6b2a67b5e50 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs @@ -41,9 +41,15 @@ public async Task ExecuteAsync(RecipeExecutionContext context) continue; } + if (string.IsNullOrEmpty(importedRule.Pattern)) + { + context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule Pattern field cannot be null or empty.", importedRule.Name]); + continue; + } + if (string.IsNullOrEmpty(importedRule.Substitution)) { - context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule substitution cannot be null or empty.", importedRule.Name]); + context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule Substitution field cannot be null or empty.", importedRule.Name]); continue; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs index 62eff2fe922..733521625c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -3,7 +3,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public sealed class RewriteRuleViewModel +public class RewriteRuleViewModel { public string Name { get; set; } @@ -41,7 +41,7 @@ public enum RedirectType PernamentRedirect308 } -public sealed class RedirectActionViewModel +public class RedirectActionViewModel { public string RedirectUrl { get; set; } @@ -53,7 +53,7 @@ public sealed class RedirectActionViewModel public List AvailableRedirectTypes { get; set; } = []; } -public sealed class RewriteActionViewModel +public class RewriteActionViewModel { public string RewriteUrl { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs index 7ac00c134e4..3133433aef0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.ViewModels; -public sealed class RewriteRulesViewModel +public class RewriteRulesViewModel { public List Rules { get; set; } = []; } From 8b23ca1a2917f02752bc33edd1504eb2817964f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 2 Oct 2024 21:34:31 +0200 Subject: [PATCH 16/71] UI fix --- .../OrchardCore.UrlRewriting/Views/Admin/Index.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index c4ba32501db..651c388cff3 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -45,7 +45,7 @@ @rule.Name @rule.Pattern @rule.Substitution - Rewrite + @(rule.RuleAction == RuleAction.Rewrite ? T["Rewrite"] : T["Redirect"])
@T["Edit"] From 45d15479533c6a0be1dc382120acf423f0c71e17 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Wed, 2 Oct 2024 17:56:14 -0700 Subject: [PATCH 17/71] Cleanup, add Id to Rules and other things --- .../OrchardCore.UrlRewriting/AdminMenu.cs | 5 +- .../Controllers/AdminController.cs | 78 ++++++----- .../Drivers/RewriteRuleDisplayDriver.cs | 74 +++++------ .../Helpers/RewriteRuleViewModelHelper.cs | 6 +- .../Models/RewriteRule.cs | 2 + .../Models/RewriteRulesDocument.cs | 2 +- .../Options/RewriteOptionsConfiguration.cs | 6 +- .../Rules/ApacheRules.cs | 25 ++-- .../Rules/RuleValidator.cs | 1 + .../Services/RewriteRulesStore.cs | 38 ++---- .../ViewModels/RewriteRuleViewModel.cs | 13 +- .../ViewModels/RewriteRulesViewModel.cs | 2 +- .../{CreateRule.cshtml => Create.cshtml} | 4 +- .../Views/Admin/Edit.cshtml | 10 ++ .../Views/Admin/EditRule.cshtml | 8 -- .../Views/Admin/Index.cshtml | 40 +++--- .../NavigationItemText-urlrewriting.Id.cshtml | 5 +- .../Views/RewriteRuleFields.Edit.cshtml | 121 ++++++++---------- .../Views/_ViewImports.cshtml | 2 +- 19 files changed, 203 insertions(+), 239 deletions(-) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/{CreateRule.cshtml => Create.cshtml} (51%) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs index 5281e20995e..0e825478d41 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs @@ -5,7 +5,7 @@ namespace OrchardCore.UrlRewriting; public sealed class AdminMenu : AdminNavigationProvider { - public readonly IStringLocalizer S; + internal readonly IStringLocalizer S; public AdminMenu(IStringLocalizer stringLocalizer) { @@ -17,7 +17,8 @@ protected override ValueTask BuildAsync(NavigationBuilder builder) builder .Add(S["Configuration"], configuration => configuration .Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting - .AddClass("urlrewriting").Id("urlrewriting") + .AddClass("urlrewriting") + .Id("urlrewriting") .Permission(UrlRewritingPermissions.ManageUrlRewriting) .Action("Index", "Admin", "OrchardCore.UrlRewriting") .LocalNav() diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index c449936e8bb..66e14a9c149 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -58,16 +58,15 @@ public async Task Index() var model = new RewriteRulesViewModel { - Rules = rules.Rules + Rules = rules.Rules.Values .Select(BuildViewModel) - .ToList() + .ToArray() }; return View(model); } - [Admin("UrlRewriting/CreateRule", nameof(CreateRule))] - public async Task CreateRule() + public async Task Create() { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { @@ -76,13 +75,14 @@ public async Task CreateRule() var rule = new RewriteRule(); - var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true); + var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); return View(shape); } - [HttpPost, ActionName(nameof(CreateRule))] - public async Task CreateRulePOST() + [HttpPost] + [ActionName(nameof(Create))] + public async Task CreatePOST() { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { @@ -91,104 +91,98 @@ public async Task CreateRulePOST() var rule = new RewriteRule(); - var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, updater: _updateModelAccessor.ModelUpdater, isNew: true); + var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); - if (!ModelState.IsValid) + if (ModelState.IsValid) { - return View(shape); - } + rule.Id = IdGenerator.GenerateId(); - await _rewriteRulesStore.SaveAsync(rule); + await _rewriteRulesStore.SaveAsync(rule); - await _notifier.SuccessAsync(H["Rule created successfully."]); + await _notifier.SuccessAsync(H["Rule created successfully."]); - return RedirectToAction(nameof(Index)); + return RedirectToAction(nameof(Index)); + } + + _shellReleaseManager.SuspendReleaseRequest(); + + return View(shape); } - public async Task EditRule(string id) + public async Task Edit(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var currentRule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesStore.FindByIdAsync(id); - if (currentRule == null) + if (rule == null) { return NotFound(); } - var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(currentRule, updater: _updateModelAccessor.ModelUpdater, isNew: false); + var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: false); return View(shape); } - [HttpPost, ActionName(nameof(EditRule))] - public async Task EditRulePOST(string id) + [HttpPost, ActionName(nameof(Edit))] + public async Task EditPOST(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var currentRule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesStore.FindByIdAsync(id); - if (currentRule == null) + if (rule == null) { return NotFound(); } - var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(currentRule, updater: _updateModelAccessor.ModelUpdater, isNew: false); + var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: false); if (ModelState.IsValid) { - await _rewriteRulesStore.SaveAsync(currentRule); + await _rewriteRulesStore.SaveAsync(rule); await _notifier.SuccessAsync(H["Rule updated successfully."]); return RedirectToAction(nameof(Index)); } + _shellReleaseManager.SuspendReleaseRequest(); + return View(shape); } [HttpPost] - public async Task DeleteRule(string id) + public async Task Delete(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var currentRule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesStore.FindByIdAsync(id); - if (currentRule == null) + if (rule == null) { return NotFound(); } - await _rewriteRulesStore.DeleteAsync(currentRule); - - await _notifier.SuccessAsync(H["Rule deleted successfully."]); - - return RedirectToAction(nameof(Index)); - } - - [HttpPost] - public async Task ReloadWebsite() - { - if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) - { - return Forbid(); - } + await _rewriteRulesStore.DeleteAsync(rule); _shellReleaseManager.RequestRelease(); + await _notifier.SuccessAsync(H["Rule deleted successfully."]); + return RedirectToAction(nameof(Index)); } - private static RewriteRuleViewModel BuildViewModel(RewriteRule rule) { var viewModel = new RewriteRuleViewModel(); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs index a420e1091b1..ec8a7443f5d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs @@ -1,15 +1,15 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Localization; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.ViewModels; +using OrchardCore.Environment.Shell; using OrchardCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.Rendering; -using OrchardCore.UrlRewriting.Services; -using OrchardCore.UrlRewriting.Rules; using OrchardCore.UrlRewriting.Helpers; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Rules; +using OrchardCore.UrlRewriting.ViewModels; namespace OrchardCore.UrlRewriting.Drivers; @@ -17,20 +17,20 @@ internal sealed class RewriteRuleDisplayDriver : DisplayDriver { private readonly IAuthorizationService _authorizationService; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly RewriteRulesStore _rewriteRulesStore; + private readonly IShellReleaseManager _shellReleaseManager; internal readonly IStringLocalizer S; public RewriteRuleDisplayDriver( IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor, - RewriteRulesStore rewriteRulesStore, - IStringLocalizer stringLocalizer) + IStringLocalizer stringLocalizer, + IShellReleaseManager shellReleaseManager) { _authorizationService = authorizationService; _httpContextAccessor = httpContextAccessor; - _rewriteRulesStore = rewriteRulesStore; S = stringLocalizer; + _shellReleaseManager = shellReleaseManager; } public override async Task EditAsync(RewriteRule rule, BuildEditorContext context) @@ -40,8 +40,18 @@ public override async Task EditAsync(RewriteRule rule, BuildEdit return null; } - return Initialize("RewriteRuleFields_Edit", viewModel => BuildViewModel(rule, viewModel, context.IsNew)) - .Location("Content:1"); + return Initialize("RewriteRuleFields_Edit", model => + { + model.FromModel(rule); + + model.AvailableActions.Add(new SelectListItem(S["Rewrite"], nameof(RuleAction.Rewrite))); + model.AvailableActions.Add(new SelectListItem(S["Redirect"], nameof(RuleAction.Redirect))); + + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Moved Permanently (301)"], nameof(RedirectType.MovedPermanently301))); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Found (302)"], nameof(RedirectType.Found302))); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Temporary Redirect (307)"], nameof(RedirectType.TemporaryRedirect307))); + model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Permanent Redirect (308)"], nameof(RedirectType.PermanentRedirect308))); + }).Location("Content:1"); } public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) @@ -55,57 +65,39 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE await context.Updater.TryUpdateModelAsync(model, Prefix); - var rules = await _rewriteRulesStore.LoadRewriteRulesAsync(); - if (string.IsNullOrWhiteSpace(model.Name)) { context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Name), "The rule name is required."); } - else if (context.IsNew && rules.Rules.Any(x => string.Equals(x.Name, model.Name, StringComparison.OrdinalIgnoreCase))) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Name), "The rule name already exists."); - } + if (string.IsNullOrWhiteSpace(model.Pattern)) { context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Pattern), "The url match pattern is required."); } + if (model.RuleAction == RuleAction.Rewrite && string.IsNullOrWhiteSpace(model.RewriteAction.RewriteUrl)) { - context.Updater.ModelState.AddModelError(Prefix, "RewriteAction.RewriteUrl", "The rewrite url substitition is required."); + context.Updater.ModelState.AddModelError(Prefix, "RewriteAction.RewriteUrl", "The rewrite url substitution is required."); } + if (model.RuleAction == RuleAction.Redirect && string.IsNullOrWhiteSpace(model.RedirectAction.RedirectUrl)) { - context.Updater.ModelState.AddModelError(Prefix, "RedirectAction.RedirectUrl", "The redirect url substitition is required."); + context.Updater.ModelState.AddModelError(Prefix, "RedirectAction.RedirectUrl", "The redirect url substitution is required."); } - - if (context.Updater.ModelState.IsValid) + if (ApacheRuleValidator.ValidateRule(model, out var message) == false) { - if (ApacheRuleValidator.ValidateRule(model, out var message) == false) - { - context.Updater.ModelState.AddModelError(Prefix, "RuleError", S["Rule error: {0}", message]); - } + context.Updater.ModelState.AddModelError(Prefix, "RuleError", S["Rule error: {0}", message]); } rule.Name = model.Name; rule.Pattern = model.Pattern; - rule.Substitution = model.RuleAction == RuleAction.Rewrite ? model.RewriteAction.RewriteUrl : model.RedirectAction.RedirectUrl; + rule.Substitution = model.RuleAction == RuleAction.Rewrite + ? model.RewriteAction.RewriteUrl + : model.RedirectAction.RedirectUrl; rule.Flags = ApacheRules.FlagsFromViewModel(model); - return await EditAsync(rule, context); - } - - private void BuildViewModel(RewriteRule model, RewriteRuleViewModel viewModel, bool isNew) - { - viewModel.FromModel(model); - - viewModel.IsNew = isNew; - - viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Rewrite"], Value = ((int)RuleAction.Rewrite).ToString() }); - viewModel.AvailableActions.Add(new SelectListItem() { Text = S["Redirect"], Value = ((int)RuleAction.Redirect).ToString() }); + _shellReleaseManager.RequestRelease(); - viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Moved Permanently (301)"], Value = ((int)RedirectType.MovedPermanently301).ToString() }); - viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Found (302)"], Value = ((int)RedirectType.Found302).ToString() }); - viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Temporary Redirect (307)"], Value = ((int)RedirectType.TemporaryRedirect307).ToString() }); - viewModel.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem() { Text = S["Pernament Redirect (308)"], Value = ((int)RedirectType.PernamentRedirect308).ToString() }); + return await EditAsync(rule, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs index 8e96a817132..254dc5dd2d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs @@ -9,8 +9,10 @@ internal static class RewriteRuleViewModelHelper public static void FromModel(this RewriteRuleViewModel viewModel, RewriteRule model) { var flags = model.GetFlagsCollection(); - bool isRedirect = flags.Where(f => f.StartsWith("R=")).Any(); + var isRedirect = flags.Where(f => f.StartsWith("R=")).Any(); + + viewModel.Id = model.Id; viewModel.Name = model.Name; viewModel.Pattern = model.Pattern; viewModel.IgnoreCase = flags.Contains("NC"); @@ -18,7 +20,7 @@ public static void FromModel(this RewriteRuleViewModel viewModel, RewriteRule mo if (isRedirect) { - var redirectFlag = flags.Where(f => f.StartsWith("R=")).FirstOrDefault() ?? ""; + var redirectFlag = flags.Where(f => f.StartsWith("R=")).FirstOrDefault() ?? string.Empty; viewModel.RedirectAction.RedirectUrl = model.Substitution; viewModel.RedirectAction.AppendQueryString = flags.Contains("QSA"); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs index 03d0d8fddba..5839be76108 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs @@ -2,6 +2,8 @@ namespace OrchardCore.UrlRewriting.Models; public sealed class RewriteRule { + public string Id { get; set; } + public string Name { get; set; } public string Pattern { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs index 8dc9171efbb..eca78b86735 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs @@ -4,5 +4,5 @@ namespace OrchardCore.UrlRewriting.Models; public sealed class RewriteRulesDocument : Document { - public List Rules { get; set; } = []; + public Dictionary Rules { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs index 174e65c61c9..f910c01d1f5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs @@ -13,7 +13,9 @@ public sealed class RewriteOptionsConfiguration : IConfigureOptions documentManager, IOptions adminOptions) + public RewriteOptionsConfiguration( + IDocumentManager documentManager, + IOptions adminOptions) { _documentManager = documentManager; _adminOptions = adminOptions.Value; @@ -25,7 +27,7 @@ public void Configure(RewriteOptions options) .GetAwaiter() .GetResult(); - var apacheRules = ApacheRules.FromModels(rules.Rules); + var apacheRules = ApacheRules.FromModels(rules.Rules.Values); using var apacheModRewrite = new StringReader(apacheRules); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs index 02e7537acdb..d4669faa46b 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs @@ -46,22 +46,19 @@ public static string FlagsFromViewModel(RewriteRuleViewModel viewModel) public static string FromViewModel(RewriteRuleViewModel viewModel, bool includeFlags = true) { - var flags = FlagsFromViewModel(viewModel); + var flags = FlagsFromViewModel(viewModel); - var sbRewrite = new StringBuilder(); - var replaceUrl = viewModel.RuleAction == RuleAction.Rewrite ? viewModel.RewriteAction.RewriteUrl : viewModel.RedirectAction.RedirectUrl; + var replaceUrl = viewModel.RuleAction == RuleAction.Rewrite + ? viewModel.RewriteAction.RewriteUrl + : viewModel.RedirectAction.RedirectUrl; var flagsStr = flags.Length > 0 ? $"[{flags}]" : ""; if (flagsStr.Length > 0 && includeFlags) { - sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flagsStr}"); - } - else - { - sbRewrite.Append($"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\""); + return $"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flagsStr}"; } - return sbRewrite.ToString(); + return $"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\""; } public static string FromModel(RewriteRule model) @@ -70,10 +67,8 @@ public static string FromModel(RewriteRule model) { return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\""; } - else - { - return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\" [{model.Flags}]"; - } + + return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\" [{model.Flags}]"; } public static string FromModels(IEnumerable models) @@ -93,7 +88,7 @@ public static RedirectType GetRedirectType(string flag) "R=301" => RedirectType.MovedPermanently301, "R=302" => RedirectType.Found302, "R=307" => RedirectType.TemporaryRedirect307, - "R=308" => RedirectType.PernamentRedirect308, + "R=308" => RedirectType.PermanentRedirect308, _ => RedirectType.Found302 }; } @@ -105,7 +100,7 @@ private static int RedirectTypeToStatusCode(RedirectType redirectType) RedirectType.MovedPermanently301 => 301, RedirectType.Found302 => 302, RedirectType.TemporaryRedirect307 => 307, - RedirectType.PernamentRedirect308 => 308, + RedirectType.PermanentRedirect308 => 308, _ => 302, }; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs index 57e5282b108..6cac47da9e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs @@ -27,6 +27,7 @@ public static bool ValidateRule(RewriteRuleViewModel viewModel, out string valid } validationError = null; + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs index 9ada58e3cd4..6b09b4b7fc5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs @@ -15,29 +15,20 @@ public RewriteRulesStore(IDocumentManager documentManager) /// /// Loads the rewrite rules document from the store for updating and that should not be cached. /// - public Task LoadRewriteRulesAsync() => _documentManager.GetOrCreateMutableAsync(); + public Task LoadRewriteRulesAsync() + => _documentManager.GetOrCreateMutableAsync(); /// /// Gets the rewrite rules document from the cache for sharing and that should not be updated. /// - public Task GetRewriteRulesAsync() => _documentManager.GetOrCreateImmutableAsync(); + public Task GetRewriteRulesAsync() + => _documentManager.GetOrCreateImmutableAsync(); public async Task SaveAsync(RewriteRule rule) { var rules = await LoadRewriteRulesAsync(); - var preexisting = rules.Rules.FirstOrDefault(x => string.Equals(x.Name, rule.Name, StringComparison.OrdinalIgnoreCase)); - - // it's new? add it - if (preexisting == null) - { - rules.Rules.Add(rule); - } - else // not new: replace it - { - var index = rules.Rules.IndexOf(preexisting); - rules.Rules[index] = rule; - } + rules.Rules[rule.Id] = rule; await _documentManager.UpdateAsync(rules); } @@ -45,23 +36,18 @@ public async Task SaveAsync(RewriteRule rule) public async Task DeleteAsync(RewriteRule rule) { var rules = await LoadRewriteRulesAsync(); - var ruleToRemove = rules.Rules.FirstOrDefault(r => string.Equals(r.Name, rule.Name, StringComparison.OrdinalIgnoreCase)); - rules.Rules.Remove(ruleToRemove); + + rules.Rules.Remove(rule.Id); await _documentManager.UpdateAsync(rules); } - public async Task FindByIdAsync(string ruleId) + public async Task FindByIdAsync(string id) { - var rules = await GetRewriteRulesAsync(); - - var rule = rules.Rules.FirstOrDefault(x => string.Equals(x.Name, ruleId, StringComparison.OrdinalIgnoreCase)); - - if (rule == null) - { - return null; - } + var document = await GetRewriteRulesAsync(); - return rule.Clone(); + return document.Rules.TryGetValue(id, out var rule) + ? rule + : null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs index 733521625c0..9b63322cdbb 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -5,6 +5,8 @@ namespace OrchardCore.UrlRewriting.ViewModels; public class RewriteRuleViewModel { + public string Id { get; set; } + public string Name { get; set; } public string Pattern { get; set; } @@ -18,10 +20,9 @@ public class RewriteRuleViewModel public RedirectActionViewModel RedirectAction { get; set; } = new RedirectActionViewModel(); [BindNever] - public string Substitution => RuleAction == RuleAction.Rewrite ? RewriteAction.RewriteUrl : RedirectAction.RedirectUrl; - - [BindNever] - public bool IsNew { get; set; } + public string Substitution => RuleAction == RuleAction.Rewrite + ? RewriteAction.RewriteUrl + : RedirectAction.RedirectUrl; [BindNever] public List AvailableActions { get; set; } = []; @@ -30,7 +31,7 @@ public class RewriteRuleViewModel public enum RuleAction { Rewrite, - Redirect + Redirect, } public enum RedirectType @@ -38,7 +39,7 @@ public enum RedirectType MovedPermanently301, Found302, TemporaryRedirect307, - PernamentRedirect308 + PermanentRedirect308, } public class RedirectActionViewModel diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs index 3133433aef0..7a725be675d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs @@ -2,5 +2,5 @@ namespace OrchardCore.UrlRewriting.ViewModels; public class RewriteRulesViewModel { - public List Rules { get; set; } = []; + public IList Rules { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml similarity index 51% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml index 4a51031d155..d5a57692472 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/CreateRule.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml @@ -2,7 +2,9 @@

@RenderTitleSegments(T["New Rule"])

-
+ @Html.ValidationSummary() @await DisplayAsync(Model) + +
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml new file mode 100644 index 00000000000..5cc4caf7f45 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml @@ -0,0 +1,10 @@ +@model dynamic + +

@RenderTitleSegments(T["Edit Rule"])

+ +
+ @Html.ValidationSummary() + @await DisplayAsync(Model) + + +
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml deleted file mode 100644 index d51943c2fbc..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/EditRule.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@model dynamic - -

@RenderTitleSegments(T["Edit Rule"])

- -
- @Html.ValidationSummary() - @await DisplayAsync(Model) -
diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 651c388cff3..7ee074ab8d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -2,13 +2,6 @@

@RenderTitleSegments(T["URL Rewriting"])

-
-
-

@T["The website needs to be restarted for any changes to the rules to take effect."]

- @T["Restart Website"] -
-
-
@@ -19,28 +12,30 @@
-
+@if (Model.Rules.Count > 0) +{ - - - - + + + + + @{ + var message = T["Are you sure you want to delete this rule?"]; + } @foreach (var rule in Model.Rules) { - var message = T["Are you sure you want to delete this rule?"]; - @@ -48,18 +43,19 @@ }
Rule namePatternSubsitutionAction@T["Rule name"]@T["Pattern"]@T["Substitution"]@T["Action"]
@rule.Name @rule.Pattern@(rule.RuleAction == RuleAction.Rewrite ? T["Rewrite"] : T["Redirect"])
- -
+} + + \ No newline at end of file + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml index 9272feaea6f..316c0cb2935 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/NavigationItemText-urlrewriting.Id.cshtml @@ -1 +1,4 @@ -@T["URL Rewriting"] + + + +@T["URL Rewriting"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml index e76092b9e36..cbfa4e01710 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml @@ -1,95 +1,81 @@ @model RewriteRuleViewModel -
- - - @T["Rule name."] -
- -
- - - @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] -
- -
-
- - +
+
+ + + @T["Rule name."]
- @T["When checked, the pattern will be case insensitive."] -
-
-
- - -
- @T["Action type for matched URL."] -
- -
- - + + + @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."]
- - + + + @T["When checked, the pattern will be case insensitive."]
+
-
- - +
+ +
+ @T["Action type for matched URL."]
-
-
-
- - -
+
+
+ + +
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
-
-
- @if(Model.IsNew) - { - - } - else - { - - } +
+
+ + +
- @T["Cancel"] +
+
+ + +
+
+ +
+
+ + +
+
+
\ No newline at end of file + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml index fd169e3d7c6..c1f08898f5d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/_ViewImports.cshtml @@ -3,4 +3,4 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, OrchardCore.DisplayManagement @addTagHelper *, OrchardCore.ResourceManagement -@using OrchardCore.UrlRewriting.ViewModels \ No newline at end of file +@using OrchardCore.UrlRewriting.ViewModels From 78d7943084c7fd36902fafbfb0587aea1a822ca4 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Wed, 2 Oct 2024 18:03:04 -0700 Subject: [PATCH 18/71] add site release warning --- .../Drivers/RewriteRuleDisplayDriver.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs index ec8a7443f5d..0fbe47c298a 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs @@ -40,6 +40,8 @@ public override async Task EditAsync(RewriteRule rule, BuildEdit return null; } + context.AddTenantReloadWarningWrapper(); + return Initialize("RewriteRuleFields_Edit", model => { model.FromModel(rule); From f5cd1f5567ef812c7a3d580b195b11f80e7b407c Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Wed, 2 Oct 2024 18:16:07 -0700 Subject: [PATCH 19/71] style --- .../OrchardCore.UrlRewriting/Controllers/AdminController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 66e14a9c149..8a9da4555d2 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -128,7 +128,8 @@ public async Task Edit(string id) return View(shape); } - [HttpPost, ActionName(nameof(Edit))] + [HttpPost] + [ActionName(nameof(Edit))] public async Task EditPOST(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) From 1f837757d0bc9d893dae62859ad741d10348c544 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 3 Oct 2024 11:02:15 -0700 Subject: [PATCH 20/71] Use Queries like UI and change everything --- OrchardCore.sln | 14 + .../Controllers/AdminController.cs | 102 +++++--- .../Drivers/RewriteRuleDisplayDriver.cs | 105 -------- .../Drivers/RewriteRulesDisplayDriver.cs | 77 ++++++ .../Drivers/UrlRedirectRuleDisplayDriver.cs | 97 +++++++ .../Drivers/UrlRewriteRuleDisplayDriver.cs | 82 ++++++ .../Helpers/RewriteRuleViewModelHelper.cs | 36 --- .../Models/UrlRewritingSettings.cs | 8 - .../Options/RewriteOptionsConfiguration.cs | 28 +- .../OrchardCore.UrlRewriting.csproj | 1 + .../Recipes/UrlRewritingStep.cs | 5 +- .../Rules/ApacheRules.cs | 107 -------- ...UIRule.cs => ExcludeAdminUrlPrefixRule.cs} | 4 +- .../Rules/RuleValidator.cs | 33 --- .../Services/RewriteRulesStore.cs | 53 ---- .../OrchardCore.UrlRewriting/Startup.cs | 23 +- .../ViewModels/EditRewriteRuleViewModel.cs | 16 ++ .../ViewModels/ListRewriteRuleViewModel.cs | 12 + .../ViewModels/RewriteRuleEntry.cs | 11 + .../ViewModels/RewriteRuleOptions.cs | 20 ++ .../ViewModels/RewriteRuleViewModel.cs | 64 ----- .../ViewModels/RewriteRulesViewModel.cs | 6 - .../ViewModels/UrlRedirectRuleViewModel.cs | 21 ++ .../ViewModels/UrlRewriteRuleViewModel.cs | 12 + .../Views/Admin/Index.cshtml | 243 +++++++++++------- .../Views/RewriteRule-Redirect.Link.cshtml | 13 + .../Views/RewriteRule-Rewrite.Link.cshtml | 13 + .../RewriteRule.Buttons.SummaryAdmin.cshtml | 12 + .../Views/RewriteRule.Edit.cshtml | 11 +- .../Views/RewriteRule.Fields.Buttons.cshtml | 11 + .../Views/RewriteRule.Fields.Edit.cshtml | 16 ++ .../RewriteRule.Fields.SummaryAdmin.cshtml | 3 + .../Views/RewriteRule.Link.cshtml | 10 + .../Views/RewriteRule.SummaryAdmin.cshtml | 15 ++ .../Views/RewriteRuleFields.Edit.cshtml | 96 ------- .../Views/UrlRedirectRule.Edit.cshtml | 32 +++ .../Views/UrlRedirectRule.SummaryAdmin.cshtml | 1 + .../Views/UrlRewriteRule.Edit.cshtml | 27 ++ .../Views/UrlRewriteRule.SummaryAdmin.cshtml | 1 + .../IRewriteRuleHandler.cs | 24 ++ .../IRewriteRulesManager.cs | 16 ++ .../IRewriteRulesStore.cs | 14 + .../IUrlRewriteRuleSource.cs | 11 + .../Models/InitializedRewriteRuleContext.cs | 11 + .../Models/InitializingRewriteRuleContext.cs | 11 + .../Models/ListRewriteRuleResult.cs | 8 + .../Models/LoadedRewriteRuleContext.cs | 11 + .../Models/RewriteRule.cs | 7 +- .../Models/RewriteRulesQueryContext.cs | 10 + ...chardCore.UrlRewriting.Abstractions.csproj | 23 ++ .../Models/RewriteRulesDocument.cs | 0 .../Models/UrlRedirectSourceMetadata.cs | 22 ++ .../Models/UrlRewriteSourceMetadata.cs | 12 + .../OrchardCore.UrlRewriting.Core.csproj | 25 ++ .../ServiceCollectionExtensions.cs | 18 ++ .../Services/RewriteRulesManager.cs | 131 ++++++++++ .../Services/RewriteRulesStore.cs | 48 ++++ .../Services/UrlRedirectRuleSource.cs | 91 +++++++ .../Services/UrlRewriteRuleSource.cs | 65 +++++ 59 files changed, 1335 insertions(+), 664 deletions(-) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/{ExcludeAdminUIRule.cs => ExcludeAdminUrlPrefixRule.cs} (82%) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/ListRewriteRuleViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleEntry.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.SummaryAdmin.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.SummaryAdmin.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.SummaryAdmin.cshtml create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRuleHandler.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesStore.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IUrlRewriteRuleSource.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializedRewriteRuleContext.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializingRewriteRuleContext.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/ListRewriteRuleResult.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/LoadedRewriteRuleContext.cs rename src/{OrchardCore.Modules/OrchardCore.UrlRewriting => OrchardCore/OrchardCore.UrlRewriting.Abstractions}/Models/RewriteRule.cs (78%) create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRulesQueryContext.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/OrchardCore.UrlRewriting.Abstractions.csproj rename src/{OrchardCore.Modules/OrchardCore.UrlRewriting => OrchardCore/OrchardCore.UrlRewriting.Core}/Models/RewriteRulesDocument.cs (100%) create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/OrchardCore.UrlRewriting.Core.csproj create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/ServiceCollectionExtensions.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesStore.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs diff --git a/OrchardCore.sln b/OrchardCore.sln index 139f5bfc951..962062771b3 100644 --- a/OrchardCore.sln +++ b/OrchardCore.sln @@ -529,6 +529,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Sms.Azure", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.UrlRewriting", "src\OrchardCore.Modules\OrchardCore.UrlRewriting\OrchardCore.UrlRewriting.csproj", "{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.UrlRewriting.Abstractions", "src\OrchardCore\OrchardCore.UrlRewriting.Abstractions\OrchardCore.UrlRewriting.Abstractions.csproj", "{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.UrlRewriting.Core", "src\OrchardCore\OrchardCore.UrlRewriting.Core\OrchardCore.UrlRewriting.Core.csproj", "{7B18DD99-A7BB-4297-8679-D87289758756}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1401,6 +1405,14 @@ Global {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.Build.0 = Release|Any CPU + {675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Release|Any CPU.Build.0 = Release|Any CPU + {7B18DD99-A7BB-4297-8679-D87289758756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B18DD99-A7BB-4297-8679-D87289758756}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B18DD99-A7BB-4297-8679-D87289758756}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B18DD99-A7BB-4297-8679-D87289758756}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1643,6 +1655,8 @@ Global {61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} {013C8BBF-6879-4B47-80C9-A466923E45E5} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} {D0F8B342-BDA8-44CB-AA43-7A65C79636A2} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} + {675C8A76-C64F-47EC-B4F5-06D4F2D9662A} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} + {7B18DD99-A7BB-4297-8679-D87289758756} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 8a9da4555d2..b74a7a023e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -1,15 +1,18 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; using OrchardCore.Admin; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Notify; using OrchardCore.Environment.Shell; -using OrchardCore.UrlRewriting.Helpers; +using OrchardCore.Navigation; +using OrchardCore.Routing; using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Services; using OrchardCore.UrlRewriting.ViewModels; namespace OrchardCore.UrlRewriting.Controllers; @@ -17,12 +20,15 @@ namespace OrchardCore.UrlRewriting.Controllers; [Admin("UrlRewriting/{action}/{id?}", "UrlRewriting{action}")] public sealed class AdminController : Controller { + private const string _optionsSearch = "Options.Search"; + private readonly IAuthorizationService _authorizationService; private readonly INotifier _notifier; - private readonly RewriteRulesStore _rewriteRulesStore; private readonly IDisplayManager _rewriteRuleDisplayManager; private readonly IUpdateModelAccessor _updateModelAccessor; private readonly IShellReleaseManager _shellReleaseManager; + private readonly IEnumerable _urlRewritingRuleSources; + private readonly IRewriteRulesManager _rewriteRulesManager; internal readonly IStringLocalizer S; internal readonly IHtmlLocalizer H; @@ -30,50 +36,93 @@ public sealed class AdminController : Controller public AdminController( IDisplayManager rewriteRuleDisplayManager, IAuthorizationService authorizationService, - RewriteRulesStore rewriteRulesStore, INotifier notifier, IShellReleaseManager shellReleaseManager, + IEnumerable urlRewritingRuleSources, + IRewriteRulesManager rewriteRulesManager, IStringLocalizer stringLocalizer, IHtmlLocalizer htmlLocalizer, IUpdateModelAccessor updateModelAccessor) { _rewriteRuleDisplayManager = rewriteRuleDisplayManager; _authorizationService = authorizationService; - _rewriteRulesStore = rewriteRulesStore; _notifier = notifier; _shellReleaseManager = shellReleaseManager; + _urlRewritingRuleSources = urlRewritingRuleSources; + _rewriteRulesManager = rewriteRulesManager; _updateModelAccessor = updateModelAccessor; S = stringLocalizer; H = htmlLocalizer; } - public async Task Index() + public async Task Index( + RewriteRuleOptions options, + PagerParameters pagerParameters, + [FromServices] IShapeFactory shapeFactory, + [FromServices] IOptions pagerOptions) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var rules = await _rewriteRulesStore.GetRewriteRulesAsync(); + var pager = new Pager(pagerParameters, pagerOptions.Value.GetPageSize()); + + // Maintain previous route data when generating page links. + var routeData = new RouteData(); + + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - var model = new RewriteRulesViewModel + var result = await _rewriteRulesManager.PageQueriesAsync(pager.Page, pager.PageSize, new RewriteRulesQueryContext() { - Rules = rules.Rules.Values - .Select(BuildViewModel) - .ToArray() + Name = options.Search, + }); + + var model = new ListRewriteRuleViewModel + { + Rules = [], + Options = options, + Pager = await shapeFactory.PagerAsync(pager, result.Count, routeData), + SourceNames = _urlRewritingRuleSources.Select(x => x.Name), }; + foreach (var rule in result.Records) + { + model.Rules.Add(new RewriteRuleEntry + { + Rule = rule, + Shape = await _rewriteRuleDisplayManager.BuildDisplayAsync(rule, _updateModelAccessor.ModelUpdater, "SummaryAdmin") + }); + } + + model.Options.BulkActions = + [ + new SelectListItem(S["Delete"], nameof(RewriteRuleAction.Remove)), + ]; + return View(model); } - public async Task Create() + [HttpPost] + [ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(ListRewriteRuleViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); + + public async Task Create(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var rule = new RewriteRule(); + var rule = await _rewriteRulesManager.NewAsync(id); var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); @@ -82,22 +131,20 @@ public async Task Create() [HttpPost] [ActionName(nameof(Create))] - public async Task CreatePOST() + public async Task CreatePOST(string id) { if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) { return Forbid(); } - var rule = new RewriteRule(); + var rule = await _rewriteRulesManager.NewAsync(id); var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); if (ModelState.IsValid) { - rule.Id = IdGenerator.GenerateId(); - - await _rewriteRulesStore.SaveAsync(rule); + await _rewriteRulesManager.SaveAsync(rule); await _notifier.SuccessAsync(H["Rule created successfully."]); @@ -116,7 +163,7 @@ public async Task Edit(string id) return Forbid(); } - var rule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesManager.FindByIdAsync(id); if (rule == null) { @@ -137,7 +184,7 @@ public async Task EditPOST(string id) return Forbid(); } - var rule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesManager.FindByIdAsync(id); if (rule == null) { @@ -148,7 +195,7 @@ public async Task EditPOST(string id) if (ModelState.IsValid) { - await _rewriteRulesStore.SaveAsync(rule); + await _rewriteRulesManager.SaveAsync(rule); await _notifier.SuccessAsync(H["Rule updated successfully."]); @@ -168,14 +215,14 @@ public async Task Delete(string id) return Forbid(); } - var rule = await _rewriteRulesStore.FindByIdAsync(id); + var rule = await _rewriteRulesManager.FindByIdAsync(id); if (rule == null) { return NotFound(); } - await _rewriteRulesStore.DeleteAsync(rule); + await _rewriteRulesManager.DeleteAsync(rule); _shellReleaseManager.RequestRelease(); @@ -183,13 +230,4 @@ public async Task Delete(string id) return RedirectToAction(nameof(Index)); } - - private static RewriteRuleViewModel BuildViewModel(RewriteRule rule) - { - var viewModel = new RewriteRuleViewModel(); - - viewModel.FromModel(rule); - - return viewModel; - } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs deleted file mode 100644 index 0fbe47c298a..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRuleDisplayDriver.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.Extensions.Localization; -using OrchardCore.DisplayManagement.Handlers; -using OrchardCore.DisplayManagement.Views; -using OrchardCore.Environment.Shell; -using OrchardCore.Mvc.ModelBinding; -using OrchardCore.UrlRewriting.Helpers; -using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Rules; -using OrchardCore.UrlRewriting.ViewModels; - -namespace OrchardCore.UrlRewriting.Drivers; - -internal sealed class RewriteRuleDisplayDriver : DisplayDriver -{ - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IShellReleaseManager _shellReleaseManager; - - internal readonly IStringLocalizer S; - - public RewriteRuleDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor, - IStringLocalizer stringLocalizer, - IShellReleaseManager shellReleaseManager) - { - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - S = stringLocalizer; - _shellReleaseManager = shellReleaseManager; - } - - public override async Task EditAsync(RewriteRule rule, BuildEditorContext context) - { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, UrlRewritingPermissions.ManageUrlRewriting)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); - - return Initialize("RewriteRuleFields_Edit", model => - { - model.FromModel(rule); - - model.AvailableActions.Add(new SelectListItem(S["Rewrite"], nameof(RuleAction.Rewrite))); - model.AvailableActions.Add(new SelectListItem(S["Redirect"], nameof(RuleAction.Redirect))); - - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Moved Permanently (301)"], nameof(RedirectType.MovedPermanently301))); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Found (302)"], nameof(RedirectType.Found302))); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Temporary Redirect (307)"], nameof(RedirectType.TemporaryRedirect307))); - model.RedirectAction.AvailableRedirectTypes.Add(new SelectListItem(S["Permanent Redirect (308)"], nameof(RedirectType.PermanentRedirect308))); - }).Location("Content:1"); - } - - public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) - { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, UrlRewritingPermissions.ManageUrlRewriting)) - { - return null; - } - - var model = new RewriteRuleViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - - if (string.IsNullOrWhiteSpace(model.Name)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Name), "The rule name is required."); - } - - if (string.IsNullOrWhiteSpace(model.Pattern)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(RewriteRuleViewModel.Pattern), "The url match pattern is required."); - } - - if (model.RuleAction == RuleAction.Rewrite && string.IsNullOrWhiteSpace(model.RewriteAction.RewriteUrl)) - { - context.Updater.ModelState.AddModelError(Prefix, "RewriteAction.RewriteUrl", "The rewrite url substitution is required."); - } - - if (model.RuleAction == RuleAction.Redirect && string.IsNullOrWhiteSpace(model.RedirectAction.RedirectUrl)) - { - context.Updater.ModelState.AddModelError(Prefix, "RedirectAction.RedirectUrl", "The redirect url substitution is required."); - } - if (ApacheRuleValidator.ValidateRule(model, out var message) == false) - { - context.Updater.ModelState.AddModelError(Prefix, "RuleError", S["Rule error: {0}", message]); - } - - rule.Name = model.Name; - rule.Pattern = model.Pattern; - rule.Substitution = model.RuleAction == RuleAction.Rewrite - ? model.RewriteAction.RewriteUrl - : model.RedirectAction.RedirectUrl; - rule.Flags = ApacheRules.FlagsFromViewModel(model); - - _shellReleaseManager.RequestRelease(); - - return await EditAsync(rule, context); - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs new file mode 100644 index 00000000000..ec41c059432 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs @@ -0,0 +1,77 @@ +using Microsoft.Extensions.Localization; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.Views; +using OrchardCore.Mvc.ModelBinding; +using OrchardCore.UrlRewriting; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.Queries.Drivers; + +public sealed class RewriteRulesDisplayDriver : DisplayDriver +{ + private readonly IRewriteRulesManager _rulesManager; + + internal readonly IStringLocalizer S; + + public RewriteRulesDisplayDriver( + IRewriteRulesManager rulesManager, + IStringLocalizer stringLocalizer) + { + _rulesManager = rulesManager; + S = stringLocalizer; + } + + public override Task DisplayAsync(RewriteRule rule, BuildDisplayContext context) + { + return CombineAsync( + Dynamic("RewriteRule_Fields_SummaryAdmin", model => + { + model.Name = rule.Name; + model.Source = rule.Source; + model.Rule = rule; + }).Location("Content:1"), + Dynamic("RewriteRule_Buttons_SummaryAdmin", model => + { + model.Name = rule.Name; + model.Source = rule.Source; + model.Rule = rule; + }).Location("Actions:5") + ); + } + + public override Task EditAsync(RewriteRule rule, BuildEditorContext context) + { + return CombineAsync( + Initialize("RewriteRule_Fields_Edit", model => + { + model.Name = rule.Name; + model.SkipFurtherRules = rule.SkipFurtherRules; + model.Source = rule.Source; + model.Rule = rule; + }).Location("Content:1"), + Initialize("RewriteRule_Fields_Buttons", model => + { + model.Name = rule.Name; + model.Source = rule.Source; + model.SkipFurtherRules = rule.SkipFurtherRules; + model.Rule = rule; + }).Location("Actions:5") + ); + } + + public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(rule, Prefix, + m => m.Name, + m => m.SkipFurtherRules, + m => m.Source); + + if (string.IsNullOrEmpty(rule.Name)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(rule.Name), S["Name is required"]); + } + + return await EditAsync(rule, context); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs new file mode 100644 index 00000000000..39398fca8ca --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs @@ -0,0 +1,97 @@ +using Microsoft.Extensions.Localization; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.Views; +using OrchardCore.Entities; +using OrchardCore.Mvc.ModelBinding; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Services; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.UrlRewriting.Drivers; + +public sealed class UrlRedirectRuleDisplayDriver : DisplayDriver +{ + internal readonly IStringLocalizer S; + + public UrlRedirectRuleDisplayDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } + + public override IDisplayResult Display(RewriteRule rule, BuildDisplayContext context) + { + if (rule.Source != UrlRedirectRuleSource.SourceName) + { + return null; + } + + return Combine( + Dynamic("UrlRedirectRule_SummaryAdmin", model => + { + model.Query = rule; + }).Location("Content:5") + ); + } + + public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) + { + if (rule.Source != UrlRedirectRuleSource.SourceName) + { + return null; + } + + return Initialize("UrlRedirectRule_Edit", model => + { + var metadata = rule.As(); + model.Url = metadata.Url; + model.AppendQueryString = context.IsNew || metadata.AppendQueryString; + model.Pattern = metadata.Pattern; + model.IgnoreCase = metadata.IgnoreCase; + model.RedirectType = metadata.RedirectType; + model.RedirectTypes = + [ + new(S["Moved Permanently (301)"], nameof(RedirectType.MovedPermanently301)), + new(S["Temporary Redirect (307)"], nameof(RedirectType.TemporaryRedirect307)), + new(S["Found (302)"], nameof(RedirectType.Found302)), + new(S["Permanent Redirect (308)"], nameof(RedirectType.PermanentRedirect308)) + ]; + }).Location("Content:5"); + } + + public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) + { + if (rule.Source != UrlRedirectRuleSource.SourceName) + { + return null; + } + + var model = new UrlRedirectRuleViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Pattern, + m => m.IgnoreCase, + m => m.Url, + m => m.AppendQueryString, + m => m.RedirectType); + + if (string.IsNullOrWhiteSpace(model.Pattern)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Pattern), "The url match pattern is required."); + } + + if (string.IsNullOrWhiteSpace(model.Url)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Url), S["The redirect URL is required"]); + } + + rule.Put(new UrlRedirectSourceMetadata() + { + Pattern = model.Pattern, + IgnoreCase = model.IgnoreCase, + Url = model.Url, + AppendQueryString = model.AppendQueryString, + RedirectType = model.RedirectType, + }); + + return Edit(rule, context); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs new file mode 100644 index 00000000000..5522c36b62c --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs @@ -0,0 +1,82 @@ +using Microsoft.Extensions.Localization; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.Views; +using OrchardCore.Entities; +using OrchardCore.Mvc.ModelBinding; +using OrchardCore.UrlRewriting.Models; +using OrchardCore.UrlRewriting.Services; +using OrchardCore.UrlRewriting.ViewModels; + +namespace OrchardCore.UrlRewriting.Drivers; + +public sealed class UrlRewriteRuleDisplayDriver : DisplayDriver +{ + internal readonly IStringLocalizer S; + + public UrlRewriteRuleDisplayDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } + + public override IDisplayResult Display(RewriteRule rule, BuildDisplayContext context) + { + if (rule.Source != UrlRewriteRuleSource.SourceName) + { + return null; + } + + return Combine( + Dynamic("UrlRewriteRule_SummaryAdmin", model => + { + model.Query = rule; + }).Location("Content:5") + ); + } + + public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) + { + if (rule.Source != UrlRewriteRuleSource.SourceName) + { + return null; + } + + return Initialize("UrlRewriteRule_Edit", model => + { + var metadata = rule.As(); + model.Url = metadata.Url; + model.Pattern = metadata.Pattern; + model.IgnoreCase = metadata.IgnoreCase; + model.AppendQueryString = context.IsNew || metadata.AppendQueryString; + }).Location("Content:5"); + } + + public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) + { + if (rule.Source != UrlRewriteRuleSource.SourceName) + { + return null; + } + + var model = new UrlRewriteRuleViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Url, + m => m.AppendQueryString, + m => m.Pattern, + m => m.IgnoreCase); + + if (string.IsNullOrWhiteSpace(model.Url)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Url), S["The rewrite URL is required"]); + } + + rule.Put(new UrlRewriteSourceMetadata() + { + Pattern = model.Pattern, + IgnoreCase = model.IgnoreCase, + Url = model.Url, + AppendQueryString = model.AppendQueryString, + }); + + return Edit(rule, context); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs deleted file mode 100644 index 254dc5dd2d3..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Helpers/RewriteRuleViewModelHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Rules; -using OrchardCore.UrlRewriting.ViewModels; - -namespace OrchardCore.UrlRewriting.Helpers; - -internal static class RewriteRuleViewModelHelper -{ - public static void FromModel(this RewriteRuleViewModel viewModel, RewriteRule model) - { - var flags = model.GetFlagsCollection(); - - var isRedirect = flags.Where(f => f.StartsWith("R=")).Any(); - - viewModel.Id = model.Id; - viewModel.Name = model.Name; - viewModel.Pattern = model.Pattern; - viewModel.IgnoreCase = flags.Contains("NC"); - viewModel.RuleAction = isRedirect ? RuleAction.Redirect : RuleAction.Rewrite; - - if (isRedirect) - { - var redirectFlag = flags.Where(f => f.StartsWith("R=")).FirstOrDefault() ?? string.Empty; - - viewModel.RedirectAction.RedirectUrl = model.Substitution; - viewModel.RedirectAction.AppendQueryString = flags.Contains("QSA"); - viewModel.RedirectAction.RedirectType = ApacheRules.GetRedirectType(redirectFlag); - } - else - { - viewModel.RewriteAction.RewriteUrl = model.Substitution; - viewModel.RewriteAction.AppendQueryString = flags.Contains("QSA"); - viewModel.RewriteAction.SkipFurtherRules = flags.Contains("L"); - } - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs deleted file mode 100644 index 50eb46ebdd6..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/UrlRewritingSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OrchardCore.UrlRewriting.Models; - -public sealed class UrlRewritingSettings -{ - public string ApacheModRewrite { get; set; } - - public List Rules { get; set; } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs index f910c01d1f5..afb8b260b94 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Options/RewriteOptionsConfiguration.cs @@ -1,42 +1,48 @@ using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Options; using OrchardCore.Admin; -using OrchardCore.Documents; -using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Rules; namespace OrchardCore.UrlRewriting.Options; public sealed class RewriteOptionsConfiguration : IConfigureOptions { - private readonly IDocumentManager _documentManager; - + private readonly IRewriteRulesStore _rewriteRulesStore; + private readonly IEnumerable _sources; private readonly AdminOptions _adminOptions; public RewriteOptionsConfiguration( - IDocumentManager documentManager, + IRewriteRulesStore rewriteRulesStore, + IEnumerable sources, IOptions adminOptions) { - _documentManager = documentManager; + _rewriteRulesStore = rewriteRulesStore; + _sources = sources; _adminOptions = adminOptions.Value; } public void Configure(RewriteOptions options) { - var rules = _documentManager.GetOrCreateMutableAsync() + var rules = _rewriteRulesStore.GetAllAsync() .GetAwaiter() .GetResult(); - var apacheRules = ApacheRules.FromModels(rules.Rules.Values); + foreach (var rule in rules) + { + var source = _sources.FirstOrDefault(x => x.Name == rule.Source); - using var apacheModRewrite = new StringReader(apacheRules); + if (source == null) + { + continue; + } - options.AddApacheModRewrite(apacheModRewrite); + source.Configure(options, rule); + } if (options.Rules.Count > 0) { // Exclude admin ui requests to prevent accidental access bricking by provided rules - options.Rules.Insert(0, new ExcludeAdminUIRule(_adminOptions.AdminUrlPrefix)); + options.Rules.Insert(0, new ExcludeAdminUrlPrefixRule(_adminOptions.AdminUrlPrefix)); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj index d1e3f93ec2e..eff0e2cc180 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj @@ -21,6 +21,7 @@ + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs index 6b2a67b5e50..bb003c90336 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs @@ -3,7 +3,6 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Services; namespace OrchardCore.UrlRewriting.Recipes; @@ -12,12 +11,12 @@ namespace OrchardCore.UrlRewriting.Recipes; /// public sealed class UrlRewritingStep : IRecipeStepHandler { - private readonly RewriteRulesStore _rewriteRulesStore; + private readonly IRewriteRulesStore _rewriteRulesStore; internal readonly IStringLocalizer S; public UrlRewritingStep( - RewriteRulesStore rewriteRulesStore, + IRewriteRulesStore rewriteRulesStore, IStringLocalizer stringLocalizer) { _rewriteRulesStore = rewriteRulesStore; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs deleted file mode 100644 index d4669faa46b..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ApacheRules.cs +++ /dev/null @@ -1,107 +0,0 @@ -using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.ViewModels; -using System.Text; - -namespace OrchardCore.UrlRewriting.Rules; - -public sealed class ApacheRules -{ - public static string FlagsFromViewModel(RewriteRuleViewModel viewModel) - { - var sbFlags = new StringBuilder(); - - if (viewModel.IgnoreCase) - { - sbFlags.Append("NC,"); - }; - - if (viewModel.RuleAction == RuleAction.Rewrite) - { - if (viewModel.RewriteAction.AppendQueryString) - { - sbFlags.Append("QSA,"); - } - if (viewModel.RewriteAction.SkipFurtherRules == true) - { - sbFlags.Append("L,"); - } - } - - if (viewModel.RuleAction == RuleAction.Redirect) - { - if (viewModel.RedirectAction.AppendQueryString) - { - sbFlags.Append("QSA,"); - } - sbFlags.Append($"R={RedirectTypeToStatusCode(viewModel.RedirectAction.RedirectType)},"); - } - - if (sbFlags.Length > 0 && sbFlags[sbFlags.Length - 1] == ',') - { - sbFlags.Remove(sbFlags.Length - 1, 1); - } - - return sbFlags.ToString(); - } - - public static string FromViewModel(RewriteRuleViewModel viewModel, bool includeFlags = true) - { - var flags = FlagsFromViewModel(viewModel); - - var replaceUrl = viewModel.RuleAction == RuleAction.Rewrite - ? viewModel.RewriteAction.RewriteUrl - : viewModel.RedirectAction.RedirectUrl; - - var flagsStr = flags.Length > 0 ? $"[{flags}]" : ""; - if (flagsStr.Length > 0 && includeFlags) - { - return $"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\" {flagsStr}"; - } - - return $"RewriteRule \"{viewModel.Pattern}\" \"{replaceUrl}\""; - } - - public static string FromModel(RewriteRule model) - { - if (string.IsNullOrWhiteSpace(model.Flags)) - { - return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\""; - } - - return $"RewriteRule \"{model.Pattern}\" \"{model.Substitution}\" [{model.Flags}]"; - } - - public static string FromModels(IEnumerable models) - { - var sb = new StringBuilder(); - foreach (var model in models) - { - sb.AppendLine(FromModel(model)); - } - return sb.ToString(); - } - - public static RedirectType GetRedirectType(string flag) - { - return flag switch - { - "R=301" => RedirectType.MovedPermanently301, - "R=302" => RedirectType.Found302, - "R=307" => RedirectType.TemporaryRedirect307, - "R=308" => RedirectType.PermanentRedirect308, - _ => RedirectType.Found302 - }; - } - - private static int RedirectTypeToStatusCode(RedirectType redirectType) - { - return redirectType switch - { - RedirectType.MovedPermanently301 => 301, - RedirectType.Found302 => 302, - RedirectType.TemporaryRedirect307 => 307, - RedirectType.PermanentRedirect308 => 308, - _ => 302, - }; - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUrlPrefixRule.cs similarity index 82% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUrlPrefixRule.cs index 02bc0b2d4ab..b6075acbf38 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUIRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/ExcludeAdminUrlPrefixRule.cs @@ -3,11 +3,11 @@ namespace OrchardCore.UrlRewriting.Rules; -internal sealed class ExcludeAdminUIRule : IRule +internal sealed class ExcludeAdminUrlPrefixRule : IRule { private readonly PathString _adminUrlPrefix; - public ExcludeAdminUIRule(string adminUrlPrefix) + public ExcludeAdminUrlPrefixRule(string adminUrlPrefix) { _adminUrlPrefix = new PathString("/" + adminUrlPrefix); } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs deleted file mode 100644 index 6cac47da9e5..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Rules/RuleValidator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Rewrite; -using OrchardCore.UrlRewriting.ViewModels; - -namespace OrchardCore.UrlRewriting.Rules; - -public sealed class ApacheRuleValidator -{ - public static bool ValidateRule(RewriteRuleViewModel viewModel, out string validationError) - { - var apacheRule = ApacheRules.FromViewModel(viewModel, true); - - try - { - var rewriteOptions = new RewriteOptions(); - using var apacheModRewrite = new StringReader(apacheRule); - rewriteOptions.AddApacheModRewrite(apacheModRewrite); - } - catch (FormatException ex) - { - validationError = ex.Message; - return false; - } - catch (Exception ex) - { - validationError = ex.Message; - return false; - } - - validationError = null; - - return true; - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs deleted file mode 100644 index 6b09b4b7fc5..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/RewriteRulesStore.cs +++ /dev/null @@ -1,53 +0,0 @@ -using OrchardCore.Documents; -using OrchardCore.UrlRewriting.Models; - -namespace OrchardCore.UrlRewriting.Services; - -public sealed class RewriteRulesStore -{ - private readonly IDocumentManager _documentManager; - - public RewriteRulesStore(IDocumentManager documentManager) - { - _documentManager = documentManager; - } - - /// - /// Loads the rewrite rules document from the store for updating and that should not be cached. - /// - public Task LoadRewriteRulesAsync() - => _documentManager.GetOrCreateMutableAsync(); - - /// - /// Gets the rewrite rules document from the cache for sharing and that should not be updated. - /// - public Task GetRewriteRulesAsync() - => _documentManager.GetOrCreateImmutableAsync(); - - public async Task SaveAsync(RewriteRule rule) - { - var rules = await LoadRewriteRulesAsync(); - - rules.Rules[rule.Id] = rule; - - await _documentManager.UpdateAsync(rules); - } - - public async Task DeleteAsync(RewriteRule rule) - { - var rules = await LoadRewriteRulesAsync(); - - rules.Rules.Remove(rule.Id); - - await _documentManager.UpdateAsync(rules); - } - - public async Task FindByIdAsync(string id) - { - var document = await GetRewriteRulesAsync(); - - return document.Rules.TryGetValue(id, out var rule) - ? rule - : null; - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index d707aa97209..2ca07fed62b 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -3,16 +3,17 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.Modules; using OrchardCore.Navigation; +using OrchardCore.Queries.Drivers; +using OrchardCore.Recipes; using OrchardCore.Security.Permissions; -using OrchardCore.Modules; using OrchardCore.UrlRewriting.Drivers; -using OrchardCore.DisplayManagement.Handlers; -using OrchardCore.UrlRewriting.Options; using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Services; -using OrchardCore.Recipes; +using OrchardCore.UrlRewriting.Options; using OrchardCore.UrlRewriting.Recipes; +using OrchardCore.UrlRewriting.Services; namespace OrchardCore.UrlRewriting; @@ -23,12 +24,20 @@ public override int Order public override void ConfigureServices(IServiceCollection services) { - services.AddScoped(); + services.AddSingleton(); services.AddNavigationProvider(); - services.AddScoped, RewriteRuleDisplayDriver>(); + services.AddScoped, RewriteRulesDisplayDriver>(); + services.AddPermissionProvider(); services.AddTransient, RewriteOptionsConfiguration>(); services.AddRecipeExecutionStep(); + services.AddScoped(); + + services.AddRewriteRuleSource(UrlRedirectRuleSource.SourceName) + .AddScoped, UrlRedirectRuleDisplayDriver>(); + + services.AddRewriteRuleSource(UrlRewriteRuleSource.SourceName) + .AddScoped, UrlRewriteRuleDisplayDriver>(); } public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs new file mode 100644 index 00000000000..6d0cfb97ebf --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class EditRewriteRuleViewModel +{ + public string Name { get; set; } + + public string Source { get; set; } + + public bool SkipFurtherRules { get; set; } + + [BindNever] + public RewriteRule Rule { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/ListRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/ListRewriteRuleViewModel.cs new file mode 100644 index 00000000000..afd6d8efe49 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/ListRewriteRuleViewModel.cs @@ -0,0 +1,12 @@ +namespace OrchardCore.UrlRewriting.ViewModels; + +public class ListRewriteRuleViewModel +{ + public IList Rules { get; set; } + + public RewriteRuleOptions Options { get; set; } + + public IEnumerable SourceNames { get; set; } + + public dynamic Pager { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleEntry.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleEntry.cs new file mode 100644 index 00000000000..832937b3866 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleEntry.cs @@ -0,0 +1,11 @@ +using OrchardCore.DisplayManagement; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class RewriteRuleEntry +{ + public RewriteRule Rule { get; set; } + + public IShape Shape { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs new file mode 100644 index 00000000000..5e755b26ea9 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class RewriteRuleOptions +{ + public string Search { get; set; } + + public RewriteRuleAction BulkAction { get; set; } + + [BindNever] + public List BulkActions { get; set; } +} + +public enum RewriteRuleAction +{ + None, + Remove +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs deleted file mode 100644 index 9b63322cdbb..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace OrchardCore.UrlRewriting.ViewModels; - -public class RewriteRuleViewModel -{ - public string Id { get; set; } - - public string Name { get; set; } - - public string Pattern { get; set; } - - public bool IgnoreCase { get; set; } - - public RuleAction RuleAction { get; set; } - - public RewriteActionViewModel RewriteAction { get; set; } = new RewriteActionViewModel(); - - public RedirectActionViewModel RedirectAction { get; set; } = new RedirectActionViewModel(); - - [BindNever] - public string Substitution => RuleAction == RuleAction.Rewrite - ? RewriteAction.RewriteUrl - : RedirectAction.RedirectUrl; - - [BindNever] - public List AvailableActions { get; set; } = []; -} - -public enum RuleAction -{ - Rewrite, - Redirect, -} - -public enum RedirectType -{ - MovedPermanently301, - Found302, - TemporaryRedirect307, - PermanentRedirect308, -} - -public class RedirectActionViewModel -{ - public string RedirectUrl { get; set; } - - public bool AppendQueryString { get; set; } = true; - - public RedirectType RedirectType { get; set; } = RedirectType.Found302; - - [BindNever] - public List AvailableRedirectTypes { get; set; } = []; -} - -public class RewriteActionViewModel -{ - public string RewriteUrl { get; set; } - - public bool AppendQueryString { get; set; } = true; - - public bool SkipFurtherRules { get; set; } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs deleted file mode 100644 index 7a725be675d..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRulesViewModel.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OrchardCore.UrlRewriting.ViewModels; - -public class RewriteRulesViewModel -{ - public IList Rules { get; set; } -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs new file mode 100644 index 00000000000..13aac2a25d5 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.Rendering; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.ViewModels; + +public class UrlRedirectRuleViewModel +{ + public string Url { get; set; } + + public bool AppendQueryString { get; set; } + + public string Pattern { get; set; } + + public bool IgnoreCase { get; set; } + + public RedirectType RedirectType { get; set; } + + [BindNever] + public List RedirectTypes { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs new file mode 100644 index 00000000000..55e18e567eb --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs @@ -0,0 +1,12 @@ +namespace OrchardCore.UrlRewriting.ViewModels; + +public class UrlRewriteRuleViewModel +{ + public string Url { get; set; } + + public bool AppendQueryString { get; set; } + + public string Pattern { get; set; } + + public bool IgnoreCase { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 7ee074ab8d5..d721a6b3b66 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -1,115 +1,178 @@ -@model RewriteRulesViewModel +@using Microsoft.AspNetCore.Mvc.Localization +@model ListRewriteRuleViewModel

@RenderTitleSegments(T["URL Rewriting"])

-
-
-
-
- -@if (Model.Rules.Count > 0) -{ - - - - - - - - - - - - @{ - var message = T["Are you sure you want to delete this rule?"]; - } - @foreach (var rule in Model.Rules) - { - - - - - - - + + + + @foreach (var entry in Model.Rules) + { +
  • +
    + + +
    + @await DisplayAsync(entry.Shape) +
  • } - -
    @T["Rule name"]@T["Pattern"]@T["Substitution"]@T["Action"]
    @rule.Name@rule.Pattern@rule.Substitution@(rule.RuleAction == RuleAction.Rewrite ? T["Rewrite"] : T["Redirect"]) -
    - @T["Edit"] - @T["Delete"] +
      + @if (Model.Rules.Any()) + { + int startIndex = (Model.Pager.Page - 1) * (Model.Pager.PageSize) + 1; + int endIndex = startIndex + Model.Rules.Count - 1; + +
    • +
      +
      +
      + + + + +
      +
      +
      + -
    -} - - + } + else + { +
  • + +
  • + } + - diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml new file mode 100644 index 00000000000..9e1b8c88c66 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml @@ -0,0 +1,13 @@ +@model dynamic + +
    +
    +
    +

    @T["Redirect"]

    +

    @T["Redirect Rule."]

    +
    + +
    +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml new file mode 100644 index 00000000000..6cc30635d3b --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml @@ -0,0 +1,13 @@ +@model dynamic + +
    +
    +
    +

    @T["Rewrite"]

    +

    @T["Rewrite Rule."]

    +
    + +
    +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml new file mode 100644 index 00000000000..b275ee216c6 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml @@ -0,0 +1,12 @@ +@model dynamic + +@T["Edit"] + +@T["Delete"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml index 6c1ea05e8f2..ad1bf3a8cc6 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Edit.cshtml @@ -1,10 +1 @@ -
    -
    - @if (Model.Content != null) - { -
    - @await DisplayAsync(Model.Content) -
    - } -
    -
    +@await DisplayAsync(Model.Content) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml new file mode 100644 index 00000000000..fa88463f0d9 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml @@ -0,0 +1,11 @@ +@model EditRewriteRuleViewModel + +
    + + + @T["Cancel"] +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml new file mode 100644 index 00000000000..1f45604ad62 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml @@ -0,0 +1,16 @@ +@model EditRewriteRuleViewModel + +
    + + + + @T["A name of the rule."] + +
    + +
    +
    + + +
    +
    \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml new file mode 100644 index 00000000000..2c9cb11cf84 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml @@ -0,0 +1,3 @@ +@model dynamic + +
    @Model.Name
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml new file mode 100644 index 00000000000..6c75e1ac01a --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml @@ -0,0 +1,10 @@ +
    +
    +
    +

    @Model.Source

    +
    + +
    +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.SummaryAdmin.cshtml new file mode 100644 index 00000000000..c95f30d2336 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.SummaryAdmin.cshtml @@ -0,0 +1,15 @@ +@model dynamic + +
    + @if (Model.Actions != null) + { +
    + @await DisplayAsync(Model.Actions) +
    + } + + @if (Model.Content != null) + { + @await DisplayAsync(Model.Content) + } +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml deleted file mode 100644 index cbfa4e01710..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRuleFields.Edit.cshtml +++ /dev/null @@ -1,96 +0,0 @@ -@model RewriteRuleViewModel - -
    -
    - - - @T["Rule name."] -
    - -
    - - - @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] -
    - -
    -
    - - - @T["When checked, the pattern will be case insensitive."] -
    - -
    - -
    -
    - - -
    - @T["Action type for matched URL."] -
    - -
    -
    - - -
    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    - -
    -
    - - -
    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    -
    - - diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml new file mode 100644 index 00000000000..1e3651153c1 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml @@ -0,0 +1,32 @@ +@model UrlRedirectRuleViewModel + +
    + + + @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] +
    + +
    +
    + + + @T["When checked, the pattern will be case insensitive."] +
    +
    + +
    + + +
    + +
    +
    + + +
    +
    + +
    + + +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.SummaryAdmin.cshtml new file mode 100644 index 00000000000..cc0f7162a01 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.SummaryAdmin.cshtml @@ -0,0 +1 @@ +@T["Redirect Rule"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml new file mode 100644 index 00000000000..01e44005ec2 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml @@ -0,0 +1,27 @@ +@model UrlRewriteRuleViewModel + +
    + + + @T["Rewrite rules use regular expression syntax for pattern matching as defined in ECMA-262."] +
    + +
    +
    + + + @T["When checked, the pattern will be case insensitive."] +
    +
    + +
    + + +
    + +
    +
    + + +
    +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.SummaryAdmin.cshtml new file mode 100644 index 00000000000..4e57c1aad06 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.SummaryAdmin.cshtml @@ -0,0 +1 @@ +@T["Rewrite Rule"] diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRuleHandler.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRuleHandler.cs new file mode 100644 index 00000000000..7e9f313e272 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRuleHandler.cs @@ -0,0 +1,24 @@ +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting; + +public interface IRewriteRuleHandler +{ + /// + /// This method in invoked during rule initializing. + /// + /// An instance of . + Task InitializingAsync(InitializingRewriteRuleContext context); + + /// + /// This method in invoked after the rule was initialized. + /// + /// An instance of . + Task InitializedAsync(InitializedRewriteRuleContext context); + + /// + /// This method in invoked after the rule was loaded from the store. + /// + /// An instance of . + Task LoadedAsync(LoadedRewriteRuleContext context); +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs new file mode 100644 index 00000000000..8d2d3d4f31d --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs @@ -0,0 +1,16 @@ +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting; + +public interface IRewriteRulesManager +{ + Task DeleteAsync(RewriteRule rule); + + Task FindByIdAsync(string id); + + Task NewAsync(string source); + + Task PageQueriesAsync(int page, int pageSize, RewriteRulesQueryContext context = null); + + Task SaveAsync(RewriteRule rule); +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesStore.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesStore.cs new file mode 100644 index 00000000000..107db25ce3b --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesStore.cs @@ -0,0 +1,14 @@ +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting; + +public interface IRewriteRulesStore +{ + Task DeleteAsync(RewriteRule rule); + + Task FindByIdAsync(string id); + + Task> GetAllAsync(); + + Task SaveAsync(RewriteRule rule); +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IUrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IUrlRewriteRuleSource.cs new file mode 100644 index 00000000000..5d7a453480b --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IUrlRewriteRuleSource.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Rewrite; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting; + +public interface IUrlRewriteRuleSource +{ + string Name { get; } + + void Configure(RewriteOptions options, RewriteRule rule); +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializedRewriteRuleContext.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializedRewriteRuleContext.cs new file mode 100644 index 00000000000..8ca320302a1 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializedRewriteRuleContext.cs @@ -0,0 +1,11 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class InitializedRewriteRuleContext +{ + public RewriteRule Rule { get; } + + public InitializedRewriteRuleContext(RewriteRule rule) + { + Rule = rule; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializingRewriteRuleContext.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializingRewriteRuleContext.cs new file mode 100644 index 00000000000..91a2c1f56a6 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/InitializingRewriteRuleContext.cs @@ -0,0 +1,11 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class InitializingRewriteRuleContext +{ + public RewriteRule Rule { get; } + + public InitializingRewriteRuleContext(RewriteRule rule) + { + Rule = rule; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/ListRewriteRuleResult.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/ListRewriteRuleResult.cs new file mode 100644 index 00000000000..a58e1a4f853 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/ListRewriteRuleResult.cs @@ -0,0 +1,8 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class ListRewriteRuleResult +{ + public IEnumerable Records { get; set; } + + public int Count { get; set; } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/LoadedRewriteRuleContext.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/LoadedRewriteRuleContext.cs new file mode 100644 index 00000000000..30fa035b136 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/LoadedRewriteRuleContext.cs @@ -0,0 +1,11 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class LoadedRewriteRuleContext +{ + public RewriteRule Rule { get; } + + public LoadedRewriteRuleContext(RewriteRule rule) + { + Rule = rule; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs similarity index 78% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs rename to src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs index 5839be76108..8c73abc11c5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRule.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs @@ -1,16 +1,21 @@ +using OrchardCore.Entities; + namespace OrchardCore.UrlRewriting.Models; -public sealed class RewriteRule +public sealed class RewriteRule : Entity { public string Id { get; set; } public string Name { get; set; } + public string Source { get; set; } + public string Pattern { get; set; } public string Substitution { get; set; } public string Flags { get; set; } + public bool SkipFurtherRules { get; set; } public string[] GetFlagsCollection() { diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRulesQueryContext.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRulesQueryContext.cs new file mode 100644 index 00000000000..0f7cdef2a79 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRulesQueryContext.cs @@ -0,0 +1,10 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class RewriteRulesQueryContext +{ + public string Source { get; set; } + + public string Name { get; set; } + + public bool Sorted { get; set; } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/OrchardCore.UrlRewriting.Abstractions.csproj b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/OrchardCore.UrlRewriting.Abstractions.csproj new file mode 100644 index 00000000000..9b4cd6c2859 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/OrchardCore.UrlRewriting.Abstractions.csproj @@ -0,0 +1,23 @@ + + + + OrchardCore.UrlRewriting + + OrchardCore UrlRewriting Abstractions + + $(OCCMSDescription) + + Abstractions for the UrlRewriting module. + + $(PackageTags) OrchardCore Abstractions + + + + + + + + + + + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RewriteRulesDocument.cs similarity index 100% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/Models/RewriteRulesDocument.cs rename to src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RewriteRulesDocument.cs diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs new file mode 100644 index 00000000000..719b34d7a54 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs @@ -0,0 +1,22 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class UrlRedirectSourceMetadata +{ + public string Pattern { get; set; } + + public bool IgnoreCase { get; set; } + + public string Url { get; set; } + + public bool AppendQueryString { get; set; } + + public RedirectType RedirectType { get; set; } +} + +public enum RedirectType +{ + Found302, + MovedPermanently301, + TemporaryRedirect307, + PermanentRedirect308, +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs new file mode 100644 index 00000000000..428cb200de5 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs @@ -0,0 +1,12 @@ +namespace OrchardCore.UrlRewriting.Models; + +public class UrlRewriteSourceMetadata +{ + public string Pattern { get; set; } + + public bool IgnoreCase { get; set; } + + public string Url { get; set; } + + public bool AppendQueryString { get; set; } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/OrchardCore.UrlRewriting.Core.csproj b/src/OrchardCore/OrchardCore.UrlRewriting.Core/OrchardCore.UrlRewriting.Core.csproj new file mode 100644 index 00000000000..8a0d6a679b7 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/OrchardCore.UrlRewriting.Core.csproj @@ -0,0 +1,25 @@ + + + + OrchardCore.UrlRewriting + + OrchardCore UrlRewriting Core + + $(OCCMSDescription) + + Core Services for the UrlRewriting module. + + $(PackageTags) OrchardCore Core + + + + + + + + + + + + + diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/ServiceCollectionExtensions.cs new file mode 100644 index 00000000000..dcb0bf0834b --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/ServiceCollectionExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace OrchardCore.UrlRewriting; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddRewriteRuleSource(this IServiceCollection services, string sourceName) + where TSource : class, IUrlRewriteRuleSource + { + ArgumentException.ThrowIfNullOrEmpty(sourceName); + + services.AddSingleton(); + services.AddSingleton(sp => sp.GetService()); + services.AddKeyedSingleton(sourceName, (sp, key) => sp.GetService()); + + return services; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs new file mode 100644 index 00000000000..29ae2b95c16 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -0,0 +1,131 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OrchardCore.Modules; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.Services; + +public class RewriteRulesManager : IRewriteRulesManager +{ + private readonly IRewriteRulesStore _store; + private readonly IEnumerable _rewriteRuleHandlers; + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + + public RewriteRulesManager( + IRewriteRulesStore store, + IEnumerable rewriteRuleHandlers, + IServiceProvider serviceProvider, + ILogger logger) + { + _store = store; + _rewriteRuleHandlers = rewriteRuleHandlers; + _serviceProvider = serviceProvider; + _logger = logger; + } + + public Task DeleteAsync(RewriteRule rule) + => _store.DeleteAsync(rule); + + public async Task FindByIdAsync(string id) + { + var rule = await _store.FindByIdAsync(id); + + if (rule == null) + { + await LoadAsync(rule); + } + + return rule; + } + + public async Task NewAsync(string source) + { + ArgumentException.ThrowIfNullOrEmpty(source); + + var ruleSource = _serviceProvider.GetKeyedService(source); + + if (ruleSource == null) + { + _logger.LogWarning("Unable to find a rule-source that can handle the source '{Source}'.", source); + + return null; + } + + var rule = new RewriteRule() + { + Id = IdGenerator.GenerateId(), + Source = source, + }; + + var initializingContext = new InitializingRewriteRuleContext(rule); + await _rewriteRuleHandlers.InvokeAsync((handler, ctx) => handler.InitializingAsync(ctx), initializingContext, _logger); + + var initializedContext = new InitializedRewriteRuleContext(rule); + await _rewriteRuleHandlers.InvokeAsync((handler, ctx) => handler.InitializedAsync(ctx), initializedContext, _logger); + + // Set the source again after calling handlers to prevent handlers from updating the source during initialization. + rule.Source = source; + + return rule; + } + + public async Task PageQueriesAsync(int page, int pageSize, RewriteRulesQueryContext context = null) + { + var records = await LocateQueriesAsync(context); + + var skip = (page - 1) * pageSize; + + var result = new ListRewriteRuleResult + { + Count = records.Count(), + Records = records.Skip(skip).Take(pageSize).ToArray() + }; + + foreach (var record in result.Records) + { + await LoadAsync(record); + } + + return result; + } + + public Task SaveAsync(RewriteRule rule) + { + throw new NotImplementedException(); + } + + private Task LoadAsync(RewriteRule rule) + { + var loadedContext = new LoadedRewriteRuleContext(rule); + + return _rewriteRuleHandlers.InvokeAsync((handler, context) => handler.LoadedAsync(context), loadedContext, _logger); + } + + private async Task> LocateQueriesAsync(RewriteRulesQueryContext context) + { + var rules = await _store.GetAllAsync(); + + if (context == null) + { + return rules; + } + + if (!string.IsNullOrEmpty(context.Source)) + { + rules = rules.Where(x => x.Source.Equals(context.Source, StringComparison.OrdinalIgnoreCase)); + } + + if (!string.IsNullOrEmpty(context.Name)) + { + rules = rules.Where(x => x.Name.Contains(context.Name, StringComparison.OrdinalIgnoreCase)); + } + + if (context.Sorted) + { + rules = rules.OrderBy(x => x.Name); + } + + return rules; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesStore.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesStore.cs new file mode 100644 index 00000000000..ee798c6c3af --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesStore.cs @@ -0,0 +1,48 @@ +using OrchardCore.Documents; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.Services; + +public sealed class RewriteRulesStore : IRewriteRulesStore +{ + private readonly IDocumentManager _documentManager; + + public RewriteRulesStore(IDocumentManager documentManager) + { + _documentManager = documentManager; + } + + public async Task> GetAllAsync() + { + var document = await _documentManager.GetOrCreateImmutableAsync(); + + return document.Rules.Values; + } + + public async Task SaveAsync(RewriteRule rule) + { + var document = await _documentManager.GetOrCreateMutableAsync(); + + document.Rules[rule.Id] = rule; + + await _documentManager.UpdateAsync(document); + } + + public async Task DeleteAsync(RewriteRule rule) + { + var document = await _documentManager.GetOrCreateMutableAsync(); + + document.Rules.Remove(rule.Id); + + await _documentManager.UpdateAsync(document); + } + + public async Task FindByIdAsync(string id) + { + var document = await _documentManager.GetOrCreateImmutableAsync(); + + return document.Rules.TryGetValue(id, out var rule) + ? rule + : null; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs new file mode 100644 index 00000000000..f6821d99915 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -0,0 +1,91 @@ +using System.Text; +using Microsoft.AspNetCore.Rewrite; +using OrchardCore.Entities; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.Services; + +public class UrlRedirectRuleSource : IUrlRewriteRuleSource +{ + public const string SourceName = "Redirect"; + + public string Name => SourceName; + + public void Configure(RewriteOptions options, RewriteRule rule) + { + using var apacheModRewrite = new StringReader(GetRewriteRule(rule)); + + options.AddApacheModRewrite(apacheModRewrite); + } + + private static string GetRewriteRule(RewriteRule rule) + { + var metadata = rule.As(); + + var flags = GetFlags(rule, metadata); + if (flags.Length > 0) + { + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\""; + } + + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\" [{flags}]"; + } + + private static StringBuilder GetFlags(RewriteRule rule, UrlRedirectSourceMetadata metadata) + { + var sbFlags = new StringBuilder(); + + if (metadata.IgnoreCase) + { + sbFlags.Append("NC"); + }; + + if (metadata.AppendQueryString) + { + if (sbFlags.Length > 0) + { + sbFlags.Append(','); + } + sbFlags.Append("QSA,"); + + sbFlags.Append($"R="); + sbFlags.Append(RedirectTypeToStatusCode(metadata.RedirectType)); + } + + if (rule.SkipFurtherRules == true) + { + if (sbFlags.Length > 0) + { + sbFlags.Append(','); + } + + sbFlags.Append('L'); + } + + return sbFlags; + } + + public static RedirectType GetRedirectType(string flag) + { + return flag switch + { + "R=301" => RedirectType.MovedPermanently301, + "R=302" => RedirectType.Found302, + "R=307" => RedirectType.TemporaryRedirect307, + "R=308" => RedirectType.PermanentRedirect308, + _ => RedirectType.Found302 + }; + } + + private static int RedirectTypeToStatusCode(RedirectType redirectType) + { + return redirectType switch + { + RedirectType.MovedPermanently301 => 301, + RedirectType.Found302 => 302, + RedirectType.TemporaryRedirect307 => 307, + RedirectType.PermanentRedirect308 => 308, + _ => 302, + }; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs new file mode 100644 index 00000000000..a438e82982c --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -0,0 +1,65 @@ +using System.Text; +using Microsoft.AspNetCore.Rewrite; +using OrchardCore.Entities; +using OrchardCore.UrlRewriting.Models; + +namespace OrchardCore.UrlRewriting.Services; + +public class UrlRewriteRuleSource : IUrlRewriteRuleSource +{ + public const string SourceName = "Rewrite"; + + public string Name => SourceName; + + public void Configure(RewriteOptions options, RewriteRule rule) + { + using var apacheModRewrite = new StringReader(GetRewriteRule(rule)); + + options.AddApacheModRewrite(apacheModRewrite); + } + + private static string GetRewriteRule(RewriteRule rule) + { + var metadata = rule.As(); + + var flags = GetFlags(rule, metadata); + if (flags.Length > 0) + { + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\""; + } + + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\" [{flags}]"; + } + + private static StringBuilder GetFlags(RewriteRule rule, UrlRedirectSourceMetadata metadata) + { + var sbFlags = new StringBuilder(); + + if (metadata.IgnoreCase) + { + sbFlags.Append("NC"); + }; + + if (metadata.AppendQueryString) + { + if (sbFlags.Length > 0) + { + sbFlags.Append(','); + } + sbFlags.Append("QSA"); + } + + if (rule.SkipFurtherRules == true) + { + if (sbFlags.Length > 0) + { + sbFlags.Append(','); + } + + sbFlags.Append('L'); + } + + return sbFlags; + } + +} From 342d69bbc999aa84888a0da68205b8675de9be8d Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 3 Oct 2024 11:19:38 -0700 Subject: [PATCH 21/71] remove old properties from RewriteStep --- .../Recipes/UrlRewritingStep.cs | 42 +++++++++++-------- .../IRewriteRulesManager.cs | 3 +- .../Models/RewriteRule.cs | 21 ---------- .../Services/RewriteRulesManager.cs | 10 ++++- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs index bb003c90336..26f5ef6f32d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs @@ -1,5 +1,7 @@ +using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; using OrchardCore.UrlRewriting.Models; @@ -11,15 +13,17 @@ namespace OrchardCore.UrlRewriting.Recipes; /// public sealed class UrlRewritingStep : IRecipeStepHandler { - private readonly IRewriteRulesStore _rewriteRulesStore; - + private readonly JsonSerializerOptions _jsonSerializerOptions; + private readonly IRewriteRulesManager _rewriteRulesManager; internal readonly IStringLocalizer S; public UrlRewritingStep( - IRewriteRulesStore rewriteRulesStore, + IRewriteRulesManager rewriteRulesManager, + IOptions jsonSerializerOptions, IStringLocalizer stringLocalizer) { - _rewriteRulesStore = rewriteRulesStore; + _rewriteRulesManager = rewriteRulesManager; + _jsonSerializerOptions = jsonSerializerOptions.Value; S = stringLocalizer; } @@ -30,34 +34,38 @@ public async Task ExecuteAsync(RecipeExecutionContext context) return; } - var model = context.Step.ToObject(); + var model = context.Step.ToObject(_jsonSerializerOptions); + + var rules = new List(); - foreach (var importedRule in model.Rules) + foreach (var token in model.Rules.Cast()) { - if (string.IsNullOrWhiteSpace(importedRule.Name)) - { - context.Errors.Add(S["Unable to add or update url rewriting rule. The rule name cannot be null or empty."]); - continue; - } + var name = token[nameof(RewriteRule.Name)]?.GetValue(); - if (string.IsNullOrEmpty(importedRule.Pattern)) + if (string.IsNullOrEmpty(name)) { - context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule Pattern field cannot be null or empty.", importedRule.Name]); + context.Errors.Add(S["Rule name is missing or empty. The query will not be imported."]); + continue; } - if (string.IsNullOrEmpty(importedRule.Substitution)) + var sourceName = token[nameof(RewriteRule.Source)]?.GetValue(); + + if (string.IsNullOrEmpty(sourceName)) { - context.Errors.Add(S["Unable to add or update url rewriting rule '{0}'. The rule Substitution field cannot be null or empty.", importedRule.Name]); + context.Errors.Add(S["Could not find rule source value. The rule '{0}' will not be imported.", name]); + continue; } - await _rewriteRulesStore.SaveAsync(importedRule); + var rule = await _rewriteRulesManager.NewAsync(sourceName, token); + + rules.Add(rule); } } } public sealed class UrlRewritingStepModel { - public RewriteRule[] Rules { get; set; } + public JsonArray Rules { get; set; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs index 8d2d3d4f31d..392cdcf8ad8 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Nodes; using OrchardCore.UrlRewriting.Models; namespace OrchardCore.UrlRewriting; @@ -8,7 +9,7 @@ public interface IRewriteRulesManager Task FindByIdAsync(string id); - Task NewAsync(string source); + Task NewAsync(string source, JsonNode data = null); Task PageQueriesAsync(int page, int pageSize, RewriteRulesQueryContext context = null); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs index 8c73abc11c5..7e3cf1be60b 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs @@ -10,26 +10,5 @@ public sealed class RewriteRule : Entity public string Source { get; set; } - public string Pattern { get; set; } - - public string Substitution { get; set; } - - public string Flags { get; set; } public bool SkipFurtherRules { get; set; } - - public string[] GetFlagsCollection() - { - return Flags?.Split(',') ?? []; - } - - public RewriteRule Clone() - { - return new RewriteRule - { - Name = Name, - Pattern = Pattern, - Substitution = Substitution, - Flags = Flags - }; - } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 29ae2b95c16..96bc9e5937c 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Nodes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OrchardCore.Modules; @@ -39,7 +40,7 @@ public async Task FindByIdAsync(string id) return rule; } - public async Task NewAsync(string source) + public async Task NewAsync(string source, JsonNode data = null) { ArgumentException.ThrowIfNullOrEmpty(source); @@ -61,11 +62,18 @@ public async Task NewAsync(string source) var initializingContext = new InitializingRewriteRuleContext(rule); await _rewriteRuleHandlers.InvokeAsync((handler, ctx) => handler.InitializingAsync(ctx), initializingContext, _logger); + if (data != null) + { + rule.Name = data[nameof(RewriteRule.Name)]?.GetValue(); + rule.SkipFurtherRules = data[nameof(RewriteRule.SkipFurtherRules)]?.GetValue() ?? false; + } + var initializedContext = new InitializedRewriteRuleContext(rule); await _rewriteRuleHandlers.InvokeAsync((handler, ctx) => handler.InitializedAsync(ctx), initializedContext, _logger); // Set the source again after calling handlers to prevent handlers from updating the source during initialization. rule.Source = source; + rule.Id ??= IdGenerator.GenerateId(); return rule; } From a1de614cad704c7a39edad454eb5c4807ac469e1 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 3 Oct 2024 11:24:20 -0700 Subject: [PATCH 22/71] missed method --- .../Services/RewriteRulesManager.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 96bc9e5937c..79326133747 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -99,9 +99,7 @@ public async Task PageQueriesAsync(int page, int pageSize } public Task SaveAsync(RewriteRule rule) - { - throw new NotImplementedException(); - } + => _store.SaveAsync(rule); private Task LoadAsync(RewriteRule rule) { From b567e9265d0e952624230a355fc9b898ea26b1a5 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Fri, 4 Oct 2024 14:52:39 -0700 Subject: [PATCH 23/71] Fix the UI, add order and simplify drivers and views --- .../Drivers/RewriteRulesDisplayDriver.cs | 28 +++------- .../Drivers/UrlRedirectRuleDisplayDriver.cs | 16 +----- .../Drivers/UrlRewriteRuleDisplayDriver.cs | 22 +++----- .../OrchardCore.UrlRewriting/Startup.cs | 4 +- .../ViewModels/EditRewriteRuleViewModel.cs | 2 + .../Views/Admin/Index.cshtml | 2 +- .../Views/RewriteRule-Redirect.Link.cshtml | 13 ----- .../Views/RewriteRule-Rewrite.Link.cshtml | 13 ----- .../RewriteRule.Buttons.SummaryAdmin.cshtml | 20 ++++--- ...ewriteRule.DefaultMeta.SummaryAdmin.cshtml | 24 +++++++++ ...ewriteRule.DefaultTags.SummaryAdmin.cshtml | 8 +++ .../Views/RewriteRule.Fields.Buttons.cshtml | 5 +- .../Views/RewriteRule.Fields.Edit.cshtml | 12 ++++- .../RewriteRule.Fields.SummaryAdmin.cshtml | 7 ++- .../Views/RewriteRule.Link.cshtml | 7 +++ .../Views/RewriteRule.SummaryAdmin.cshtml | 53 +++++++++++++++---- .../Views/UrlRedirectRule.SummaryAdmin.cshtml | 1 - .../Views/UrlRewriteRule.SummaryAdmin.cshtml | 1 - .../IUrlRewriteRuleSource.cs | 3 ++ .../Models/RewriteRule.cs | 8 +++ .../Handler/RewriteRuleHandler.cs | 15 ++++++ .../Handler/UpdateRewriteRuleHandler.cs | 29 ++++++++++ .../OrchardCore.UrlRewriting.Core.csproj | 1 + .../Rules/ExcludeAdminUrlPrefixRule.cs | 0 .../Services}/RewriteOptionsConfiguration.cs | 9 +++- .../Services/RewriteRulesManager.cs | 3 +- .../Services/UrlRedirectRuleSource.cs | 28 +++++++--- .../Services/UrlRewriteRuleSource.cs | 27 +++++++--- 28 files changed, 240 insertions(+), 121 deletions(-) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultTags.SummaryAdmin.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.SummaryAdmin.cshtml delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.SummaryAdmin.cshtml create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Handler/RewriteRuleHandler.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Handler/UpdateRewriteRuleHandler.cs rename src/{OrchardCore.Modules/OrchardCore.UrlRewriting => OrchardCore/OrchardCore.UrlRewriting.Core}/Rules/ExcludeAdminUrlPrefixRule.cs (100%) rename src/{OrchardCore.Modules/OrchardCore.UrlRewriting/Options => OrchardCore/OrchardCore.UrlRewriting.Core/Services}/RewriteOptionsConfiguration.cs (86%) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs index ec41c059432..b8dc5c7614e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs @@ -25,18 +25,10 @@ public RewriteRulesDisplayDriver( public override Task DisplayAsync(RewriteRule rule, BuildDisplayContext context) { return CombineAsync( - Dynamic("RewriteRule_Fields_SummaryAdmin", model => - { - model.Name = rule.Name; - model.Source = rule.Source; - model.Rule = rule; - }).Location("Content:1"), - Dynamic("RewriteRule_Buttons_SummaryAdmin", model => - { - model.Name = rule.Name; - model.Source = rule.Source; - model.Rule = rule; - }).Location("Actions:5") + View("RewriteRule_Fields_SummaryAdmin", rule).Location("Content:1"), + View("RewriteRule_Buttons_SummaryAdmin", rule).Location("Actions:5"), + View("RewriteRule_DefaultTags_SummaryAdmin", rule).Location("Tags:5"), + View("RewriteRule_DefaultMeta_SummaryAdmin", rule).Location("Meta:5") ); } @@ -48,15 +40,10 @@ public override Task EditAsync(RewriteRule rule, BuildEditorCont model.Name = rule.Name; model.SkipFurtherRules = rule.SkipFurtherRules; model.Source = rule.Source; + model.Order = rule.Order; model.Rule = rule; }).Location("Content:1"), - Initialize("RewriteRule_Fields_Buttons", model => - { - model.Name = rule.Name; - model.Source = rule.Source; - model.SkipFurtherRules = rule.SkipFurtherRules; - model.Rule = rule; - }).Location("Actions:5") + View("RewriteRule_Fields_Buttons", rule).Location("Actions:5") ); } @@ -65,7 +52,8 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE await context.Updater.TryUpdateModelAsync(rule, Prefix, m => m.Name, m => m.SkipFurtherRules, - m => m.Source); + m => m.Source, + m => m.Order); if (string.IsNullOrEmpty(rule.Name)) { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs index 39398fca8ca..c74dfb2a761 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs @@ -18,21 +18,6 @@ public UrlRedirectRuleDisplayDriver(IStringLocalizer - { - model.Query = rule; - }).Location("Content:5") - ); - } - public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) { if (rule.Source != UrlRedirectRuleSource.SourceName) @@ -66,6 +51,7 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE } var model = new UrlRedirectRuleViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.Pattern, m => m.IgnoreCase, diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs index 5522c36b62c..3c8b86cc02d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs @@ -18,21 +18,6 @@ public UrlRewriteRuleDisplayDriver(IStringLocalizer S = stringLocalizer; } - public override IDisplayResult Display(RewriteRule rule, BuildDisplayContext context) - { - if (rule.Source != UrlRewriteRuleSource.SourceName) - { - return null; - } - - return Combine( - Dynamic("UrlRewriteRule_SummaryAdmin", model => - { - model.Query = rule; - }).Location("Content:5") - ); - } - public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) { if (rule.Source != UrlRewriteRuleSource.SourceName) @@ -42,7 +27,7 @@ public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context return Initialize("UrlRewriteRule_Edit", model => { - var metadata = rule.As(); + var metadata = rule.As(); model.Url = metadata.Url; model.Pattern = metadata.Pattern; model.IgnoreCase = metadata.IgnoreCase; @@ -64,6 +49,11 @@ await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.Pattern, m => m.IgnoreCase); + if (string.IsNullOrWhiteSpace(model.Pattern)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Pattern), "The url match pattern is required."); + } + if (string.IsNullOrWhiteSpace(model.Url)) { context.Updater.ModelState.AddModelError(Prefix, nameof(model.Url), S["The rewrite URL is required"]); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 2ca07fed62b..c8878a50c48 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -10,8 +10,8 @@ using OrchardCore.Recipes; using OrchardCore.Security.Permissions; using OrchardCore.UrlRewriting.Drivers; +using OrchardCore.UrlRewriting.Handler; using OrchardCore.UrlRewriting.Models; -using OrchardCore.UrlRewriting.Options; using OrchardCore.UrlRewriting.Recipes; using OrchardCore.UrlRewriting.Services; @@ -32,7 +32,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddTransient, RewriteOptionsConfiguration>(); services.AddRecipeExecutionStep(); services.AddScoped(); - + services.AddScoped(); services.AddRewriteRuleSource(UrlRedirectRuleSource.SourceName) .AddScoped, UrlRedirectRuleDisplayDriver>(); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs index 6d0cfb97ebf..f9813563941 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs @@ -11,6 +11,8 @@ public class EditRewriteRuleViewModel public bool SkipFurtherRules { get; set; } + public int Order { get; set; } + [BindNever] public RewriteRule Rule { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index d721a6b3b66..41b787f7584 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -19,7 +19,7 @@
    - +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml deleted file mode 100644 index 9e1b8c88c66..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Redirect.Link.cshtml +++ /dev/null @@ -1,13 +0,0 @@ -@model dynamic - -
    -
    -
    -

    @T["Redirect"]

    -

    @T["Redirect Rule."]

    -
    - -
    -
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml deleted file mode 100644 index 6cc30635d3b..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule-Rewrite.Link.cshtml +++ /dev/null @@ -1,13 +0,0 @@ -@model dynamic - -
    -
    -
    -

    @T["Rewrite"]

    -

    @T["Rewrite Rule."]

    -
    - -
    -
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml index b275ee216c6..0bdf9564b59 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Buttons.SummaryAdmin.cshtml @@ -1,12 +1,16 @@ -@model dynamic +@using OrchardCore.DisplayManagement.Views +@using OrchardCore.UrlRewriting.Models + +@model ShapeViewModel + @T["Edit"] + asp-controller="Admin" + asp-route-id="@Model.Value.Id" + class="btn btn-primary btn-sm">@T["Edit"] @T["Delete"] + asp-controller="Admin" + asp-route-id="@Model.Value.Id" + class="btn btn-danger btn-sm" + data-url-af="RemoveUrl UnsafeUrl">@T["Delete"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml new file mode 100644 index 00000000000..d4d366d3237 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml @@ -0,0 +1,24 @@ +@using System.Globalization +@using OrchardCore.DisplayManagement.Views +@using OrchardCore.UrlRewriting.Models + +@model ShapeViewModel + +@{ + var createdAt = Model.Value.CreatedUtc.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); +} + + + + + + + @Model.Value.Order + + +@if (!string.IsNullOrEmpty(Model.Value.Author)) +{ + + @Model.Value.Author + +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultTags.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultTags.SummaryAdmin.cshtml new file mode 100644 index 00000000000..03f1fad540d --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultTags.SummaryAdmin.cshtml @@ -0,0 +1,8 @@ +@using OrchardCore.DisplayManagement.Views +@using OrchardCore.UrlRewriting.Models + +@model ShapeViewModel + + + @Model.Value.Source + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml index fa88463f0d9..65de1b7c884 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml @@ -1,4 +1,7 @@ -@model EditRewriteRuleViewModel +@using OrchardCore.DisplayManagement.Views +@using OrchardCore.UrlRewriting.Models + +@model ShapeViewModel
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml index 1f45604ad62..f6c92410c05 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml @@ -2,15 +2,23 @@
    - + @T["A name of the rule."]
    +
    + + + + @T["The execution order number of this rule."] + +
    +
    -
    \ No newline at end of file +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml index 2c9cb11cf84..3c603d0af13 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.SummaryAdmin.cshtml @@ -1,3 +1,6 @@ -@model dynamic +@using OrchardCore.DisplayManagement.Views +@using OrchardCore.UrlRewriting.Models -
    @Model.Name
    +@model ShapeViewModel + +
    @Model.Value.Name
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml index 6c75e1ac01a..8467c1bafa9 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml @@ -1,7 +1,14 @@ +@using Microsoft.Extensions.DependencyInjection +@using OrchardCore.UrlRewriting + +@{ + var service = ViewContext.HttpContext.RequestServices.GetKeyedService(Model.Source as string); +}

    @Model.Source

    +

    @service?.Description

    + +
    +
    + + +
    +
    \ No newline at end of file diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs index 388f54e9ca4..e3facb78c43 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRule.cs @@ -12,8 +12,6 @@ public sealed class RewriteRule : Entity public int Order { get; set; } - public bool SkipFurtherRules { get; set; } - public DateTime CreatedUtc { get; set; } public string OwnerId { get; set; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs index 428cb200de5..7423f807ece 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs @@ -9,4 +9,6 @@ public class UrlRewriteSourceMetadata public string Url { get; set; } public bool AppendQueryString { get; set; } + + public bool SkipFurtherRules { get; set; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs index 4842c127dbd..a07b7fe1165 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs @@ -37,11 +37,6 @@ public void Configure(RewriteOptions options) } source.Configure(options, rule); - - if (rule.SkipFurtherRules) - { - break; - } } if (options.Rules.Count > 0) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index c25f860bf97..5877ac9d228 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -73,7 +73,6 @@ public async Task NewAsync(string source, JsonNode data = null) if (data != null) { rule.Name = data[nameof(RewriteRule.Name)]?.GetValue(); - rule.SkipFurtherRules = data[nameof(RewriteRule.SkipFurtherRules)]?.GetValue() ?? false; } var initializedContext = new InitializedRewriteRuleContext(rule); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index 7699749c7d6..55690af3e10 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -68,16 +68,6 @@ private static StringBuilder GetFlags(RewriteRule rule, UrlRedirectSourceMetadat sbFlags.Append(RedirectTypeToStatusCode(metadata.RedirectType)); } - if (rule.SkipFurtherRules == true) - { - if (sbFlags.Length > 0) - { - sbFlags.Append(','); - } - - sbFlags.Append('L'); - } - return sbFlags; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index b3fd5d6faa9..69690c716a4 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -64,7 +64,7 @@ private static StringBuilder GetFlags(RewriteRule rule, UrlRewriteSourceMetadata sbFlags.Append("QSA"); } - if (rule.SkipFurtherRules == true) + if (metadata.SkipFurtherRules) { if (sbFlags.Length > 0) { From a33b1046380a1fcd75ad689821a9019c062a5a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Sun, 6 Oct 2024 21:45:06 +0200 Subject: [PATCH 31/71] Fix RedirectRule flags creation --- .../Services/UrlRedirectRuleSource.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index 55690af3e10..621fba0e355 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -63,11 +63,11 @@ private static StringBuilder GetFlags(RewriteRule rule, UrlRedirectSourceMetadat sbFlags.Append(','); } sbFlags.Append("QSA,"); - - sbFlags.Append($"R="); - sbFlags.Append(RedirectTypeToStatusCode(metadata.RedirectType)); } + sbFlags.Append($"R="); + sbFlags.Append(RedirectTypeToStatusCode(metadata.RedirectType)); + return sbFlags; } From b5344318767113be9066f8d958ae573deb76ca49 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Mon, 7 Oct 2024 15:27:28 -0700 Subject: [PATCH 32/71] fix build --- .../Models/RewriteRuleContextBase.cs | 4 ++- .../Services/UrlRedirectRuleSource.cs | 26 ++++++++--------- .../Services/UrlRewriteRuleSource.cs | 28 +++++++++---------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRuleContextBase.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRuleContextBase.cs index 6bae1ea8c4b..58ebaf25eb8 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRuleContextBase.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRuleContextBase.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.UrlRewriting.Models; +namespace OrchardCore.UrlRewriting.Models; public abstract class RewriteRuleContextBase { @@ -6,6 +6,8 @@ public abstract class RewriteRuleContextBase public RewriteRuleContextBase(RewriteRule rule) { + ArgumentNullException.ThrowIfNull(rule); + Rule = rule; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index 621fba0e355..d7cba09b5d3 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -30,14 +30,14 @@ public void Configure(RewriteOptions options, RewriteRule rule) return; } - using var apacheModRewrite = new StringReader(GetRewriteRule(rule, metadata)); + using var reader = new StringReader(GetRewriteRule(metadata)); - options.AddApacheModRewrite(apacheModRewrite); + options.AddApacheModRewrite(reader); } - private static string GetRewriteRule(RewriteRule rule, UrlRedirectSourceMetadata metadata) + private static string GetRewriteRule(UrlRedirectSourceMetadata metadata) { - var flags = GetFlags(rule, metadata); + var flags = GetFlags(metadata); if (flags.Length > 0) { @@ -47,28 +47,28 @@ private static string GetRewriteRule(RewriteRule rule, UrlRedirectSourceMetadata return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\""; } - private static StringBuilder GetFlags(RewriteRule rule, UrlRedirectSourceMetadata metadata) + private static StringBuilder GetFlags(UrlRedirectSourceMetadata metadata) { - var sbFlags = new StringBuilder(); + var builder = new StringBuilder(); if (metadata.IgnoreCase) { - sbFlags.Append("NC"); + builder.Append("NC"); }; if (metadata.AppendQueryString) { - if (sbFlags.Length > 0) + if (builder.Length > 0) { - sbFlags.Append(','); + builder.Append(','); } - sbFlags.Append("QSA,"); + builder.Append("QSA,"); } - sbFlags.Append($"R="); - sbFlags.Append(RedirectTypeToStatusCode(metadata.RedirectType)); + builder.Append($"R="); + builder.Append(RedirectTypeToStatusCode(metadata.RedirectType)); - return sbFlags; + return builder; } public static RedirectType GetRedirectType(string flag) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index 69690c716a4..fd301a491ca 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -29,14 +29,14 @@ public void Configure(RewriteOptions options, RewriteRule rule) return; } - using var apacheModRewrite = new StringReader(GetRewriteRule(rule, metadata)); + using var reader = new StringReader(GetRewriteRule(metadata)); - options.AddApacheModRewrite(apacheModRewrite); + options.AddApacheModRewrite(reader); } - private static string GetRewriteRule(RewriteRule rule, UrlRewriteSourceMetadata metadata) + private static string GetRewriteRule(UrlRewriteSourceMetadata metadata) { - var flags = GetFlags(rule, metadata); + var flags = GetFlags(metadata); if (flags.Length > 0) { @@ -46,35 +46,35 @@ private static string GetRewriteRule(RewriteRule rule, UrlRewriteSourceMetadata return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\""; } - private static StringBuilder GetFlags(RewriteRule rule, UrlRewriteSourceMetadata metadata) + private static StringBuilder GetFlags(UrlRewriteSourceMetadata metadata) { - var sbFlags = new StringBuilder(); + var builder = new StringBuilder(); if (metadata.IgnoreCase) { - sbFlags.Append("NC"); + builder.Append("NC"); }; if (metadata.AppendQueryString) { - if (sbFlags.Length > 0) + if (builder.Length > 0) { - sbFlags.Append(','); + builder.Append(','); } - sbFlags.Append("QSA"); + builder.Append("QSA"); } if (metadata.SkipFurtherRules) { - if (sbFlags.Length > 0) + if (builder.Length > 0) { - sbFlags.Append(','); + builder.Append(','); } - sbFlags.Append('L'); + builder.Append('L'); } - return sbFlags; + return builder; } } From 53b08e2a2a7e85059646ce4ce25d916691495808 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Mon, 7 Oct 2024 15:46:34 -0700 Subject: [PATCH 33/71] cleanup --- .../Extensions/StringBuilderExtensions.cs | 60 +++++++++++++++++++ ...lPrefixRule.cs => ExcludeUrlPrefixRule.cs} | 16 ++--- .../Services/RewriteOptionsConfiguration.cs | 4 +- .../Services/UrlRedirectRuleSource.cs | 13 ++-- .../Services/UrlRewriteRuleSource.cs | 16 ++--- 5 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 src/OrchardCore/OrchardCore.Abstractions/Modules/Extensions/StringBuilderExtensions.cs rename src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/{ExcludeAdminUrlPrefixRule.cs => ExcludeUrlPrefixRule.cs} (57%) diff --git a/src/OrchardCore/OrchardCore.Abstractions/Modules/Extensions/StringBuilderExtensions.cs b/src/OrchardCore/OrchardCore.Abstractions/Modules/Extensions/StringBuilderExtensions.cs new file mode 100644 index 00000000000..342bb529602 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Abstractions/Modules/Extensions/StringBuilderExtensions.cs @@ -0,0 +1,60 @@ +using System.Text; + +namespace OrchardCore.Modules.Extensions; + +public static class StringBuilderExtensions +{ + private const char _comma = ','; + + public static StringBuilder AppendComma(this StringBuilder builder) + { + builder.Append(_comma); + + return builder; + } + + public static StringBuilder AppendCommaSeparatedValues(this StringBuilder builder, params string[] values) + { + if (values == null || values.Length == 0) + { + return builder; + } + + foreach (var value in values) + { + if (value == null) + { + continue; + } + + if (builder.Length > 0) + { + builder.AppendComma(); + } + + builder.Append(value); + } + + return builder; + } + + public static StringBuilder AppendCommaSeparatedValues(this StringBuilder builder, params char[] values) + { + if (values == null || values.Length == 0) + { + return builder; + } + + foreach (var value in values) + { + if (builder.Length > 0) + { + builder.AppendComma(); + } + + builder.Append(value); + } + + return builder; + } +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeAdminUrlPrefixRule.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs similarity index 57% rename from src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeAdminUrlPrefixRule.cs rename to src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs index b6075acbf38..3d515e84c0c 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeAdminUrlPrefixRule.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs @@ -3,13 +3,15 @@ namespace OrchardCore.UrlRewriting.Rules; -internal sealed class ExcludeAdminUrlPrefixRule : IRule +internal sealed class ExcludeUrlPrefixRule : IRule { private readonly PathString _adminUrlPrefix; - public ExcludeAdminUrlPrefixRule(string adminUrlPrefix) + public ExcludeUrlPrefixRule(string prefix) { - _adminUrlPrefix = new PathString("/" + adminUrlPrefix); + ArgumentException.ThrowIfNullOrEmpty(prefix); + + _adminUrlPrefix = new PathString('/' + prefix.TrimStart('/')); } public void ApplyRule(RewriteContext context) @@ -17,10 +19,10 @@ public void ApplyRule(RewriteContext context) if (context.HttpContext.Request.Path.StartsWithSegments(_adminUrlPrefix)) { context.Result = RuleResult.SkipRemainingRules; + + return; } - else - { - context.Result = RuleResult.ContinueRules; - } + + context.Result = RuleResult.ContinueRules; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs index a07b7fe1165..04de1d02d73 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs @@ -41,8 +41,8 @@ public void Configure(RewriteOptions options) if (options.Rules.Count > 0) { - // Exclude admin ui requests to prevent accidental access bricking by provided rules - options.Rules.Insert(0, new ExcludeAdminUrlPrefixRule(_adminOptions.AdminUrlPrefix)); + // Exclude URIs prefixed with 'admin' to prevent accidental access restrictions caused by the provided rules. + options.Rules.Insert(0, new ExcludeUrlPrefixRule(_adminOptions.AdminUrlPrefix)); } } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index d7cba09b5d3..5937ccd4b42 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Localization; using OrchardCore.Entities; +using OrchardCore.Modules.Extensions; using OrchardCore.UrlRewriting.Models; namespace OrchardCore.UrlRewriting.Services; @@ -53,20 +54,16 @@ private static StringBuilder GetFlags(UrlRedirectSourceMetadata metadata) if (metadata.IgnoreCase) { - builder.Append("NC"); + builder.AppendCommaSeparatedValues("NC"); }; if (metadata.AppendQueryString) { - if (builder.Length > 0) - { - builder.Append(','); - } - builder.Append("QSA,"); + builder.AppendCommaSeparatedValues("QSA"); } - builder.Append($"R="); - builder.Append(RedirectTypeToStatusCode(metadata.RedirectType)); + builder.AppendCommaSeparatedValues("R=") + .Append(RedirectTypeToStatusCode(metadata.RedirectType)); return builder; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index fd301a491ca..284e2b843ec 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Localization; using OrchardCore.Entities; +using OrchardCore.Modules.Extensions; using OrchardCore.UrlRewriting.Models; namespace OrchardCore.UrlRewriting.Services; @@ -52,26 +53,17 @@ private static StringBuilder GetFlags(UrlRewriteSourceMetadata metadata) if (metadata.IgnoreCase) { - builder.Append("NC"); + builder.AppendCommaSeparatedValues("NC"); }; if (metadata.AppendQueryString) { - if (builder.Length > 0) - { - builder.Append(','); - } - builder.Append("QSA"); + builder.AppendCommaSeparatedValues("QSA"); } if (metadata.SkipFurtherRules) { - if (builder.Length > 0) - { - builder.Append(','); - } - - builder.Append('L'); + builder.AppendCommaSeparatedValues('L'); } return builder; From d688d18428bd2745b8b5e7d1a287811ba0e4b9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Tue, 8 Oct 2024 18:45:50 +0200 Subject: [PATCH 34/71] Fix names Queries -> Rules --- .../OrchardCore.UrlRewriting/Controllers/AdminController.cs | 2 +- .../IRewriteRulesManager.cs | 2 +- .../Services/RewriteRulesManager.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 80a436f0dda..03780871264 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -76,7 +76,7 @@ public async Task Index( routeData.Values.TryAdd(_optionsSearch, options.Search); } - var result = await _rewriteRulesManager.PageQueriesAsync(pager.Page, pager.PageSize, new RewriteRulesQueryContext() + var result = await _rewriteRulesManager.PageRulesAsync(pager.Page, pager.PageSize, new RewriteRulesQueryContext() { Name = options.Search, Sorted = true, diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs index 392cdcf8ad8..90e88e41c7e 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs @@ -11,7 +11,7 @@ public interface IRewriteRulesManager Task NewAsync(string source, JsonNode data = null); - Task PageQueriesAsync(int page, int pageSize, RewriteRulesQueryContext context = null); + Task PageRulesAsync(int page, int pageSize, RewriteRulesQueryContext context = null); Task SaveAsync(RewriteRule rule); } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 5877ac9d228..4b64dada6a0 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -85,9 +85,9 @@ public async Task NewAsync(string source, JsonNode data = null) return rule; } - public async Task PageQueriesAsync(int page, int pageSize, RewriteRulesQueryContext context = null) + public async Task PageRulesAsync(int page, int pageSize, RewriteRulesQueryContext context = null) { - var records = await LocateQueriesAsync(context); + var records = await LocateRulesAsync(context); var skip = (page - 1) * pageSize; @@ -123,7 +123,7 @@ private Task LoadAsync(RewriteRule rule) return _rewriteRuleHandlers.InvokeAsync((handler, context) => handler.LoadedAsync(context), loadedContext, _logger); } - private async Task> LocateQueriesAsync(RewriteRulesQueryContext context) + private async Task> LocateRulesAsync(RewriteRulesQueryContext context) { var rules = await _store.GetAllAsync(); From 0bd925b215b22ccf804b5e3ac2a947a922755d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Tue, 8 Oct 2024 19:03:00 +0200 Subject: [PATCH 35/71] Autogen initial rule order --- .../Services/RewriteRulesManager.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 4b64dada6a0..81d4b256e40 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -61,10 +61,13 @@ public async Task NewAsync(string source, JsonNode data = null) return null; } + var order = await GenerateNextAvaliableOrderNumber(); + var rule = new RewriteRule() { Id = IdGenerator.GenerateId(), Source = source, + Order = order }; var initializingContext = new InitializingRewriteRuleContext(rule); @@ -81,6 +84,7 @@ public async Task NewAsync(string source, JsonNode data = null) // Set the source again after calling handlers to prevent handlers from updating the source during initialization. rule.Source = source; rule.Id ??= IdGenerator.GenerateId(); + rule.Order = order; return rule; } @@ -150,4 +154,11 @@ private async Task> LocateRulesAsync(RewriteRulesQueryC return rules; } + + private async Task GenerateNextAvaliableOrderNumber() + { + var rules = await LocateRulesAsync(new RewriteRulesQueryContext() { Sorted = true }); + + return rules.LastOrDefault()?.Order + 1 ?? 0; + } } From f6d3b70526d3b689232518f22f15e644f4cd3cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Tue, 8 Oct 2024 23:05:43 +0200 Subject: [PATCH 36/71] Fix names --- .../Drivers/RewriteRulesDisplayDriver.cs | 3 +-- .../OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs | 2 +- src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs | 1 - .../Views/RewriteRule.Fields.Buttons.cshtml | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs index b3aa4863430..e390c010593 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs @@ -2,11 +2,10 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -using OrchardCore.UrlRewriting; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.ViewModels; -namespace OrchardCore.Queries.Drivers; +namespace OrchardCore.UrlRewriting.Drivers; public sealed class RewriteRulesDisplayDriver : DisplayDriver { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs index 26f5ef6f32d..32e0b706a92 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Recipes/UrlRewritingStep.cs @@ -44,7 +44,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context) if (string.IsNullOrEmpty(name)) { - context.Errors.Add(S["Rule name is missing or empty. The query will not be imported."]); + context.Errors.Add(S["Rule name is missing or empty. The rule will not be imported."]); continue; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index c8878a50c48..e40d63450f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -6,7 +6,6 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.Modules; using OrchardCore.Navigation; -using OrchardCore.Queries.Drivers; using OrchardCore.Recipes; using OrchardCore.Security.Permissions; using OrchardCore.UrlRewriting.Drivers; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml index 65de1b7c884..0f3180b9bf7 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml @@ -10,5 +10,5 @@ role="button" asp-route-action="Index" asp-route-controller="Admin" - asp-route-area="OrchardCore.Queries">@T["Cancel"] + asp-route-area="OrchardCore.UrlRewriting">@T["Cancel"]
    From dda221c9afa844318a9a7c5f7628b132cb1e48cf Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Tue, 8 Oct 2024 15:20:36 -0700 Subject: [PATCH 37/71] remove buttons shape --- .../Drivers/RewriteRulesDisplayDriver.cs | 8 +++----- .../Views/Admin/Create.cshtml | 6 ++++++ .../Views/Admin/Edit.cshtml | 6 ++++++ .../Views/RewriteRule.Fields.Buttons.cshtml | 14 -------------- 4 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs index b3aa4863430..e43e590d47e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs @@ -2,7 +2,6 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -using OrchardCore.UrlRewriting; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.ViewModels; @@ -27,17 +26,16 @@ public override Task DisplayAsync(RewriteRule rule, BuildDisplay ); } - public override Task EditAsync(RewriteRule rule, BuildEditorContext context) + public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) { - return CombineAsync( + return Combine( Initialize("RewriteRule_Fields_Edit", model => { model.Name = rule.Name; model.Source = rule.Source; model.Order = rule.Order; model.Rule = rule; - }).Location("Content:1"), - View("RewriteRule_Fields_Buttons", rule).Location("Actions:5") + }).Location("Content:1") ); } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml index d5a57692472..5a57a469150 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml @@ -7,4 +7,10 @@ @await DisplayAsync(Model) + + @T["Cancel"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml index 5cc4caf7f45..9ea7b8624c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml @@ -7,4 +7,10 @@ @await DisplayAsync(Model) + + @T["Cancel"] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml deleted file mode 100644 index 65de1b7c884..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Buttons.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@using OrchardCore.DisplayManagement.Views -@using OrchardCore.UrlRewriting.Models - -@model ShapeViewModel - -
    - - - @T["Cancel"] -
    From 2faad49bc5e623e849ac6a215725186654833eb5 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Tue, 8 Oct 2024 15:52:17 -0700 Subject: [PATCH 38/71] handle bulk remove abd cleanup --- .../Controllers/AdminController.cs | 46 +++++++++++++++++-- .../Drivers/RewriteRulesDisplayDriver.cs | 28 ++++++----- .../ViewModels/EditRewriteRuleViewModel.cs | 6 --- .../Views/Admin/Index.cshtml | 16 +++---- .../IRewriteRulesManager.cs | 2 +- .../Services/RewriteRulesManager.cs | 17 +++---- 6 files changed, 76 insertions(+), 39 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 03780871264..ac16d69f8ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -40,9 +40,10 @@ public AdminController( IShellReleaseManager shellReleaseManager, IEnumerable urlRewritingRuleSources, IRewriteRulesManager rewriteRulesManager, + IUpdateModelAccessor updateModelAccessor, IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - IUpdateModelAccessor updateModelAccessor) + IHtmlLocalizer htmlLocalizer + ) { _rewriteRuleDisplayManager = rewriteRuleDisplayManager; _authorizationService = authorizationService; @@ -76,7 +77,7 @@ public async Task Index( routeData.Values.TryAdd(_optionsSearch, options.Search); } - var result = await _rewriteRulesManager.PageRulesAsync(pager.Page, pager.PageSize, new RewriteRulesQueryContext() + var result = await _rewriteRulesManager.PageAsync(pager.Page, pager.PageSize, new RewriteRulesQueryContext() { Name = options.Search, Sorted = true, @@ -231,4 +232,43 @@ public async Task Delete(string id) return RedirectToAction(nameof(Index)); } + + + [HttpPost] + [ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(RewriteRuleOptions options, IEnumerable ruleIds) + { + if (!await _authorizationService.AuthorizeAsync(User, UrlRewritingPermissions.ManageUrlRewriting)) + { + return Forbid(); + } + + if (ruleIds?.Count() > 0) + { + switch (options.BulkAction) + { + case RewriteRuleAction.None: + break; + case RewriteRuleAction.Remove: + foreach (var id in ruleIds) + { + var rule = await _rewriteRulesManager.FindByIdAsync(id); + + if (rule == null) + { + continue; + } + + await _rewriteRulesManager.DeleteAsync(rule); + } + await _notifier.SuccessAsync(H["Rules removed successfully."]); + break; + default: + return BadRequest(); + } + } + + return RedirectToAction(nameof(Index)); + } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs index 81f239365f4..e84cda27141 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/RewriteRulesDisplayDriver.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Localization; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; +using OrchardCore.Environment.Shell; using OrchardCore.Mvc.ModelBinding; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.ViewModels; @@ -10,10 +11,14 @@ namespace OrchardCore.UrlRewriting.Drivers; public sealed class RewriteRulesDisplayDriver : DisplayDriver { internal readonly IStringLocalizer S; + private readonly IShellReleaseManager _shellReleaseManager; - public RewriteRulesDisplayDriver(IStringLocalizer stringLocalizer) + public RewriteRulesDisplayDriver( + IStringLocalizer stringLocalizer, + IShellReleaseManager shellReleaseManager) { S = stringLocalizer; + _shellReleaseManager = shellReleaseManager; } public override Task DisplayAsync(RewriteRule rule, BuildDisplayContext context) @@ -28,15 +33,14 @@ public override Task DisplayAsync(RewriteRule rule, BuildDisplay public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context) { - return Combine( - Initialize("RewriteRule_Fields_Edit", model => - { - model.Name = rule.Name; - model.Source = rule.Source; - model.Order = rule.Order; - model.Rule = rule; - }).Location("Content:1") - ); + context.AddTenantReloadWarningWrapper(); + + return Initialize("RewriteRule_Fields_Edit", model => + { + model.Name = rule.Name; + model.Source = rule.Source; + model.Order = rule.Order; + }).Location("Content:1"); } public override async Task UpdateAsync(RewriteRule rule, UpdateEditorContext context) @@ -51,6 +55,8 @@ await context.Updater.TryUpdateModelAsync(rule, Prefix, context.Updater.ModelState.AddModelError(Prefix, nameof(rule.Name), S["Name is required"]); } - return await EditAsync(rule, context); + _shellReleaseManager.RequestRelease(); + + return Edit(rule, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs index ffee6e9d891..61c5745ef42 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/EditRewriteRuleViewModel.cs @@ -1,6 +1,3 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; -using OrchardCore.UrlRewriting.Models; - namespace OrchardCore.UrlRewriting.ViewModels; public class EditRewriteRuleViewModel @@ -10,7 +7,4 @@ public class EditRewriteRuleViewModel public string Source { get; set; } public int Order { get; set; } - - [BindNever] - public RewriteRule Rule { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 41b787f7584..0e9f9c887bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -62,8 +62,8 @@ {
  • - - + +
    @await DisplayAsync(entry.Shape)
  • @@ -125,10 +125,10 @@ var filters = $(".filter"); var selectAllCtrl = $("#select-all"); var selectedItems = $("#selected-items"); - var itemsCheckboxes = $(":checkbox[name='itemIds']"); + var itemsCheckboxes = $(":checkbox[name='ruleIds']"); function displayActionsOrFilters() { - if ($(":checkbox[name='itemIds']:checked").length > 1) { + if ($(":checkbox[name='ruleIds']:checked").length > 1) { actions.show(); filters.hide(); selectedItems.show(); @@ -145,7 +145,7 @@ $(".dropdown-menu .dropdown-item").filter(function () { return $(this).data("action"); }).on("click", function () { - if ($(":checkbox[name='itemIds']:checked").length > 1) { + if ($(":checkbox[name='ruleIds']:checked").length > 1) { var $this = $(this); confirmDialog({ ...$this.data(), callback: function (r) { @@ -160,13 +160,13 @@ selectAllCtrl.click(function () { itemsCheckboxes.not(this).prop("checked", this.checked); - selectedItems.text($(":checkbox[name='itemIds']:checked").length + ' @T["selected"]'); + selectedItems.text($(":checkbox[name='ruleIds']:checked").length + ' @T["selected"]'); displayActionsOrFilters(); }); itemsCheckboxes.on("click", function () { - var itemsCount = $(":checkbox[name='itemIds']").length; - var selectedItemsCount = $(":checkbox[name='itemIds']:checked").length; + var itemsCount = $(":checkbox[name='ruleIds']").length; + var selectedItemsCount = $(":checkbox[name='ruleIds']:checked").length; selectAllCtrl.prop("checked", selectedItemsCount == itemsCount); selectAllCtrl.prop("indeterminate", selectedItemsCount > 0 && selectedItemsCount < itemsCount); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs index 90e88e41c7e..3c0d52e1e3f 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs @@ -11,7 +11,7 @@ public interface IRewriteRulesManager Task NewAsync(string source, JsonNode data = null); - Task PageRulesAsync(int page, int pageSize, RewriteRulesQueryContext context = null); + Task PageAsync(int page, int pageSize, RewriteRulesQueryContext context = null); Task SaveAsync(RewriteRule rule); } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 81d4b256e40..087a106bdcd 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -40,7 +40,7 @@ public async Task FindByIdAsync(string id) { var rule = await _store.FindByIdAsync(id); - if (rule == null) + if (rule != null) { await LoadAsync(rule); } @@ -61,13 +61,11 @@ public async Task NewAsync(string source, JsonNode data = null) return null; } - var order = await GenerateNextAvaliableOrderNumber(); - var rule = new RewriteRule() { Id = IdGenerator.GenerateId(), Source = source, - Order = order + Order = await GetNextOrderSequence() }; var initializingContext = new InitializingRewriteRuleContext(rule); @@ -84,12 +82,11 @@ public async Task NewAsync(string source, JsonNode data = null) // Set the source again after calling handlers to prevent handlers from updating the source during initialization. rule.Source = source; rule.Id ??= IdGenerator.GenerateId(); - rule.Order = order; return rule; } - public async Task PageRulesAsync(int page, int pageSize, RewriteRulesQueryContext context = null) + public async Task PageAsync(int page, int pageSize, RewriteRulesQueryContext context = null) { var records = await LocateRulesAsync(context); @@ -98,7 +95,7 @@ public async Task PageRulesAsync(int page, int pageSize, var result = new ListRewriteRuleResult { Count = records.Count(), - Records = records.Skip(skip).Take(pageSize).ToArray() + Records = records.Skip(skip).Take(pageSize), }; foreach (var record in result.Records) @@ -155,10 +152,10 @@ private async Task> LocateRulesAsync(RewriteRulesQueryC return rules; } - private async Task GenerateNextAvaliableOrderNumber() + private async Task GetNextOrderSequence() { - var rules = await LocateRulesAsync(new RewriteRulesQueryContext() { Sorted = true }); + var rules = await _store.GetAllAsync(); - return rules.LastOrDefault()?.Order + 1 ?? 0; + return rules.Max(x => x.Order) + 1; } } From 80cfe0a6d625ebab67f0cb47918c5a5d0b127f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Wed, 9 Oct 2024 23:13:06 +0200 Subject: [PATCH 39/71] Change 'Skip Further Rules' to 'Stop processing of subsequent rules' --- .../OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml index 39cfbd37e3c..3585e5a6370 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml @@ -29,6 +29,6 @@
    - +
    \ No newline at end of file From 4c81d4414026dcb89da1b615bf483b1e47e99cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Fri, 11 Oct 2024 18:45:08 +0200 Subject: [PATCH 40/71] Rename Url to SubstitutionUrl --- .../Drivers/UrlRedirectRuleDisplayDriver.cs | 10 +++++----- .../Drivers/UrlRewriteRuleDisplayDriver.cs | 10 +++++----- .../ViewModels/UrlRedirectRuleViewModel.cs | 2 +- .../ViewModels/UrlRewriteRuleViewModel.cs | 2 +- .../Views/UrlRedirectRule.Edit.cshtml | 4 ++-- .../Views/UrlRewriteRule.Edit.cshtml | 4 ++-- .../Models/UrlRewriteSourceMetadata.cs | 2 +- .../Services/UrlRewriteRuleSource.cs | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs index c74dfb2a761..5e9284d00f9 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRedirectRuleDisplayDriver.cs @@ -28,7 +28,7 @@ public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context return Initialize("UrlRedirectRule_Edit", model => { var metadata = rule.As(); - model.Url = metadata.Url; + model.SubstitutionUrl = metadata.Url; model.AppendQueryString = context.IsNew || metadata.AppendQueryString; model.Pattern = metadata.Pattern; model.IgnoreCase = metadata.IgnoreCase; @@ -55,7 +55,7 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.Pattern, m => m.IgnoreCase, - m => m.Url, + m => m.SubstitutionUrl, m => m.AppendQueryString, m => m.RedirectType); @@ -64,16 +64,16 @@ await context.Updater.TryUpdateModelAsync(model, Prefix, context.Updater.ModelState.AddModelError(Prefix, nameof(model.Pattern), "The url match pattern is required."); } - if (string.IsNullOrWhiteSpace(model.Url)) + if (string.IsNullOrWhiteSpace(model.SubstitutionUrl)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Url), S["The redirect URL is required"]); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.SubstitutionUrl), S["The redirect URL is required"]); } rule.Put(new UrlRedirectSourceMetadata() { Pattern = model.Pattern, IgnoreCase = model.IgnoreCase, - Url = model.Url, + Url = model.SubstitutionUrl, AppendQueryString = model.AppendQueryString, RedirectType = model.RedirectType, }); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs index cc8d21fbe6b..88205962825 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Drivers/UrlRewriteRuleDisplayDriver.cs @@ -28,7 +28,7 @@ public override IDisplayResult Edit(RewriteRule rule, BuildEditorContext context return Initialize("UrlRewriteRule_Edit", model => { var metadata = rule.As(); - model.Url = metadata.Url; + model.SubstitutionUrl = metadata.SubstitutionUrl; model.Pattern = metadata.Pattern; model.IgnoreCase = metadata.IgnoreCase; model.AppendQueryString = context.IsNew || metadata.AppendQueryString; @@ -45,7 +45,7 @@ public override async Task UpdateAsync(RewriteRule rule, UpdateE var model = new UrlRewriteRuleViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Url, + m => m.SubstitutionUrl, m => m.AppendQueryString, m => m.Pattern, m => m.IgnoreCase, @@ -56,16 +56,16 @@ await context.Updater.TryUpdateModelAsync(model, Prefix, context.Updater.ModelState.AddModelError(Prefix, nameof(model.Pattern), "The url match pattern is required."); } - if (string.IsNullOrWhiteSpace(model.Url)) + if (string.IsNullOrWhiteSpace(model.SubstitutionUrl)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Url), S["The rewrite URL is required"]); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.SubstitutionUrl), S["The rewrite URL is required"]); } rule.Put(new UrlRewriteSourceMetadata() { Pattern = model.Pattern, IgnoreCase = model.IgnoreCase, - Url = model.Url, + SubstitutionUrl = model.SubstitutionUrl, AppendQueryString = model.AppendQueryString, SkipFurtherRules = model.SkipFurtherRules }); diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs index 13aac2a25d5..6f5945a67a1 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRedirectRuleViewModel.cs @@ -6,7 +6,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; public class UrlRedirectRuleViewModel { - public string Url { get; set; } + public string SubstitutionUrl { get; set; } public bool AppendQueryString { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs index 2c521873df6..48375bd6bf2 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/UrlRewriteRuleViewModel.cs @@ -2,7 +2,7 @@ namespace OrchardCore.UrlRewriting.ViewModels; public class UrlRewriteRuleViewModel { - public string Url { get; set; } + public string SubstitutionUrl { get; set; } public bool AppendQueryString { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml index 1e3651153c1..538351e26d1 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml @@ -15,8 +15,8 @@
    - - + +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml index 3585e5a6370..24a828e47ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml @@ -15,8 +15,8 @@
    - - + +
    diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs index 7423f807ece..af9c08f927f 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs @@ -6,7 +6,7 @@ public class UrlRewriteSourceMetadata public bool IgnoreCase { get; set; } - public string Url { get; set; } + public string SubstitutionUrl { get; set; } public bool AppendQueryString { get; set; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index 284e2b843ec..e69f38a6a66 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -41,10 +41,10 @@ private static string GetRewriteRule(UrlRewriteSourceMetadata metadata) if (flags.Length > 0) { - return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\" [{flags}]"; + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.SubstitutionUrl}\" [{flags}]"; } - return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.Url}\""; + return $"RewriteRule \"{metadata.Pattern}\" \"{metadata.SubstitutionUrl}\""; } private static StringBuilder GetFlags(UrlRewriteSourceMetadata metadata) From 6a5f621d3ee82ac3e3474be7ab55cc4b8afa5853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20W=C3=B3jcik?= Date: Fri, 11 Oct 2024 18:54:28 +0200 Subject: [PATCH 41/71] Fix exception in Max fun if there is no rules --- .../Services/RewriteRulesManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 087a106bdcd..460e6427a4c 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -156,6 +156,6 @@ private async Task GetNextOrderSequence() { var rules = await _store.GetAllAsync(); - return rules.Max(x => x.Order) + 1; + return rules.Any() ? rules.Max(x => x.Order) + 1 : 0; } } From 396c05dcec01fbe1e63b09c079c2b9fd14be608d Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sun, 13 Oct 2024 09:59:14 -0700 Subject: [PATCH 42/71] minor change --- .../Services/UrlRedirectRuleSource.cs | 2 +- .../Services/UrlRewriteRuleSource.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index 5937ccd4b42..b286c30f56c 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -17,7 +17,7 @@ public UrlRedirectRuleSource(IStringLocalizer stringLocal { S = stringLocalizer; - Description = S["Redirect Rule"]; + Description = S["URL Redirect Rule"]; } public string Name => SourceName; diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index e69f38a6a66..9ac0ae1f990 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -17,7 +17,7 @@ public UrlRewriteRuleSource(IStringLocalizer stringLocaliz { S = stringLocalizer; - Description = S["Rewrite Rule"]; + Description = S["URL Rewrite Rule"]; } public string Name => SourceName; From 7378ffee48e05e0c97a91dc51bf6f8b85c8c41da Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sun, 13 Oct 2024 11:41:05 -0700 Subject: [PATCH 43/71] improve logic --- .../Views/Admin/Index.cshtml | 2 +- .../Views/UrlRewriteRule.Edit.cshtml | 2 +- .../Views/_ViewImports.cshtml | 1 + .../Services/UrlRedirectRuleSource.cs | 61 +++++++++++-------- .../Services/UrlRewriteRuleSource.cs | 6 +- 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 0e9f9c887bd..a7c39b30d9d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -118,7 +118,7 @@
    - - function displayActionsOrFilters() { - if ($(":checkbox[name='ruleIds']:checked").length > 1) { - actions.show(); - filters.hide(); - selectedItems.show(); - items.hide(); + diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml index d4d366d3237..071451bff6e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml @@ -12,10 +12,6 @@ - - @Model.Value.Order - - @if (!string.IsNullOrEmpty(Model.Value.Author)) { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml index 8a0f5a9146c..ff71e37eb44 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Fields.Edit.cshtml @@ -6,11 +6,3 @@ @T["A display name for the rule."]
    - -
    - - - - @T["The execution order number of this rule."] - -
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.js b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.js new file mode 100644 index 00000000000..3f18be2dce0 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.js @@ -0,0 +1,51 @@ +/* +** NOTE: This file is generated by Gulp and should not be edited directly! +** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp. +*/ + +sortingListManager = function () { + var saveOrders = function saveOrders(evt, url, errorMessage) { + var data = { + oldIndex: evt.oldIndex, + newIndex: evt.newIndex + }; + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + })["catch"](function (error) { + console.log(error); + alert(errorMessage || 'Unable to sort the list'); + }); + }; + var create = function create(selector, sortUrl, errorMessage) { + var sortable = document.querySelector(selector); + if (!sortable) { + console.log('Unable to find the sortable element. The given selector is: ' + selector); + return; + } + if (sortUrl) { + orderUrl = sortUrl; + } else { + orderUrl = sortable.getAttribute('data-sort-uri'); + } + if (!orderUrl) { + console.log('Unable to determine the sort post URI. Either pass it to the create function or set it as data-sort-uri to the sorting element.'); + return; + } + var sortable = Sortable.create(sortable, { + handle: ".ui-sortable-handle", + animation: 150, + filter: ".ignore-elements", + draggable: ".item", + onUpdate: function onUpdate(evt) { + saveOrders(evt, orderUrl, errorMessage); + } + }); + }; + return { + create: create + }; +}(); \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.min.js b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.min.js new file mode 100644 index 00000000000..d297c9bf52c --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/sortable-rules.min.js @@ -0,0 +1 @@ +sortingListManager={create:function(e,t,o){if(n=document.querySelector(e))if(orderUrl=t||n.getAttribute("data-sort-uri"),orderUrl)var n=Sortable.create(n,{handle:".ui-sortable-handle",animation:150,filter:".ignore-elements",draggable:".item",onUpdate:function(e){!function(e,t,o){var n={oldIndex:e.oldIndex,newIndex:e.newIndex};fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}).catch((function(e){console.log(e),alert(o||"Unable to sort the list")}))}(e,orderUrl,o)}});else console.log("Unable to determine the sort post URI. Either pass it to the create function or set it as data-sort-uri to the sorting element.");else console.log("Unable to find the sortable element. The given selector is: "+e)}}; diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs index e94fcb80282..10dc87a7c3a 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/IRewriteRulesManager.cs @@ -18,4 +18,8 @@ public interface IRewriteRulesManager Task DeleteAsync(RewriteRule rule); Task UpdateAsync(RewriteRule rule, JsonNode data = null); + + Task ResortOrderAsync(int value1, int value2); + + Task GetAllAsync(); } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index e67f8d07129..a616d7c46c8 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -118,6 +118,27 @@ public async Task PageAsync(int page, int pageSize, Rewri return result; } + public async Task GetAllAsync() + { + var records = await LocateRulesAsync(new RewriteRulesQueryContext + { + Sorted = true, + }); + + var result = new ListRewriteRuleResult + { + Count = records.Count(), + Records = records, + }; + + foreach (var record in result.Records) + { + await LoadAsync(record); + } + + return result; + } + public async Task UpdateAsync(RewriteRule rule, JsonNode data = null) { var updatingContext = new UpdatingRewriteRuleContext(rule, data); @@ -138,6 +159,48 @@ public async Task SaveAsync(RewriteRule rule) await _rewriteRuleHandlers.InvokeAsync((handler, ctx) => handler.SavedAsync(ctx), savedContext, _logger); } + public async Task ResortOrderAsync(int oldOrder, int newOrder) + { + var rules = await _store.GetAllAsync(); + + var ruleToMove = rules.FirstOrDefault(x => x.Order == oldOrder); + + if (ruleToMove == null) + { + return; + } + + var ruleToShift = rules.FirstOrDefault(x => x.Order == newOrder); + + if (ruleToShift == null) + { + ruleToMove.Order = newOrder; + + await _store.SaveAsync(ruleToMove); + } + else + { + var shift = false; + + foreach (var rule in rules) + { + if (!shift) + { + if (rule.Id != ruleToShift.Id) + { + continue; + } + + shift = true; + } + + rule.Order = oldOrder++; + + await _store.SaveAsync(rule); + } + } + } + private Task LoadAsync(RewriteRule rule) { var loadedContext = new LoadedRewriteRuleContext(rule); diff --git a/src/docs/reference/modules/UrlRewriting/README.md b/src/docs/reference/modules/UrlRewriting/README.md index 538eadba095..f5c184cacde 100644 --- a/src/docs/reference/modules/UrlRewriting/README.md +++ b/src/docs/reference/modules/UrlRewriting/README.md @@ -48,7 +48,6 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. { "Source": "Redirect", "Name": "Redirect about-us to about", - "Order": 1, "Pattern": "^/about-us$", "SubstitutionPattern": "/about", "IsCaseInsensitive": true, @@ -58,7 +57,6 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. { "Source": "Rewrite", "Name": "Serve media URLs from img", - "Order": 2, "Pattern": "^/img/(.*)$", "SubstitutionPattern": "/media/$1", "IsCaseInsensitive": true, @@ -77,7 +75,6 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. - **Id**: A unique identifier for the redirect rule. If the specified Id matches an existing rule, that rule will be updated with the provided properties. To create a new rule, leave the Id property empty or specify a unique value that does not match any existing rule. - **Name**: A descriptive name for the rule (e.g., `"Redirect about-us to about"`). -- **Order**: A execution sequence number for this rule. - **Pattern**: The URL pattern to match (e.g., `^/about-us$` for an exact match). - **SubstitutionPattern**: The target URL to redirect to (e.g., `/about`). - **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. @@ -88,7 +85,6 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. - **Id**: A unique identifier for the rewrite rule. Similar to redirect rules, if the specified Id matches an existing rule, that rule will be updated. Leave the Id empty to create a new rule. - **Name**: A descriptive name for the rule (e.g., `"Serve media URLs from img"`). -- **Order**: A execution sequence number for this rule. - **Pattern**: The URL pattern to match (e.g., `^/img/(.*)$` to match any URL starting with `/img/`). - **SubstitutionPattern**: The target URL for the rewrite (e.g., `/media/$1`, where `$1` captures the matched portion of the original URL). - **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. From 0ed0b77b24e8958011f69bddd28389afb95bfbe6 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Wed, 16 Oct 2024 17:22:31 -0700 Subject: [PATCH 64/71] save --- .../Controllers/AdminController.cs | 4 +- .../Endpoints/Rules/SortRulesEndpoint.cs | 2 +- .../Views/Admin/Index.cshtml | 2 +- .../IRewriteRulesManager.cs | 6 +- .../IRewriteRulesStore.cs | 2 + .../Models/ListRewriteRuleResult.cs | 8 -- .../Models/RewriteRulesQueryContext.cs | 10 -- .../Handlers/RewriteRuleHandler.cs | 7 -- .../Services/RewriteRulesManager.cs | 102 +++--------------- .../Services/RewriteRulesStore.cs | 35 +++++- 10 files changed, 55 insertions(+), 123 deletions(-) delete mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/ListRewriteRuleResult.cs delete mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteRulesQueryContext.cs diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 87f04920292..53f8aaf7041 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -60,7 +60,7 @@ public async Task Index(RewriteRuleOptions options) return Forbid(); } - var result = await _rewriteRulesManager.GetAllAsync(); + var rules = await _rewriteRulesManager.GetAllAsync(); var model = new ListRewriteRuleViewModel { @@ -69,7 +69,7 @@ public async Task Index(RewriteRuleOptions options) SourceNames = _urlRewritingRuleSources.Select(x => x.Name), }; - foreach (var rule in result.Records) + foreach (var rule in rules) { model.Rules.Add(new RewriteRuleEntry { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Endpoints/Rules/SortRulesEndpoint.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Endpoints/Rules/SortRulesEndpoint.cs index a51bd0cd754..ffcb09ec44e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Endpoints/Rules/SortRulesEndpoint.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Endpoints/Rules/SortRulesEndpoint.cs @@ -41,7 +41,7 @@ private static async Task HandleAsync( return TypedResults.Ok(); } - private class ResortingRequest + private sealed class ResortingRequest { public int? OldIndex { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index 868b8872b8e..45f4c41664f 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -130,7 +130,7 @@ - + - diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml index a96d5a5fa06..064977c6201 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRedirectRule.Edit.cshtml @@ -15,11 +15,8 @@
    -
    - - - @T["When selected, the query string will be appended if it exists."] -
    + +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml index cb1408df5a1..bfd87ae139e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/UrlRewriteRule.Edit.cshtml @@ -15,11 +15,8 @@
    -
    - - - @T["When selected, any value in the query string will be ignored."] -
    + +
    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.js b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.js new file mode 100644 index 00000000000..105fb539bcc --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.js @@ -0,0 +1,159 @@ +/* +** NOTE: This file is generated by Gulp and should not be edited directly! +** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp. +*/ + +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +urlRewritingAdmin = function () { + var initialize = function initialize(url, errorLabel, selectedLabel) { + // Create the sortable UI. + sortingListManager.create('#rewrite-rules-sortable-list', url, errorLabel); + var searchBox = document.getElementById('search-box'); + var searchAlert = document.getElementById('list-alert'); + var filterElements = document.querySelectorAll('[data-filter-value]'); + + // If the user press Enter, don't submit. + searchBox.addEventListener('keydown', function (e) { + if (e.key === 'Enter') { + e.preventDefault(); + } + }); + searchBox.addEventListener('keyup', function (e) { + var search = e.target.value.toLowerCase(); + // On ESC, clear the search box and display all rules. + if (e.key == 'Escape' || search == '') { + searchAlert.classList.add('d-none'); + searchBox.value = ''; + for (var i = 0; i < filterElements.length; i++) { + filterElements[i].classList.remove("d-none"); + filterElements[i].classList.remove("first-child-visible"); + filterElements[i].classList.remove("last-child-visible"); + } + if (filterElements.length > 0) { + filterElements[0].classList.add('first-child-visible'); + filterElements[filterElements.length - 1].classList.add('last-child-visible'); + } + } else { + var visibleElements = []; + for (var _i = 0; _i < filterElements.length; _i++) { + var filter = filterElements[_i]; + var text = filter.getAttribute('data-filter-value'); + if (!text) { + filter.classList.add("d-none"); + continue; + } + var found = text.indexOf(search) > -1; + if (found) { + filter.classList.remove("d-none"); + filter.classList.remove("first-child-visible"); + filter.classList.remove("last-child-visible"); + visibleElements.push(filter); + } else { + filter.classList.add("d-none"); + } + } + if (visibleElements.length > 0) { + visibleElements[0].classList.add('first-child-visible'); + visibleElements[visibleElements.length - 1].classList.add('last-child-visible'); + searchAlert.classList.add('d-none'); + } else { + searchAlert.classList.remove('d-none'); + } + } + }); + var actions = document.getElementById('actions'); + var items = document.getElementById('items'); + var filters = document.querySelectorAll('.filter'); + var selectAllCtrl = document.getElementById('select-all'); + var selectedItems = document.getElementById('selected-items'); + var itemsCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']"); + function displayActionsOrFilters() { + // Select all checked checkboxes with name 'ruleIds' + var checkedCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked"); + if (checkedCheckboxes.length > 1) { + actions.classList.remove('d-none'); + for (var i = 0; i < filters.length; i++) { + filters[i].classList.add('d-none'); + } + selectedItems.classList.remove('d-none'); + items.classList.add('d-none'); + } else { + actions.classList.add('d-none'); + for (var _i2 = 0; _i2 < filters.length; _i2++) { + filters[_i2].classList.remove('d-none'); + } + selectedItems.classList.add('d-none'); + items.classList.remove('d-none'); + } + } + var dropdownItems = document.querySelectorAll(".dropdown-menu .dropdown-item"); + + // Add click event listeners to each dropdown item + dropdownItems.forEach(function (item) { + // Check if the item has a data-action attribute + if (item.dataset.action) { + item.addEventListener("click", function () { + // Get all checked checkboxes + var checkedCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked"); + + // Check if more than one checkbox is checked + if (checkedCheckboxes.length > 1) { + // Get data attributes from the clicked item + var actionData = Object.assign({}, item.dataset); + confirmDialog(_objectSpread(_objectSpread({}, actionData), {}, { + callback: function callback(r) { + if (r) { + // Set the value of the BulkAction option + document.querySelector("[name='Options.BulkAction']").value = actionData.action; + // Trigger the submit action + document.querySelector("[name='submit.BulkAction']").click(); + } + } + })); + } + }); + } + }); + selectAllCtrl.addEventListener("click", function () { + itemsCheckboxes.forEach(function (checkbox) { + if (checkbox !== selectAllCtrl) { + checkbox.checked = selectAllCtrl.checked; // Set the checked state of all checkboxes + } + }); + + // Update the selected items text + updateSelectedItemsText(); + displayActionsOrFilters(); + }); + + // Event listener for individual checkboxes + itemsCheckboxes.forEach(function (checkbox) { + checkbox.addEventListener("click", function () { + var itemsCount = itemsCheckboxes.length; + var selectedItemsCount = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length; + + // Update selectAllCtrl state + selectAllCtrl.checked = selectedItemsCount === itemsCount; + selectAllCtrl.indeterminate = selectedItemsCount > 0 && selectedItemsCount < itemsCount; + + // Update the selected items text + updateSelectedItemsText(); + displayActionsOrFilters(); + }); + }); + + // Function to update selected items text + function updateSelectedItemsText() { + var selectedCount = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length; + selectedItems.textContent = selectedCount + ' ' + selectedLabel; + } + }; + return { + initialize: initialize + }; +}(); \ No newline at end of file diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.min.js b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.min.js new file mode 100644 index 00000000000..0df68759c32 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/wwwroot/Scripts/admin-ui.min.js @@ -0,0 +1 @@ +function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}function ownKeys(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function _objectSpread(e){for(var t=1;t0&&(i[0].classList.add("first-child-visible"),i[i.length-1].classList.add("last-child-visible"))}else{for(var c=[],l=0;l-1?(s.classList.remove("d-none"),s.classList.remove("first-child-visible"),s.classList.remove("last-child-visible"),c.push(s)):s.classList.add("d-none")}c.length>0?(c[0].classList.add("first-child-visible"),c[c.length-1].classList.add("last-child-visible"),o.classList.add("d-none")):o.classList.remove("d-none")}}));var c=document.getElementById("actions"),l=document.getElementById("items"),s=document.querySelectorAll(".filter"),a=document.getElementById("select-all"),d=document.getElementById("selected-items"),u=document.querySelectorAll("input[type='checkbox'][name='ruleIds']");function f(){if(document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length>1){c.classList.remove("d-none");for(var e=0;e1){var t=Object.assign({},e.dataset);confirmDialog(_objectSpread(_objectSpread({},t),{},{callback:function(e){e&&(document.querySelector("[name='Options.BulkAction']").value=t.action,document.querySelector("[name='submit.BulkAction']").click())}}))}}))})),a.addEventListener("click",(function(){u.forEach((function(e){e!==a&&(e.checked=a.checked)})),m(),f()})),u.forEach((function(e){e.addEventListener("click",(function(){var e=u.length,t=document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length;a.checked=t===e,a.indeterminate=t>0&&t(); + var queryStringPolicy = data[nameof(UrlRewriteSourceMetadata.QueryStringPolicy)]?.GetEnumValue(); - if (appendQueryString.HasValue) + if (queryStringPolicy.HasValue) { - metadata.AppendQueryString = appendQueryString.Value; + metadata.QueryStringPolicy = queryStringPolicy.Value; } var redirectType = data[nameof(UrlRedirectSourceMetadata.RedirectType)]?.GetEnumValue(); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Handlers/UrlRewriteRuleHandler.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Handlers/UrlRewriteRuleHandler.cs index c1fa7fc8be0..227981713ad 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Handlers/UrlRewriteRuleHandler.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Handlers/UrlRewriteRuleHandler.cs @@ -82,11 +82,11 @@ private static Task PopulateAsync(RewriteRule rule, JsonNode data) metadata.IsCaseInsensitive = ignoreCase.Value; } - var appendQueryString = data[nameof(UrlRewriteSourceMetadata.IgnoreQueryString)]?.GetValue(); + var queryStringPolicy = data[nameof(UrlRewriteSourceMetadata.QueryStringPolicy)]?.GetEnumValue(); - if (appendQueryString.HasValue) + if (queryStringPolicy.HasValue) { - metadata.IgnoreQueryString = appendQueryString.Value; + metadata.QueryStringPolicy = queryStringPolicy.Value; } var skipFurtherRules = data[nameof(UrlRewriteSourceMetadata.SkipFurtherRules)]?.GetValue(); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/QueryStringPolicy.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/QueryStringPolicy.cs new file mode 100644 index 00000000000..51287bf18d7 --- /dev/null +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/QueryStringPolicy.cs @@ -0,0 +1,7 @@ +namespace OrchardCore.UrlRewriting.Models; + +public enum QueryStringPolicy +{ + Append, + Drop, +} diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RedirectType.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RedirectType.cs index 50eb4816171..5c6bf25cf42 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RedirectType.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/RedirectType.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.UrlRewriting.Models; +namespace OrchardCore.UrlRewriting.Models; public enum RedirectType { diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs index 969ac2b44e2..625b51dbdc9 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs @@ -8,7 +8,7 @@ public class UrlRedirectSourceMetadata public bool IsCaseInsensitive { get; set; } - public bool AppendQueryString { get; set; } + public QueryStringPolicy QueryStringPolicy { get; set; } public RedirectType RedirectType { get; set; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs index ab783eacdf4..e50b96c4187 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs @@ -8,7 +8,7 @@ public class UrlRewriteSourceMetadata public string SubstitutionPattern { get; set; } - public bool IgnoreQueryString { get; set; } + public QueryStringPolicy QueryStringPolicy { get; set; } public bool SkipFurtherRules { get; set; } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index 32fd170b2f0..aa5d729026b 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -144,14 +144,13 @@ public async Task ResortOrderAsync(int oldOrder, int newOrder) return; } - // Adjust newOrder for zero-based index var zeroBasedOldOrder = oldOrder - 1; var zeroBasedNewOrder = newOrder - 1; - // Get the element to move + // Get the element to move. var ruleToMove = rules[zeroBasedOldOrder]; - // Remove the rule from its current position + // Remove the rule from its current position. rules.RemoveAt(zeroBasedOldOrder); rules.Insert(zeroBasedNewOrder, ruleToMove); diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs index ea341cde54c..2dc3da2f86f 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRedirectRuleSource.cs @@ -71,11 +71,11 @@ StringBuilder AppendFlag(StringBuilder builder, string flag) AppendFlag(builder, "NC"); }; - if (metadata.AppendQueryString) + if (metadata.QueryStringPolicy == QueryStringPolicy.Append) { AppendFlag(builder, "QSA"); } - else + else if (metadata.QueryStringPolicy == QueryStringPolicy.Drop) { AppendFlag(builder, "QSD"); } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs index 3c5abab0b28..efea6311026 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/UrlRewriteRuleSource.cs @@ -71,13 +71,13 @@ StringBuilder AppendFlag(StringBuilder builder, string flag) AppendFlag(builder, "NC"); }; - if (metadata.IgnoreQueryString) + if (metadata.QueryStringPolicy == QueryStringPolicy.Append) { - AppendFlag(builder, "QSD"); + AppendFlag(builder, "QSA"); } - else + else if (metadata.QueryStringPolicy == QueryStringPolicy.Drop) { - AppendFlag(builder, "QSA"); + AppendFlag(builder, "QSD"); } if (metadata.SkipFurtherRules) diff --git a/src/docs/reference/modules/UrlRewriting/README.md b/src/docs/reference/modules/UrlRewriting/README.md index f5c184cacde..ec1701ba2da 100644 --- a/src/docs/reference/modules/UrlRewriting/README.md +++ b/src/docs/reference/modules/UrlRewriting/README.md @@ -51,7 +51,7 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. "Pattern": "^/about-us$", "SubstitutionPattern": "/about", "IsCaseInsensitive": true, - "AppendQueryString": false, + "QueryStringPolicy": "Append", "RedirectType": "MovedPermanently" }, { @@ -60,7 +60,7 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. "Pattern": "^/img/(.*)$", "SubstitutionPattern": "/media/$1", "IsCaseInsensitive": true, - "IgnoreQueryString": false, + "QueryStringPolicy": "Drop", "SkipFurtherRules": true } ] @@ -78,7 +78,7 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. - **Pattern**: The URL pattern to match (e.g., `^/about-us$` for an exact match). - **SubstitutionPattern**: The target URL to redirect to (e.g., `/about`). - **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. -- **AppendQueryString**: When `false`, the original query string will not be included in the redirect. +- **QueryStringPolicy**: To append the query string to the new URL, use 'Append'. To ignore the query string during the redirect, select 'Drop'. - **RedirectType**: Specifies the HTTP status code for the redirect. `MovedPermanently` (HTTP 301) indicates a permanent redirect. ### Rewrite Rule Properties @@ -88,5 +88,5 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. - **Pattern**: The URL pattern to match (e.g., `^/img/(.*)$` to match any URL starting with `/img/`). - **SubstitutionPattern**: The target URL for the rewrite (e.g., `/media/$1`, where `$1` captures the matched portion of the original URL). - **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. -- **IgnoreQueryString**: When set to `false`, the query string from the original request will be considered during the rewrite. +- **QueryStringPolicy**: To append the query string to the new URL, use 'Append'. To ignore the query string during the redirect, select 'Drop'. - **SkipFurtherRules**: When set to `true`, subsequent rules will not be processed if this rule matches. From 85a22d2b7ad2b9f5c4eb581f193c32eb0aae463e Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Fri, 18 Oct 2024 10:13:25 -0700 Subject: [PATCH 67/71] cleanup --- .../OrchardCore.UrlRewriting/Manifest.cs | 2 +- .../OrchardCore.UrlRewriting.csproj | 4 +-- .../ResourceManagementOptionsConfiguration.cs | 4 +-- .../UrlRewritingPermissionProvider.cs | 2 +- .../OrchardCore.UrlRewriting/Startup.cs | 25 ++++++++++++------- .../ViewModels/RewriteRuleAction.cs | 7 ++++++ .../ViewModels/RewriteRuleOptions.cs | 6 ----- .../Views/Admin/Create.cshtml | 2 -- .../Views/Admin/Edit.cshtml | 2 -- .../Views/Admin/Index.cshtml | 7 +++--- ...ewriteRule.DefaultMeta.SummaryAdmin.cshtml | 2 +- .../Services/RewriteRulesManager.cs | 2 +- 12 files changed, 35 insertions(+), 30 deletions(-) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/{ => Services}/ResourceManagementOptionsConfiguration.cs (79%) rename src/OrchardCore.Modules/OrchardCore.UrlRewriting/{ => Services}/UrlRewritingPermissionProvider.cs (93%) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleAction.cs diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs index 3feadfb2583..8b6108ab510 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Manifest.cs @@ -5,6 +5,6 @@ Author = ManifestConstants.OrchardCoreTeam, Website = ManifestConstants.OrchardCoreWebsite, Version = ManifestConstants.OrchardCoreVersion, - Description = "Enables URL rewrites and redirects for incoming requests.", + Description = "Enables URL rewriting for incoming requests.", Category = "Infrastructure" )] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj index eff0e2cc180..12a46540458 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/OrchardCore.UrlRewriting.csproj @@ -3,11 +3,11 @@ true - OrchardCore Rewrite + OrchardCore UrlRewriting $(OCCMSDescription) - Provides url rewrite capabilities. + Provides url rewriting capabilities. $(PackageTags) OrchardCoreCMS diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/ResourceManagementOptionsConfiguration.cs similarity index 79% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/ResourceManagementOptionsConfiguration.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/ResourceManagementOptionsConfiguration.cs index 3653fa350b0..f527f8b01ad 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/ResourceManagementOptionsConfiguration.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.UrlRewriting; +namespace OrchardCore.UrlRewriting.Services; -public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions +internal sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { private static readonly ResourceManifest _manifest; diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/UrlRewritingPermissionProvider.cs similarity index 93% rename from src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs rename to src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/UrlRewritingPermissionProvider.cs index 9fe065ac9d2..dc39f950a7f 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/UrlRewritingPermissionProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Services/UrlRewritingPermissionProvider.cs @@ -1,6 +1,6 @@ using OrchardCore.Security.Permissions; -namespace OrchardCore.UrlRewriting; +namespace OrchardCore.UrlRewriting.Services; public sealed class UrlRewritingPermissionProvider : IPermissionProvider { diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 5899c55b2eb..79b3d41da81 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -25,25 +25,23 @@ public override int Order public override void ConfigureServices(IServiceCollection services) { - services.AddSingleton(); services.AddNavigationProvider(); - services.AddScoped, RewriteRulesDisplayDriver>(); - services.AddPermissionProvider(); + services.AddTransient, RewriteOptionsConfiguration>(); - services.AddRecipeExecutionStep(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); + services.AddSingleton(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, RewriteRulesDisplayDriver>(); + // Add Apache Mod Rewrite options. services.AddRewriteRuleSource(UrlRedirectRuleSource.SourceName) + .AddScoped() .AddScoped, UrlRedirectRuleDisplayDriver>(); - services.AddRewriteRuleSource(UrlRewriteRuleSource.SourceName) + .AddScoped() .AddScoped, UrlRewriteRuleDisplayDriver>(); - - services.AddTransient, ResourceManagementOptionsConfiguration>(); } public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) @@ -55,3 +53,12 @@ public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder ro app.UseRewriter(rewriteOptions); } } + +[Feature("OrchardCore.Recipes.Core")] +public sealed class RecipesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddRecipeExecutionStep(); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleAction.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleAction.cs new file mode 100644 index 00000000000..28d2fcb83d6 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleAction.cs @@ -0,0 +1,7 @@ +namespace OrchardCore.UrlRewriting.ViewModels; + +public enum RewriteRuleAction +{ + None, + Remove, +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs index a9cb01bf350..18560a96182 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleOptions.cs @@ -12,9 +12,3 @@ public class RewriteRuleOptions [BindNever] public List BulkActions { get; set; } } - -public enum RewriteRuleAction -{ - None, - Remove, -} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml index 5574c919c21..35770c01f35 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml @@ -1,5 +1,3 @@ -@model dynamic -

    @RenderTitleSegments(T["New rule"])

    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml index 706d1d7182b..bbda39ba90f 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml @@ -1,5 +1,3 @@ -@model dynamic -

    @RenderTitleSegments(T["Edit rule"])

    diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index d1ff0526134..bc764eb468c 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -1,5 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using OrchardCore.UrlRewriting.Endpoints.Rules + @model ListRewriteRuleViewModel

    @RenderTitleSegments(T["URL Rewriting"])

    @@ -130,7 +131,7 @@ diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml index 071451bff6e..bcb93b0d425 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.DefaultMeta.SummaryAdmin.cshtml @@ -15,6 +15,6 @@ @if (!string.IsNullOrEmpty(Model.Value.Author)) { - @Model.Value.Author + @Model.Value.Author } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs index aa5d729026b..526701967fe 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteRulesManager.cs @@ -6,7 +6,7 @@ namespace OrchardCore.UrlRewriting.Services; -public class RewriteRulesManager : IRewriteRulesManager +public sealed class RewriteRulesManager : IRewriteRulesManager { private readonly IRewriteRulesStore _store; private readonly IEnumerable _rewriteRuleHandlers; From 6823a922ebd74657c263c1dae5b9835e006746ac Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sat, 19 Oct 2024 10:27:48 -0700 Subject: [PATCH 68/71] Add a display name for the source --- .../Controllers/AdminController.cs | 65 ++++++++++++++++--- .../OrchardCore.UrlRewriting/Startup.cs | 24 +++---- .../ViewModels/RewriteRuleViewModel.cs | 8 +++ .../Views/Admin/Create.cshtml | 6 +- .../Views/Admin/Edit.cshtml | 6 +- .../Views/Admin/Index.cshtml | 4 +- .../Views/RewriteRule.Link.cshtml | 2 +- .../IUrlRewriteRuleSource.cs | 7 +- .../Models/RewriteRule.cs | 15 +++++ .../ApplicationBuilderExtensions.cs | 18 +++++ .../Extensions/ServiceCollectionExtensions.cs | 20 ++++++ .../Services/RewriteOptionsConfiguration.cs | 9 +-- .../Services/UrlRedirectRuleSource.cs | 5 +- .../Services/UrlRewriteRuleSource.cs | 5 +- 14 files changed, 156 insertions(+), 38 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Extensions/ApplicationBuilderExtensions.cs create mode 100644 src/OrchardCore/OrchardCore.UrlRewriting.Core/Extensions/ServiceCollectionExtensions.cs diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs index 53f8aaf7041..9f71044c47e 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Controllers/AdminController.cs @@ -2,7 +2,9 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; using OrchardCore.Admin; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.ModelBinding; @@ -23,6 +25,8 @@ public sealed class AdminController : Controller private readonly INotifier _notifier; private readonly IDisplayManager _rewriteRuleDisplayManager; private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; private readonly IShellReleaseManager _shellReleaseManager; private readonly IEnumerable _urlRewritingRuleSources; private readonly IRewriteRulesManager _rewriteRulesManager; @@ -38,6 +42,8 @@ public AdminController( IEnumerable urlRewritingRuleSources, IRewriteRulesManager rewriteRulesManager, IUpdateModelAccessor updateModelAccessor, + IServiceProvider serviceProvider, + ILogger logger, IStringLocalizer stringLocalizer, IHtmlLocalizer htmlLocalizer ) @@ -49,6 +55,8 @@ IHtmlLocalizer htmlLocalizer _urlRewritingRuleSources = urlRewritingRuleSources; _rewriteRulesManager = rewriteRulesManager; _updateModelAccessor = updateModelAccessor; + _serviceProvider = serviceProvider; + _logger = logger; S = stringLocalizer; H = htmlLocalizer; } @@ -66,7 +74,7 @@ public async Task Index(RewriteRuleOptions options) { Rules = [], Options = options, - SourceNames = _urlRewritingRuleSources.Select(x => x.Name), + SourceNames = _urlRewritingRuleSources.Select(x => x.TechnicalName), }; foreach (var rule in rules) @@ -93,6 +101,15 @@ public async Task Create(string id) return Forbid(); } + var ruleSource = _serviceProvider.GetKeyedService(id); + + if (ruleSource == null) + { + await _notifier.ErrorAsync(H["Unable to find a rule-source that can handle the source '{Source}'.", id]); + + return RedirectToAction(nameof(Index)); + } + var rule = await _rewriteRulesManager.NewAsync(id); if (rule == null) @@ -102,9 +119,13 @@ public async Task Create(string id) return RedirectToAction(nameof(Index)); } - var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); + var model = new RewriteRuleViewModel + { + DisplayName = ruleSource.DisplayName, + Editor = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true), + }; - return View(shape); + return View(model); } [HttpPost] @@ -116,6 +137,15 @@ public async Task CreatePOST(string id) return Forbid(); } + var ruleSource = _serviceProvider.GetKeyedService(id); + + if (ruleSource == null) + { + await _notifier.ErrorAsync(H["Unable to find a rule-source that can handle the source '{Source}'.", id]); + + return RedirectToAction(nameof(Index)); + } + var rule = await _rewriteRulesManager.NewAsync(id); if (rule == null) @@ -125,7 +155,11 @@ public async Task CreatePOST(string id) return RedirectToAction(nameof(Index)); } - var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true); + var model = new RewriteRuleViewModel + { + DisplayName = ruleSource.DisplayName, + Editor = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: true), + }; if (ModelState.IsValid) { @@ -138,7 +172,7 @@ public async Task CreatePOST(string id) _shellReleaseManager.SuspendReleaseRequest(); - return View(shape); + return View(model); } public async Task Edit(string id) @@ -155,9 +189,13 @@ public async Task Edit(string id) return NotFound(); } - var shape = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: false); + var model = new RewriteRuleViewModel + { + DisplayName = rule.Name, + Editor = await _rewriteRuleDisplayManager.BuildEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: false), + }; - return View(shape); + return View(model); } [HttpPost] @@ -176,11 +214,18 @@ public async Task EditPOST(string id) return NotFound(); } - var shape = await _rewriteRuleDisplayManager.UpdateEditorAsync(rule, _updateModelAccessor.ModelUpdater, isNew: false); + // Clone the rule to prevent modifying the original instance in the store. + var ruleToUpdate = rule.Clone(); + + var model = new RewriteRuleViewModel + { + DisplayName = ruleToUpdate.Name, + Editor = await _rewriteRuleDisplayManager.UpdateEditorAsync(ruleToUpdate, _updateModelAccessor.ModelUpdater, isNew: false), + }; if (ModelState.IsValid) { - await _rewriteRulesManager.SaveAsync(rule); + await _rewriteRulesManager.SaveAsync(ruleToUpdate); await _notifier.SuccessAsync(H["Rule updated successfully."]); @@ -189,7 +234,7 @@ public async Task EditPOST(string id) _shellReleaseManager.SuspendReleaseRequest(); - return View(shape); + return View(model); } [HttpPost] diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs index 79b3d41da81..0668879bb93 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Startup.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Rewrite; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -11,6 +10,7 @@ using OrchardCore.Security.Permissions; using OrchardCore.UrlRewriting.Drivers; using OrchardCore.UrlRewriting.Endpoints.Rules; +using OrchardCore.UrlRewriting.Extensions; using OrchardCore.UrlRewriting.Handlers; using OrchardCore.UrlRewriting.Models; using OrchardCore.UrlRewriting.Recipes; @@ -25,20 +25,18 @@ public override int Order public override void ConfigureServices(IServiceCollection services) { - services.AddNavigationProvider(); - services.AddPermissionProvider(); + services.AddUrlRewritingServices() + .AddNavigationProvider() + .AddPermissionProvider() + .AddTransient, ResourceManagementOptionsConfiguration>() + .AddScoped, RewriteRulesDisplayDriver>(); - services.AddTransient, RewriteOptionsConfiguration>(); - services.AddTransient, ResourceManagementOptionsConfiguration>(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, RewriteRulesDisplayDriver>(); - - // Add Apache Mod Rewrite options. + // Add Apache Mod Redirect Rule. services.AddRewriteRuleSource(UrlRedirectRuleSource.SourceName) .AddScoped() .AddScoped, UrlRedirectRuleDisplayDriver>(); + + // Add Apache Mod Rewrite Rule. services.AddRewriteRuleSource(UrlRewriteRuleSource.SourceName) .AddScoped() .AddScoped, UrlRewriteRuleDisplayDriver>(); @@ -48,9 +46,7 @@ public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder ro { routes.AddSortRulesEndpoint(); - var rewriteOptions = serviceProvider.GetRequiredService>().Value; - - app.UseRewriter(rewriteOptions); + app.UseUrlRewriting(serviceProvider); } } diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs new file mode 100644 index 00000000000..58d8356ec6e --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/ViewModels/RewriteRuleViewModel.cs @@ -0,0 +1,8 @@ +namespace OrchardCore.UrlRewriting.ViewModels; + +public class RewriteRuleViewModel +{ + public string DisplayName { get; set; } + + public dynamic Editor { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml index 35770c01f35..ae1682855d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Create.cshtml @@ -1,8 +1,10 @@ -

    @RenderTitleSegments(T["New rule"])

    +@model RewriteRuleViewModel + +

    @RenderTitleSegments(T["New '{0}' rule", Model.DisplayName])

    @Html.ValidationSummary() - @await DisplayAsync(Model) + @await DisplayAsync(Model.Editor) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml index bbda39ba90f..cbe465fb439 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Edit.cshtml @@ -1,8 +1,10 @@ -

    @RenderTitleSegments(T["Edit rule"])

    +@model RewriteRuleViewModel + +

    @RenderTitleSegments(T["Edit '{0}' rule", Model.DisplayName])

    @Html.ValidationSummary() - @await DisplayAsync(Model) + @await DisplayAsync(Model.Editor) diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml index bc764eb468c..1941b223be5 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/Admin/Index.cshtml @@ -3,7 +3,7 @@ @model ListRewriteRuleViewModel -

    @RenderTitleSegments(T["URL Rewriting"])

    +

    @RenderTitleSegments(T["URL Rewriting Rules"])

    @* the form is necessary to generate an antiforgery token for the delete and toggle actions *@ @@ -131,7 +131,7 @@ diff --git a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml index 8a67572ee80..e8fcf45f54d 100644 --- a/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.UrlRewriting/Views/RewriteRule.Link.cshtml @@ -8,7 +8,7 @@
    -

    @Model.Source

    +

    @(service?.DisplayName ?? Model.Source)

    @service?.Description

    diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteValidateResult.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteValidateResult.cs index 99a0b7ebad4..593d80f0a3b 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteValidateResult.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Abstractions/Models/RewriteValidateResult.cs @@ -6,7 +6,8 @@ public class RewriteValidateResult { private readonly List _errors = []; - public IReadOnlyList Errors => _errors; + public IReadOnlyList Errors + => _errors; /// /// Success may be altered by a handler during the validating async event. @@ -16,6 +17,7 @@ public class RewriteValidateResult public void Fail(ValidationResult error) { Succeeded = false; + _errors.Add(error); } } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs index 625b51dbdc9..f57756c16cd 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRedirectSourceMetadata.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.Models; -public class UrlRedirectSourceMetadata +public sealed class UrlRedirectSourceMetadata { public string Pattern { get; set; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs index e50b96c4187..653292d3452 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Models/UrlRewriteSourceMetadata.cs @@ -1,6 +1,6 @@ namespace OrchardCore.UrlRewriting.Models; -public class UrlRewriteSourceMetadata +public sealed class UrlRewriteSourceMetadata { public string Pattern { get; set; } diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs index 3d515e84c0c..5bb686686e3 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Rules/ExcludeUrlPrefixRule.cs @@ -5,18 +5,18 @@ namespace OrchardCore.UrlRewriting.Rules; internal sealed class ExcludeUrlPrefixRule : IRule { - private readonly PathString _adminUrlPrefix; + private readonly PathString _prefix; - public ExcludeUrlPrefixRule(string prefix) + public ExcludeUrlPrefixRule(PathString prefix) { ArgumentException.ThrowIfNullOrEmpty(prefix); - _adminUrlPrefix = new PathString('/' + prefix.TrimStart('/')); + _prefix = prefix; } public void ApplyRule(RewriteContext context) { - if (context.HttpContext.Request.Path.StartsWithSegments(_adminUrlPrefix)) + if (context.HttpContext.Request.Path.StartsWithSegments(_prefix)) { context.Result = RuleResult.SkipRemainingRules; diff --git a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs index f1ce1593c48..2ca6d14e6de 100644 --- a/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs +++ b/src/OrchardCore/OrchardCore.UrlRewriting.Core/Services/RewriteOptionsConfiguration.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -43,7 +44,9 @@ public void Configure(RewriteOptions options) if (options.Rules.Count > 0) { // Exclude URIs prefixed with 'admin' to prevent accidental access restrictions caused by the provided rules. - options.Rules.Insert(0, new ExcludeUrlPrefixRule(_adminOptions.AdminUrlPrefix)); + var prefix = new PathString('/' + _adminOptions.AdminUrlPrefix.TrimStart('/')); + + options.Rules.Insert(0, new ExcludeUrlPrefixRule(prefix)); } } } From 9c882232bdf4bcfdae19950466edec5411665da8 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sat, 19 Oct 2024 15:02:13 -0700 Subject: [PATCH 70/71] improve docs --- .../reference/modules/UrlRewriting/README.md | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/docs/reference/modules/UrlRewriting/README.md b/src/docs/reference/modules/UrlRewriting/README.md index ec1701ba2da..35909c9316d 100644 --- a/src/docs/reference/modules/UrlRewriting/README.md +++ b/src/docs/reference/modules/UrlRewriting/README.md @@ -1,20 +1,17 @@ # URL Rewriting (`OrchardCore.UrlRewriting`) -The URL Rewriting module enables you to configure URL rewrites and redirects for incoming HTTP requests, significantly enhancing your site's SEO and user experience. This allows you to control how URLs are handled and presented to users and search engines. +The URL Rewriting feature allows you to configure URL rewrites and redirects for incoming HTTP requests, significantly improving your site's SEO and user experience. This feature enables you to control how URLs are presented to both users and search engines. -After enabling this feature, you can manage your rewrite rules by navigating to **Configuration** >> **URL Rewriting**. +Once enabled, you can manage your rewrite rules by navigating to **Configuration** >> **URL Rewriting**. The order of these rules is crucial, as they are processed sequentially based on their position. The first listed rule is evaluated first for matches. To facilitate this, the UI provides a drag-and-drop feature for easy sorting of the rules. ## Available Rule Sources -### Redirect Rule +| Rule Type | Description | Example | +|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| +| **Redirect Rule** | The **Redirect Rule** is utilized to send users from one URL to another, which is particularly beneficial for maintaining SEO integrity when URLs change. | Permanently redirect users from `/about-us` to `/about`. | +| **Rewrite Rule** | The **Rewrite Rule** allows you to modify the incoming request URL without changing the URL displayed in the browser's address bar, aiding in content organization. | Change requests for media files from `/img/` to `/media/`. | -The **Redirect Rule** is utilized to send users from one URL to another. This is particularly beneficial for maintaining SEO integrity when URLs change. For example, a redirect rule can permanently redirect users from `/about-us` to `/about`, ensuring both users and search engines are directed to the correct page. - -### Rewrite Rule - -The **Rewrite Rule** allows you to modify the incoming request URL without changing the URL displayed in the browser's address bar. This can be advantageous for organizing content and serving files more effectively. For instance, a rewrite rule can change requests for media files from the `/img/` prefix to `/media/`, thus creating a consistent structure for accessing media files. - -## Adding Rule Sources +## Creating Additional Rule Sources To add a new rule source, implement the `IUrlRewriteRuleSource` interface. This implementation will allow you to register a new rule source and provide a mechanism to configure these rules. @@ -35,6 +32,8 @@ In this example, `CustomRuleSource` represents your implementation of `IUrlRewri ## Recipes +The recipe will be accessible only if the OrchardCore.Recipes.Core feature is enabled. + ### Recipe for Creating and Updating Rules The `UrlRewriting` step allows you to create or update URL rewrite rules easily. The example below illustrates how to create a rule that permanently redirects users from `/about-us` to `/about`, along with another rule that serves all media files using the `/img/` prefix instead of `/media/`. @@ -73,20 +72,28 @@ The `UrlRewriting` step allows you to create or update URL rewrite rules easily. ### Redirect Rule Properties -- **Id**: A unique identifier for the redirect rule. If the specified Id matches an existing rule, that rule will be updated with the provided properties. To create a new rule, leave the Id property empty or specify a unique value that does not match any existing rule. -- **Name**: A descriptive name for the rule (e.g., `"Redirect about-us to about"`). +- **Id**: A unique identifier for the redirect rule. If the specified ID matches an existing rule, that rule will be updated with the provided properties. To create a new rule, leave the ID property empty or specify a unique value that does not match any existing rule. +- **Name**: A descriptive name for the rule (e.g., "Redirect about-us to about"). - **Pattern**: The URL pattern to match (e.g., `^/about-us$` for an exact match). -- **SubstitutionPattern**: The target URL to redirect to (e.g., `/about`). -- **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. -- **QueryStringPolicy**: To append the query string to the new URL, use 'Append'. To ignore the query string during the redirect, select 'Drop'. -- **RedirectType**: Specifies the HTTP status code for the redirect. `MovedPermanently` (HTTP 301) indicates a permanent redirect. +- **SubstitutionPattern**: The target URL to which the redirect will occur (e.g., `/about`). +- **IsCaseInsensitive**: When set to `true`, the pattern matching will be case-insensitive. +- **QueryStringPolicy**: Determines how query strings are handled during the redirect: + - **Append**: Appends the original query string to the new URL. + - **Drop**: Ignores the query string during the redirect. +- **RedirectType**: Specifies the HTTP status code for the redirect. The following values are supported: + - **Found**: (HTTP 302) Indicates a temporary redirect. + - **MovedPermanently**: (HTTP 301) Indicates a permanent redirect, instructing clients to update their bookmarks or links to the new URL. + - **TemporaryRedirect**: (HTTP 307) Similar to 302 but ensures that the request method remains unchanged (e.g., a POST request remains a POST). + - **PermanentRedirect**: (HTTP 308) Indicates that the resource has been permanently moved to a new URL. ### Rewrite Rule Properties -- **Id**: A unique identifier for the rewrite rule. Similar to redirect rules, if the specified Id matches an existing rule, that rule will be updated. Leave the Id empty to create a new rule. -- **Name**: A descriptive name for the rule (e.g., `"Serve media URLs from img"`). -- **Pattern**: The URL pattern to match (e.g., `^/img/(.*)$` to match any URL starting with `/img/`). +- **Id**: A unique identifier for the rewrite rule. If the specified ID matches an existing rule, that rule will be updated. To create a new rule, leave the ID empty. +- **Name**: A descriptive name for the rule (e.g., "Serve media URLs from img"). +- **Pattern**: The URL pattern to match (e.g., `^/img/(.*)$` matches any URL starting with `/img/`). - **SubstitutionPattern**: The target URL for the rewrite (e.g., `/media/$1`, where `$1` captures the matched portion of the original URL). -- **IsCaseInsensitive**: When set to `true`, matching is case-insensitive. -- **QueryStringPolicy**: To append the query string to the new URL, use 'Append'. To ignore the query string during the redirect, select 'Drop'. -- **SkipFurtherRules**: When set to `true`, subsequent rules will not be processed if this rule matches. +- **IsCaseInsensitive**: When set to `true`, the pattern matching will be case-insensitive. +- **QueryStringPolicy**: Determines how query strings are handled during the rewrite: + - **Append**: Appends the original query string to the new URL. + - **Drop**: Ignores the query string during the rewrite. +- **SkipFurtherRules**: When set to `true`, any subsequent rules will not be processed if this rule matches. From f2740c2c81a2dc6b3a2b4dc3274e72e43385e897 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sat, 19 Oct 2024 15:05:10 -0700 Subject: [PATCH 71/71] cleaner table --- src/docs/reference/modules/UrlRewriting/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs/reference/modules/UrlRewriting/README.md b/src/docs/reference/modules/UrlRewriting/README.md index 35909c9316d..02562a3f1a4 100644 --- a/src/docs/reference/modules/UrlRewriting/README.md +++ b/src/docs/reference/modules/UrlRewriting/README.md @@ -6,9 +6,9 @@ Once enabled, you can manage your rewrite rules by navigating to **Configuration ## Available Rule Sources -| Rule Type | Description | Example | -|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| -| **Redirect Rule** | The **Redirect Rule** is utilized to send users from one URL to another, which is particularly beneficial for maintaining SEO integrity when URLs change. | Permanently redirect users from `/about-us` to `/about`. | +| Rule Type | Description | Example | +|---------------|---------------|---------------| +| **Redirect Rule** | The **Redirect Rule** is utilized to send users from one URL to another, which is particularly beneficial for maintaining SEO integrity when URLs change. | Permanently redirect users from `/about-us` to `/about`. | | **Rewrite Rule** | The **Rewrite Rule** allows you to modify the incoming request URL without changing the URL displayed in the browser's address bar, aiding in content organization. | Change requests for media files from `/img/` to `/media/`. | ## Creating Additional Rule Sources