From e854118e45792722e3f699e0896e248cdb58c11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 14 Jul 2023 20:06:30 +0200 Subject: [PATCH 01/13] Unified PublicSign property --- src/Framework/Testing/DotVVM.Framework.Testing.csproj | 2 +- src/Tests/DotVVM.Framework.Tests.csproj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Framework/Testing/DotVVM.Framework.Testing.csproj b/src/Framework/Testing/DotVVM.Framework.Testing.csproj index c1527c56ec..bef84dc8bb 100644 --- a/src/Framework/Testing/DotVVM.Framework.Testing.csproj +++ b/src/Framework/Testing/DotVVM.Framework.Testing.csproj @@ -14,7 +14,7 @@ True - True + true dotvvmwizard.snk diff --git a/src/Tests/DotVVM.Framework.Tests.csproj b/src/Tests/DotVVM.Framework.Tests.csproj index e9c7067ff5..fa3ee599c0 100644 --- a/src/Tests/DotVVM.Framework.Tests.csproj +++ b/src/Tests/DotVVM.Framework.Tests.csproj @@ -17,7 +17,8 @@ net6.0 - True + true + true dotvvmwizard.snk From 5421a3ae863c1fab5b92a1f88484d0b5db2cec7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 14 Jul 2023 20:06:44 +0200 Subject: [PATCH 02/13] Added a new version of WebForms adapters --- .../DotVVM.Adapters.WebForms.Tests.csproj | 24 ++++ .../Tests/WebForms/HybridRouteLinkTests.cs | 97 +++++++++++++ .../Tests/WebForms/WebFormsRouteTableInit.cs | 31 +++++ ...eLinkTests.HybridRouteLink_NoBindings.html | 12 ++ ....HybridRouteLink_SuffixAndQueryString.html | 10 ++ ...inkTests.HybridRouteLink_ValueBinding.html | 10 ++ .../WebForms/Controls/HybridRouteLink.cs | 54 ++++++++ .../WebForms/Controls/RouteLinkCapability.cs | 24 ++++ .../WebForms/Controls/WebFormsLinkUtils.cs | 130 ++++++++++++++++++ .../WebForms/DotVVM.Adapters.WebForms.csproj | 27 ++++ .../WebForms/DotvvmConfigurationExtensions.cs | 20 +++ src/Adapters/WebForms/RedirectHelper.cs | 39 ++++++ src/Adapters/WebForms/dotvvmwizard.snk | Bin 0 -> 596 bytes src/DotVVM.sln | 38 +++++ 14 files changed, 516 insertions(+) create mode 100644 src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj create mode 100644 src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs create mode 100644 src/Adapters/Tests/WebForms/WebFormsRouteTableInit.cs create mode 100644 src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html create mode 100644 src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html create mode 100644 src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html create mode 100644 src/Adapters/WebForms/Controls/HybridRouteLink.cs create mode 100644 src/Adapters/WebForms/Controls/RouteLinkCapability.cs create mode 100644 src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs create mode 100644 src/Adapters/WebForms/DotVVM.Adapters.WebForms.csproj create mode 100644 src/Adapters/WebForms/DotvvmConfigurationExtensions.cs create mode 100644 src/Adapters/WebForms/RedirectHelper.cs create mode 100644 src/Adapters/WebForms/dotvvmwizard.snk diff --git a/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj new file mode 100644 index 0000000000..b6c6e34cda --- /dev/null +++ b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj @@ -0,0 +1,24 @@ + + + + net472 + false + + + + + + + + + + + + + + + + + + + diff --git a/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs new file mode 100644 index 0000000000..b4df903b56 --- /dev/null +++ b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Web; +using CheckTestOutput; +using DotVVM.Framework.Testing; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotVVM.Adapters.WebForms.Tests +{ + [TestClass] + public class HybridRouteLinkTests + { + private static readonly ControlTestHelper cth = new ControlTestHelper(config: config => config.AddWebFormsAdapters()); + OutputChecker check = new OutputChecker("testoutputs"); + + [ClassInitialize] + public static void Init(TestContext testContext) + { + WebFormsRouteTableInit.EnsureInitialized(); + } + + [TestMethod] + public async Task HybridRouteLink_NoBindings() + { + HttpContext.Current = new HttpContext( + new HttpRequest("", "http://tempuri.org", ""), + new HttpResponse(new StringWriter()) + ); + + var r = await cth.RunPage(typeof(ControlTestViewModel), @" + + + + ; + ; + + "); + + check.CheckString(r.FormattedHtml, fileExtension: "html"); + } + + [TestMethod] + public async Task HybridRouteLink_ValueBinding() + { + HttpContext.Current = new HttpContext( + new HttpRequest("", "http://tempuri.org", ""), + new HttpResponse(new StringWriter()) + ); + + var r = await cth.RunPage(typeof(ControlTestViewModel), @" + + + + "); + + check.CheckString(r.FormattedHtml, fileExtension: "html"); + } + + [TestMethod] + public async Task HybridRouteLink_SuffixAndQueryString() + { + HttpContext.Current = new HttpContext( + new HttpRequest("", "http://tempuri.org", ""), + new HttpResponse(new StringWriter()) + ); + + var r = await cth.RunPage(typeof(ControlTestViewModel), @" + + + + + "); + + check.CheckString(r.FormattedHtml, fileExtension: "html"); + } + } + + class ControlTestViewModel + { + public int Value { get; set; } = 15; + + public List Items { get; set; } = new() + { + new ControlTestChildViewModel() { Id = 1, Name = "one" }, + new ControlTestChildViewModel() { Id = 2, Name = "two" }, + new ControlTestChildViewModel() { Id = 3, Name = "three" } + }; + } + + class ControlTestChildViewModel + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/src/Adapters/Tests/WebForms/WebFormsRouteTableInit.cs b/src/Adapters/Tests/WebForms/WebFormsRouteTableInit.cs new file mode 100644 index 0000000000..c56508ce8b --- /dev/null +++ b/src/Adapters/Tests/WebForms/WebFormsRouteTableInit.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Routing; + +namespace DotVVM.Adapters.WebForms.Tests +{ + public static class WebFormsRouteTableInit + { + + static WebFormsRouteTableInit() + { + RouteTable.Routes.Add("NoParams", new Route("", new EmptyHandler())); + RouteTable.Routes.Add("SingleParam", new Route("page/{Index}", new EmptyHandler())); + RouteTable.Routes.Add("MultipleOptionalParams", new Route("catalog/{Tag}/{SubTag}", new EmptyHandler()) { Defaults = new RouteValueDictionary(new { Tag = "xx", SubTag = "yy" })}); + } + + public static void EnsureInitialized() + { + } + + } + + public class EmptyHandler : IRouteHandler + { + public IHttpHandler GetHttpHandler(RequestContext requestContext) => throw new NotImplementedException(); + } +} diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html new file mode 100644 index 0000000000..85bf141b2a --- /dev/null +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html @@ -0,0 +1,12 @@ + + + + hello 1 + hello 2 + hello 3 + hello 4); + hello 5); + hello 6 + hello 6 + + diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html new file mode 100644 index 0000000000..68abf32cc8 --- /dev/null +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html @@ -0,0 +1,10 @@ + + + + hello 1 + hello 2 + hello 3 + hello 4 + hello 5 + + diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html new file mode 100644 index 0000000000..98e84b165e --- /dev/null +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html @@ -0,0 +1,10 @@ + + + + hello 3 +
+ + + + + diff --git a/src/Adapters/WebForms/Controls/HybridRouteLink.cs b/src/Adapters/WebForms/Controls/HybridRouteLink.cs new file mode 100644 index 0000000000..9499a84105 --- /dev/null +++ b/src/Adapters/WebForms/Controls/HybridRouteLink.cs @@ -0,0 +1,54 @@ +using DotVVM.Framework.Controls; +using DotVVM.Framework.Hosting; + +#if NETFRAMEWORK +using System.Web.Routing; +#endif + +namespace DotVVM.Adapters.WebForms.Controls +{ + public class HybridRouteLink : CompositeControl + { + private readonly IDotvvmRequestContext context; + + public HybridRouteLink(IDotvvmRequestContext context) + { + this.context = context; + } + + public DotvvmControl GetContents( + HtmlCapability htmlCapability, + TextOrContentCapability textOrContent, + RouteLinkCapability routeLinkCapability + ) + { + if (context.Configuration.RouteTable.Contains(routeLinkCapability.RouteName)) + { + return GenerateDotvvmRouteLink(htmlCapability, textOrContent, routeLinkCapability); + } +#if NETFRAMEWORK + else if (RouteTable.Routes[routeLinkCapability.RouteName] is Route webFormsRoute) + { + return WebFormsLinkUtils.BuildWebFormsRouteLink(this, context, htmlCapability, textOrContent, routeLinkCapability, webFormsRoute); + } +#endif + else + { + throw new DotvvmControlException($"Route '{routeLinkCapability.RouteName}' does not exist."); + } + } + + private static DotvvmControl GenerateDotvvmRouteLink(HtmlCapability htmlCapability, TextOrContentCapability textOrContent, RouteLinkCapability routeLinkCapability) + { + var link = new RouteLink() + .SetCapability(htmlCapability) + .SetCapability(textOrContent) + .SetProperty(r => r.RouteName, routeLinkCapability.RouteName) + .SetProperty(l => l.UrlSuffix, routeLinkCapability.UrlSuffix); + link.QueryParameters.CopyFrom(routeLinkCapability.QueryParameters); + link.Params.CopyFrom(routeLinkCapability.Params); + return link; + } + + } +} diff --git a/src/Adapters/WebForms/Controls/RouteLinkCapability.cs b/src/Adapters/WebForms/Controls/RouteLinkCapability.cs new file mode 100644 index 0000000000..8b5597d962 --- /dev/null +++ b/src/Adapters/WebForms/Controls/RouteLinkCapability.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.ComponentModel; +using DotVVM.Framework.Binding; + +namespace DotVVM.Adapters.WebForms.Controls +{ + [DotvvmControlCapability()] + public sealed class RouteLinkCapability + { + [PropertyGroup("Query-")] + [DefaultValue(null)] + public IReadOnlyDictionary> QueryParameters { get; private set; } = new Dictionary>(); + + [PropertyGroup("Param-")] + [DefaultValue(null)] + public IReadOnlyDictionary> Params { get; private set; } = new Dictionary>(); + + public string RouteName { get; private set; } + + [DefaultValue(null)] + public ValueOrBinding? UrlSuffix { get; private set; } + + } +} diff --git a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs new file mode 100644 index 0000000000..fcea37e400 --- /dev/null +++ b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs @@ -0,0 +1,130 @@ +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using DotVVM.Framework.Binding; +using DotVVM.Framework.Controls; +using DotVVM.Framework.Routing; +using DotVVM.Framework.Utils; +using System.Web.Routing; +using System.Web; +using DotVVM.Framework.Binding.Expressions; +using DotVVM.Framework.Hosting; + +namespace DotVVM.Adapters.WebForms.Controls +{ + public class WebFormsLinkUtils + { + public static HtmlGenericControl BuildWebFormsRouteLink(DotvvmControl container, IDotvvmRequestContext context, HtmlCapability htmlCapability, TextOrContentCapability textOrContent, RouteLinkCapability routeLinkCapability, Route webFormsRoute) + { + var link = new HtmlGenericControl("a", textOrContent, htmlCapability); + + var parameters = BuildParameters(context, routeLinkCapability, webFormsRoute); + if (routeLinkCapability.UrlSuffix is { HasBinding: true, BindingOrDefault: IValueBinding } + || routeLinkCapability.Params.Any(p => p.Value is { HasBinding: true, BindingOrDefault: IValueBinding })) + { + // bindings are used, we have to generate client-script code + var fragments = new List { KnockoutHelper.MakeStringLiteral(context.TranslateVirtualPath("~/")) }; + + // generate binding and embed it in the function call + var routeUrlExpression = GenerateRouteUrlExpression(container, webFormsRoute, parameters); + fragments.Add(routeUrlExpression); + + // generate URL suffix + if (GenerateUrlSuffixExpression(container, routeLinkCapability) is string urlSuffix) + { + fragments.Add(urlSuffix); + } + + // render the binding and try to evaluate it on the server + link.AddAttribute("data-bind", "attr: { 'href': " + fragments.StringJoin("+") + "}"); + if (container.DataContext != null) + { + try + { + var url = context.TranslateVirtualPath(EvaluateRouteUrl(container, webFormsRoute, parameters, routeLinkCapability)); + link.SetAttribute("href", url); + } + catch (Exception ex) + { + } + } + } + else + { + // the value can be built on the server + var url = context.TranslateVirtualPath(EvaluateRouteUrl(container, webFormsRoute, parameters, routeLinkCapability)); + link.SetAttribute("href", url); + } + + return link; + } + + private static IDictionary> BuildParameters(IDotvvmRequestContext context, RouteLinkCapability routeLinkCapability, Route webFormsRoute) + { + var parameters = webFormsRoute.Defaults?.ToDictionary(t => t.Key, t => ValueOrBinding.FromBoxedValue(t.Value)) + ?? new Dictionary>(); + foreach (var param in context.Parameters) + { + parameters[param.Key] = ValueOrBinding.FromBoxedValue(param.Value); + } + + foreach (var item in routeLinkCapability.Params) + { + parameters[item.Key] = item.Value; + } + + return parameters; + } + + private static string EvaluateRouteUrl(DotvvmControl container, Route webFormsRoute, IDictionary> parameters, RouteLinkCapability routeLinkCapability) + { + // evaluate bindings on server + var routeValues = new RouteValueDictionary(); + foreach (Match param in Regex.Matches(webFormsRoute.Url, @"\{([^{}/]+)\}")) // https://referencesource.microsoft.com/#System.Web/Routing/RouteParser.cs,48 + { + var paramName = param.Groups[1].Value; + parameters.TryGetValue(paramName, out var value); + routeValues[paramName] = value.Evaluate(container) ?? ""; + } + + // generate the URL + return "~/" + + webFormsRoute.GetVirtualPath(HttpContext.Current.Request.RequestContext, routeValues)?.VirtualPath + + UrlHelper.BuildUrlSuffix(routeLinkCapability.UrlSuffix?.Evaluate(container), routeLinkCapability.QueryParameters.ToDictionary(p => p.Key, p => p.Value.Evaluate(container))); + } + + private static string GenerateRouteUrlExpression(DotvvmControl container, Route webFormsRoute, IDictionary> parameters) + { + var parametersExpression = parameters + .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key)}: {p.Value.GetJsExpression(container)}") + .StringJoin(","); + var routeUrlExpression = $"dotvvm.buildRouteUrl({KnockoutHelper.MakeStringLiteral(webFormsRoute.Url)}, {{{parametersExpression}}})"; + return routeUrlExpression; + } + + private static string GenerateUrlSuffixExpression(DotvvmControl container, RouteLinkCapability routeLinkCapability) + { + var urlSuffixBase = routeLinkCapability.UrlSuffix?.GetJsExpression(container); + var queryParams = routeLinkCapability.QueryParameters + .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key.ToLowerInvariant())}: {p.Value.GetJsExpression(container)}") + .StringJoin(","); + + // generate the function call + if (queryParams.Any()) + { + return $"dotvvm.buildUrlSuffix({urlSuffixBase}, {{{queryParams}}})"; + } + else if (urlSuffixBase != "\"\"") + { + return urlSuffixBase; + } + else + { + return null; + } + } + } +} +#endif diff --git a/src/Adapters/WebForms/DotVVM.Adapters.WebForms.csproj b/src/Adapters/WebForms/DotVVM.Adapters.WebForms.csproj new file mode 100644 index 0000000000..096c07a02b --- /dev/null +++ b/src/Adapters/WebForms/DotVVM.Adapters.WebForms.csproj @@ -0,0 +1,27 @@ + + + + $(DefaultTargetFrameworks) + DotVVM.Adapters.WebForms + + This package contains helpers for migration of ASP.NET Web Forms application to DotVVM. + $(Description) + + + + + + + True + dotvvmwizard.snk + + + + + + + + + + + diff --git a/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs b/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs new file mode 100644 index 0000000000..bc366ee25f --- /dev/null +++ b/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Adapters.WebForms.Controls; +using DotVVM.Framework.Configuration; + +namespace DotVVM.Adapters.WebForms +{ + public static class DotvvmConfigurationExtensions + { + + public static void AddWebFormsAdapters(this DotvvmConfiguration config) + { + config.Markup.AddCodeControls("webforms", typeof(HybridRouteLink)); + config.Markup.Assemblies.Add(typeof(DotvvmConfigurationExtensions).Assembly.FullName); + } + } +} diff --git a/src/Adapters/WebForms/RedirectHelper.cs b/src/Adapters/WebForms/RedirectHelper.cs new file mode 100644 index 0000000000..d72f09c00a --- /dev/null +++ b/src/Adapters/WebForms/RedirectHelper.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Web; +using DotVVM.Framework.Routing; + +#if NETFRAMEWORK +using System.Web.Routing; +#endif + +namespace DotVVM.Framework.Hosting +{ + public static class DotvvmRequestContextExtensions + { + public static void RedirectToRouteHybrid(this IDotvvmRequestContext context, string routeName, object routeValues = null, string urlSuffix = null, object query = null) + { + if (context.Configuration.RouteTable.Contains(routeName)) + { + // we have DotVVM route - use it + var url = context.Configuration.RouteTable[routeName].BuildUrl(routeValues); + url += UrlHelper.BuildUrlSuffix(urlSuffix, query); + context.RedirectToUrl(url); + } +#if NETFRAMEWORK + else if (RouteTable.Routes[routeName] is Route webFormsRoute) + { + // fall back to the Web Forms route + var url = webFormsRoute.GetVirtualPath(HttpContext.Current.Request.RequestContext, new RouteValueDictionary(routeValues))!.VirtualPath; + url += UrlHelper.BuildUrlSuffix(urlSuffix, query); + context.RedirectToUrl(url); + } +#endif + else + { + throw new ArgumentException($"The route {routeName} doesn't exist!"); + } + } + } +} diff --git a/src/Adapters/WebForms/dotvvmwizard.snk b/src/Adapters/WebForms/dotvvmwizard.snk new file mode 100644 index 0000000000000000000000000000000000000000..34430b3c78542a7d831ed4d07414ad71984bfefc GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa500968#YETJw3;Q4k97&RPe_F{!G|nCdoQ|$ zrfVO%BxTC4y6jeFJgW}}EZ710;wMw6UByyI)U4nVrR?g(4TQtF1WTGXtc$8%vQ6w{ zO4<7S$Z4&UwqQT~Qy~&VV$TY*XiJIP9@4C&@pbn5{C5l72V<7AU>C;R<)k=VduO-( zoFw-E*(g(7`iw3MHgQkMN;Q_j_uZchOF$+;l+uOwsw{*G={9%S9jth>31~U(KUd_q z>-{zG5=rgzcq83Y3v+h%Cht^>c^*zHI%xkR2nTtwuLJi!|6oJnxKAw|;sNm4Kx#~S zF5AjxMlkMlOg>Ey-vv5Q%hH8&>rg94f zvR?Ia7g{xSHP$s-s=kp`88Vj3++%{TQ_A_8s>#c-CD}_|85lrXiMn6s$bv<_w_`p0 zQr3 literal 0 HcmV?d00001 diff --git a/src/DotVVM.sln b/src/DotVVM.sln index 3542ef1634..0c677c9d39 100644 --- a/src/DotVVM.sln +++ b/src/DotVVM.sln @@ -123,6 +123,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Framework.Controls.D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Framework.Controls.DynamicData", "DynamicData\DynamicData\DotVVM.Framework.Controls.DynamicData.csproj", "{9E19A537-E1B2-4D1E-A904-D99D4222474F}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Adapters", "Adapters", "{11C116EC-5E5A-400A-9311-0732DD69401C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebForms", "WebForms", "{42513853-3772-46D2-94C2-965101E2406D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{05A3401A-C541-4F7C-AAD8-02A23648CD27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Adapters.WebForms.Tests", "Adapters\Tests\WebForms\DotVVM.Adapters.WebForms.Tests.csproj", "{A6A8451E-99D8-4296-BBA9-69E1E289270A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Adapters.WebForms", "Adapters\WebForms\DotVVM.Adapters.WebForms.csproj", "{25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -697,6 +707,30 @@ Global {9E19A537-E1B2-4D1E-A904-D99D4222474F}.Release|x64.Build.0 = Release|Any CPU {9E19A537-E1B2-4D1E-A904-D99D4222474F}.Release|x86.ActiveCfg = Release|Any CPU {9E19A537-E1B2-4D1E-A904-D99D4222474F}.Release|x86.Build.0 = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|x64.Build.0 = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Debug|x86.Build.0 = Debug|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|Any CPU.Build.0 = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|x64.ActiveCfg = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|x64.Build.0 = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|x86.ActiveCfg = Release|Any CPU + {A6A8451E-99D8-4296-BBA9-69E1E289270A}.Release|x86.Build.0 = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|x64.ActiveCfg = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|x64.Build.0 = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|x86.ActiveCfg = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Debug|x86.Build.0 = Debug|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|Any CPU.Build.0 = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|x64.ActiveCfg = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|x64.Build.0 = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|x86.ActiveCfg = Release|Any CPU + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -753,6 +787,10 @@ Global {DB0AB0C3-DA5E-4B5A-9CD4-036D37B50AED} = {E57EE0B8-30FC-4702-B310-FB82C19D7473} {3209E1B1-88BB-4A95-B234-950E89EFCEE0} = {CF90322D-63BC-4047-BFEA-EE87E45020AF} {9E19A537-E1B2-4D1E-A904-D99D4222474F} = {CF90322D-63BC-4047-BFEA-EE87E45020AF} + {42513853-3772-46D2-94C2-965101E2406D} = {11C116EC-5E5A-400A-9311-0732DD69401C} + {05A3401A-C541-4F7C-AAD8-02A23648CD27} = {42513853-3772-46D2-94C2-965101E2406D} + {A6A8451E-99D8-4296-BBA9-69E1E289270A} = {05A3401A-C541-4F7C-AAD8-02A23648CD27} + {25442AA8-7E4D-47EC-8CCB-F9E2B45EB998} = {42513853-3772-46D2-94C2-965101E2406D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {61F8A195-365E-47B1-A6F2-CD3534E918F8} From 49db919f5841887148c66f2b40f41aaaca6e46f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 14 Jul 2023 20:09:14 +0200 Subject: [PATCH 03/13] Web Forms package added to the CI scripts --- ci/scripts/Get-PublicProjects.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/scripts/Get-PublicProjects.ps1 b/ci/scripts/Get-PublicProjects.ps1 index 9669670080..f1adf33d67 100644 --- a/ci/scripts/Get-PublicProjects.ps1 +++ b/ci/scripts/Get-PublicProjects.ps1 @@ -108,5 +108,10 @@ return @( Name = "DotVVM.Tracing.MiniProfiler.Owin"; Path = "src/Tracing/MiniProfiler.Owin"; Type = "standard" + }, + [PSCustomObject]@{ + Name = "DotVVM.Adapters.WebForms"; + Path = "src/Adapters/WebForms"; + Type = "standard" } ) From e7dc3b30c502d12cca81372504b5225d5123b658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 14 Jul 2023 20:10:54 +0200 Subject: [PATCH 04/13] Test outputs fixed --- src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs | 4 ++-- .../HybridRouteLinkTests.HybridRouteLink_NoBindings.html | 4 ++-- ...uteLinkTests.HybridRouteLink_SuffixAndQueryString.html | 2 +- ...HybridRouteLinkTests.HybridRouteLink_ValueBinding.html | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs index b4df903b56..24b2565aeb 100644 --- a/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs +++ b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs @@ -33,8 +33,8 @@ public async Task HybridRouteLink_NoBindings() - ; - ; + + "); diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html index 85bf141b2a..8c77847baa 100644 --- a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_NoBindings.html @@ -4,8 +4,8 @@ hello 1 hello 2 hello 3 - hello 4); - hello 5); + hello 4 + hello 5 hello 6 hello 6 diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html index 68abf32cc8..1fa9df0670 100644 --- a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html @@ -5,6 +5,6 @@ hello 2 hello 3 hello 4 - hello 5 + hello 5 diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html index 98e84b165e..cc45494e0d 100644 --- a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_ValueBinding.html @@ -1,10 +1,10 @@ - hello 3 -
+ hello 3 +
- - + + From 242a02349758b9621887e0fc798d0b3e9fe717fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 14 Jul 2023 20:22:44 +0200 Subject: [PATCH 05/13] Fixed bug, test made deterministic --- ...idRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html | 2 +- src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html index 1fa9df0670..f08748f268 100644 --- a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html @@ -5,6 +5,6 @@ hello 2 hello 3 hello 4 - hello 5 + hello 5 diff --git a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs index fcea37e400..19d499d457 100644 --- a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs +++ b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs @@ -99,6 +99,7 @@ private static string GenerateRouteUrlExpression(DotvvmControl container, Route { var parametersExpression = parameters .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key)}: {p.Value.GetJsExpression(container)}") + .OrderBy(p => p) .StringJoin(","); var routeUrlExpression = $"dotvvm.buildRouteUrl({KnockoutHelper.MakeStringLiteral(webFormsRoute.Url)}, {{{parametersExpression}}})"; return routeUrlExpression; @@ -106,9 +107,10 @@ private static string GenerateRouteUrlExpression(DotvvmControl container, Route private static string GenerateUrlSuffixExpression(DotvvmControl container, RouteLinkCapability routeLinkCapability) { - var urlSuffixBase = routeLinkCapability.UrlSuffix?.GetJsExpression(container); + var urlSuffixBase = routeLinkCapability.UrlSuffix?.GetJsExpression(container) ?? "\"\""; var queryParams = routeLinkCapability.QueryParameters .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key.ToLowerInvariant())}: {p.Value.GetJsExpression(container)}") + .OrderBy(p => p) .StringJoin(","); // generate the function call From cab997fc31c768e6af5a3b893b45e8a1668f2959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Wed, 26 Jul 2023 17:57:32 +0200 Subject: [PATCH 06/13] Refactoring, obsolete attributes added for .NET Core branch --- src/Adapters/WebForms/Controls/HybridRouteLink.cs | 9 ++++++++- ...RedirectHelper.cs => WebFormsAdaptersExtensions.cs} | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) rename src/Adapters/WebForms/{RedirectHelper.cs => WebFormsAdaptersExtensions.cs} (75%) diff --git a/src/Adapters/WebForms/Controls/HybridRouteLink.cs b/src/Adapters/WebForms/Controls/HybridRouteLink.cs index 9499a84105..a82d38722e 100644 --- a/src/Adapters/WebForms/Controls/HybridRouteLink.cs +++ b/src/Adapters/WebForms/Controls/HybridRouteLink.cs @@ -1,4 +1,5 @@ -using DotVVM.Framework.Controls; +using System; +using DotVVM.Framework.Controls; using DotVVM.Framework.Hosting; #if NETFRAMEWORK @@ -7,6 +8,12 @@ namespace DotVVM.Adapters.WebForms.Controls { + /// + /// Renders a hyperlink pointing to the specified DotVVM route if such route exists; otherwise it falls back to a Web Forms route with the specified name. + /// +#if !NETFRAMEWORK + [Obsolete("This control is used only during the Web Forms migration and is not needed in .NET Core. Use the standard RouteLink control.")] +#endif public class HybridRouteLink : CompositeControl { private readonly IDotvvmRequestContext context; diff --git a/src/Adapters/WebForms/RedirectHelper.cs b/src/Adapters/WebForms/WebFormsAdaptersExtensions.cs similarity index 75% rename from src/Adapters/WebForms/RedirectHelper.cs rename to src/Adapters/WebForms/WebFormsAdaptersExtensions.cs index d72f09c00a..30fd66e68a 100644 --- a/src/Adapters/WebForms/RedirectHelper.cs +++ b/src/Adapters/WebForms/WebFormsAdaptersExtensions.cs @@ -8,10 +8,18 @@ using System.Web.Routing; #endif +// ReSharper disable once CheckNamespace namespace DotVVM.Framework.Hosting { - public static class DotvvmRequestContextExtensions + public static class WebFormsAdaptersExtensions { + + /// + /// Redirects to the specified DotVVM route if such route exists; otherwise it redirects to the specified Web Forms route. + /// +#if !NETFRAMEWORK + [Obsolete("This method is used only during the Web Forms migration and is not needed in .NET Core. Use the standard RedirectToRoute method.")] +#endif public static void RedirectToRouteHybrid(this IDotvvmRequestContext context, string routeName, object routeValues = null, string urlSuffix = null, object query = null) { if (context.Configuration.RouteTable.Contains(routeName)) From 0912e53876717941b6cdd3ec7d0233f968dd88ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Wed, 26 Jul 2023 17:57:41 +0200 Subject: [PATCH 07/13] RouteLinkCapability moved to framework --- src/Framework/Framework/Controls/RouteLink.cs | 13 +++++++++++++ .../Framework}/Controls/RouteLinkCapability.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) rename src/{Adapters/WebForms => Framework/Framework}/Controls/RouteLinkCapability.cs (94%) diff --git a/src/Framework/Framework/Controls/RouteLink.cs b/src/Framework/Framework/Controls/RouteLink.cs index 355d0233b4..d5f5b4c804 100644 --- a/src/Framework/Framework/Controls/RouteLink.cs +++ b/src/Framework/Framework/Controls/RouteLink.cs @@ -64,11 +64,17 @@ public string Text public static readonly DotvvmProperty TextProperty = DotvvmProperty.Register(c => c.Text, ""); + /// + /// Gets or sets a collection of parameters to be substituted in the route URL. If the current route contains a parameter with the same name, its value will be reused unless another value is specified here. + /// [PropertyGroup("Param-")] public VirtualPropertyGroupDictionary Params => new VirtualPropertyGroupDictionary(this, ParamsGroupDescriptor); public static DotvvmPropertyGroup ParamsGroupDescriptor = DotvvmPropertyGroup.Register("Param-", "Params"); + /// + /// Gets or sets a collection of parameters to be added in the query string. + /// [PropertyGroup("Query-")] public VirtualPropertyGroupDictionary QueryParameters => new VirtualPropertyGroupDictionary(this, QueryParametersGroupDescriptor); public static DotvvmPropertyGroup QueryParametersGroupDescriptor = @@ -87,6 +93,13 @@ public TextOrContentCapability TextOrContentCapability } ); + public RouteLinkCapability RouteLinkCapability + { + get => (RouteLinkCapability)RouteLinkCapabilityProperty.GetValue(this); + set => RouteLinkCapabilityProperty.SetValue(this, value); + } + public static readonly DotvvmCapabilityProperty RouteLinkCapabilityProperty = DotvvmCapabilityProperty.RegisterCapability(); + public RouteLink() : base("a", false) { } diff --git a/src/Adapters/WebForms/Controls/RouteLinkCapability.cs b/src/Framework/Framework/Controls/RouteLinkCapability.cs similarity index 94% rename from src/Adapters/WebForms/Controls/RouteLinkCapability.cs rename to src/Framework/Framework/Controls/RouteLinkCapability.cs index 8b5597d962..2e7ced5419 100644 --- a/src/Adapters/WebForms/Controls/RouteLinkCapability.cs +++ b/src/Framework/Framework/Controls/RouteLinkCapability.cs @@ -2,7 +2,7 @@ using System.ComponentModel; using DotVVM.Framework.Binding; -namespace DotVVM.Adapters.WebForms.Controls +namespace DotVVM.Framework.Controls { [DotvvmControlCapability()] public sealed class RouteLinkCapability From c753ca1c33a77752aebeed41d405bd646050e80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Wed, 26 Jul 2023 19:00:21 +0200 Subject: [PATCH 08/13] Extension method moved to a different namespace so the using will not be necessary --- src/Adapters/WebForms/DotvvmConfigurationExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs b/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs index bc366ee25f..a5e6cbd2bc 100644 --- a/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs +++ b/src/Adapters/WebForms/DotvvmConfigurationExtensions.cs @@ -4,9 +4,9 @@ using System.Text; using System.Threading.Tasks; using DotVVM.Adapters.WebForms.Controls; -using DotVVM.Framework.Configuration; -namespace DotVVM.Adapters.WebForms +// ReSharper disable once CheckNamespace +namespace DotVVM.Framework.Configuration { public static class DotvvmConfigurationExtensions { From 47fa35ce55ff8774e92704664e34b211cad2f9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 28 Jul 2023 14:14:29 +0200 Subject: [PATCH 09/13] Fixed warning and test errors in RouteLinkCapability --- .../Framework/Controls/RouteLinkCapability.cs | 11 +++++------ ...tionSerializationTests.SerializeDefaultConfig.json | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Framework/Framework/Controls/RouteLinkCapability.cs b/src/Framework/Framework/Controls/RouteLinkCapability.cs index 2e7ced5419..e80e18bca8 100644 --- a/src/Framework/Framework/Controls/RouteLinkCapability.cs +++ b/src/Framework/Framework/Controls/RouteLinkCapability.cs @@ -5,20 +5,19 @@ namespace DotVVM.Framework.Controls { [DotvvmControlCapability()] - public sealed class RouteLinkCapability + public sealed record RouteLinkCapability { [PropertyGroup("Query-")] [DefaultValue(null)] - public IReadOnlyDictionary> QueryParameters { get; private set; } = new Dictionary>(); + public IReadOnlyDictionary> QueryParameters { get; init; } = new Dictionary>(); [PropertyGroup("Param-")] [DefaultValue(null)] - public IReadOnlyDictionary> Params { get; private set; } = new Dictionary>(); + public IReadOnlyDictionary> Params { get; init; } = new Dictionary>(); - public string RouteName { get; private set; } + public string RouteName { get; init; } = null!; [DefaultValue(null)] - public ValueOrBinding? UrlSuffix { get; private set; } - + public ValueOrBinding? UrlSuffix { get; init; } } } diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json index 803a7b58c2..825ca33160 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json @@ -1720,6 +1720,9 @@ } }, "DotVVM.Framework.Controls.RouteLink": { + "RouteLinkCapability": { + "type": "DotVVM.Framework.Controls.RouteLinkCapability, DotVVM.Framework" + }, "TextOrContentCapability": { "type": "DotVVM.Framework.Controls.TextOrContentCapability, DotVVM.Framework" } From a3632152397670d5f64f078e7ced3f77c8d58d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 28 Jul 2023 14:47:31 +0200 Subject: [PATCH 10/13] Fixed compile error in tests --- .github/workflows/main.yml | 9 +++++++++ src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs | 1 + 2 files changed, 10 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 15bf44ba88..d1ec15d3ef 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -131,6 +131,15 @@ jobs: # title: Analyzer Tests # github-token: ${{ secrets.GITHUB_TOKEN }} # target-framework: net7.0 + - name: Adapters.WebForms.Tests (net472) + uses: ./.github/unittest + if: matrix.os == 'windows-2022' + with: + project: src/Adapters/Tests/WebForms + name: webforms-adapters-tests + title: WebForms Adapter Tests + github-token: ${{ secrets.GITHUB_TOKEN }} + target-framework: net472 js-tests: runs-on: ubuntu-latest diff --git a/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs index 24b2565aeb..fe3cc1616d 100644 --- a/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs +++ b/src/Adapters/Tests/WebForms/HybridRouteLinkTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using System.Web; using CheckTestOutput; +using DotVVM.Framework.Configuration; using DotVVM.Framework.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; From 59a3431e3b54e6f6ea22e863ab1e0fa7b1728ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 28 Jul 2023 15:33:52 +0200 Subject: [PATCH 11/13] Fixed test project --- .../Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj | 3 ++- src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj index b6c6e34cda..86ef296e36 100644 --- a/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj +++ b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj @@ -9,7 +9,8 @@ - + + diff --git a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs index 19d499d457..b4eb52ba8d 100644 --- a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs +++ b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs @@ -92,14 +92,14 @@ private static string EvaluateRouteUrl(DotvvmControl container, Route webFormsRo // generate the URL return "~/" + webFormsRoute.GetVirtualPath(HttpContext.Current.Request.RequestContext, routeValues)?.VirtualPath - + UrlHelper.BuildUrlSuffix(routeLinkCapability.UrlSuffix?.Evaluate(container), routeLinkCapability.QueryParameters.ToDictionary(p => p.Key, p => p.Value.Evaluate(container))); + + UrlHelper.BuildUrlSuffix(routeLinkCapability.UrlSuffix?.Evaluate(container), routeLinkCapability.QueryParameters.OrderBy(p => p.Key).Select(p => new KeyValuePair(p.Key, p.Value.Evaluate(container)))); } private static string GenerateRouteUrlExpression(DotvvmControl container, Route webFormsRoute, IDictionary> parameters) { var parametersExpression = parameters + .OrderBy(p => p.Key) .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key)}: {p.Value.GetJsExpression(container)}") - .OrderBy(p => p) .StringJoin(","); var routeUrlExpression = $"dotvvm.buildRouteUrl({KnockoutHelper.MakeStringLiteral(webFormsRoute.Url)}, {{{parametersExpression}}})"; return routeUrlExpression; @@ -109,8 +109,8 @@ private static string GenerateUrlSuffixExpression(DotvvmControl container, Route { var urlSuffixBase = routeLinkCapability.UrlSuffix?.GetJsExpression(container) ?? "\"\""; var queryParams = routeLinkCapability.QueryParameters + .OrderBy(p => p.Key) .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key.ToLowerInvariant())}: {p.Value.GetJsExpression(container)}") - .OrderBy(p => p) .StringJoin(","); // generate the function call From 6eaba698fc504c5005213b08abd5165188cd8ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Fri, 28 Jul 2023 16:13:32 +0200 Subject: [PATCH 12/13] Disabled deterministic build in Adapter tests --- .../Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj index 86ef296e36..08d3a2e33f 100644 --- a/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj +++ b/src/Adapters/Tests/WebForms/DotVVM.Adapters.WebForms.Tests.csproj @@ -3,6 +3,9 @@ net472 false + + + false From 33bb0ed25c6a01c47a47987037e32ce3f4c1cd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Herceg?= Date: Sun, 30 Jul 2023 17:13:42 +0200 Subject: [PATCH 13/13] Removed ToLower with query params Used RouteLinkCapability --- ...uteLinkTests.HybridRouteLink_SuffixAndQueryString.html | 6 +++--- src/Adapters/WebForms/Controls/HybridRouteLink.cs | 8 ++------ src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html index f08748f268..7d179a3a4a 100644 --- a/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html +++ b/src/Adapters/Tests/WebForms/testoutputs/HybridRouteLinkTests.HybridRouteLink_SuffixAndQueryString.html @@ -3,8 +3,8 @@ hello 1 hello 2 - hello 3 - hello 4 - hello 5 + hello 3 + hello 4 + hello 5 diff --git a/src/Adapters/WebForms/Controls/HybridRouteLink.cs b/src/Adapters/WebForms/Controls/HybridRouteLink.cs index a82d38722e..0606f75011 100644 --- a/src/Adapters/WebForms/Controls/HybridRouteLink.cs +++ b/src/Adapters/WebForms/Controls/HybridRouteLink.cs @@ -47,14 +47,10 @@ RouteLinkCapability routeLinkCapability private static DotvvmControl GenerateDotvvmRouteLink(HtmlCapability htmlCapability, TextOrContentCapability textOrContent, RouteLinkCapability routeLinkCapability) { - var link = new RouteLink() + return new RouteLink() .SetCapability(htmlCapability) .SetCapability(textOrContent) - .SetProperty(r => r.RouteName, routeLinkCapability.RouteName) - .SetProperty(l => l.UrlSuffix, routeLinkCapability.UrlSuffix); - link.QueryParameters.CopyFrom(routeLinkCapability.QueryParameters); - link.Params.CopyFrom(routeLinkCapability.Params); - return link; + .SetCapability(routeLinkCapability); } } diff --git a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs index b4eb52ba8d..ba730df1d9 100644 --- a/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs +++ b/src/Adapters/WebForms/Controls/WebFormsLinkUtils.cs @@ -110,7 +110,7 @@ private static string GenerateUrlSuffixExpression(DotvvmControl container, Route var urlSuffixBase = routeLinkCapability.UrlSuffix?.GetJsExpression(container) ?? "\"\""; var queryParams = routeLinkCapability.QueryParameters .OrderBy(p => p.Key) - .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key.ToLowerInvariant())}: {p.Value.GetJsExpression(container)}") + .Select(p => $"{KnockoutHelper.MakeStringLiteral(p.Key)}: {p.Value.GetJsExpression(container)}") .StringJoin(","); // generate the function call