From 91dc9cd447559d9245326d5fb295369677d68894 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Sat, 12 Sep 2026 11:05:23 +0200 Subject: [PATCH] Collapse store Url/SecureUrl/SslEnabled onto a single Url A store carried three fields to describe one address - Url, SecureUrl and SslEnabled - and every consumer resolved it the same way, as `SslEnabled ? SecureUrl : Url`. The pair is a leftover from mixed http/https sites, where only login and checkout ran over TLS. Store.Url now holds the full address including the scheme, and it stays on the entity only because emails, sitemap.xml, robots.txt and order PDFs are built outside a request and have no Request.Scheme to read. Forcing HTTPS on an incoming http:// request is left where it already lives - SecurityConfig.UseHttpsRedirection/UseHsts/UseForwardedHeaders and the reverse proxy - not in the store record. The validator accepts any absolute http(s) url, so dev and intranet installations can still run over plain http. MigrationStoreSingleUrl (2.4) copies the primary DomainHost url back onto Store.Url: the installer, the admin store editor and the 1.1 domains migration all wrote the resolved address there, so an installation that ran on HTTPS keeps generating HTTPS links. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GrGY1NnCzdkn7wUxbyHzsX --- .../DotLiquidDrops/LiquidBlogComment.cs | 2 +- .../Messages/DotLiquidDrops/LiquidCustomer.cs | 2 +- .../DotLiquidDrops/LiquidKnowledgebase.cs | 2 +- .../DotLiquidDrops/LiquidMerchandiseReturn.cs | 2 +- .../DotLiquidDrops/LiquidNewsComment.cs | 2 +- .../LiquidNewsLetterSubscription.cs | 2 +- .../Messages/DotLiquidDrops/LiquidOrder.cs | 2 +- .../DotLiquidDrops/LiquidOrderItem.cs | 2 +- .../LiquidOutOfStockSubscription.cs | 2 +- .../Messages/DotLiquidDrops/LiquidProduct.cs | 2 +- .../Messages/DotLiquidDrops/LiquidShipment.cs | 2 +- .../Messages/DotLiquidDrops/LiquidStore.cs | 2 +- .../Common/GetSitemapXMLCommandHandler.cs | 13 +-- .../GetShoppingCartTokensCommandHandler.cs | 2 +- src/Core/Grand.Domain/Stores/Store.cs | 12 +-- .../Grand.Module.Api/DTOs/Common/StoreDto.cs | 2 - .../Services/InstallDataRobotsTxt.cs | 2 +- .../Services/InstallDataStores.cs | 4 +- .../Migrations/1.1/MigrationUpdateStore.cs | 3 +- .../Migrations/2.4/MigrationStoreSingleUrl.cs | 75 ++++++++++++++++++ ...pingTests.StoreModel_ToDomain.verified.txt | 1 - ...monMappingTests.Store_ToModel.verified.txt | 1 - .../AdminShared/CommonMappingTests.cs | 2 - .../Partials/CreateOrUpdate.TabInfo.cshtml | 22 ----- .../Store/Partials/CreateOrUpdate.cshtml | 17 ---- .../Models/Stores/StoreModel.cs | 6 -- .../Services/StoreViewModelService.cs | 16 +--- .../Validators/Stores/StoreValidator.cs | 37 ++------- .../Grand.Web.Common/StoreContextSetter.cs | 2 +- .../PdfTemplates/OrderPdfTemplate.cshtml | 2 +- .../App_Data/Resources/DefaultLanguage.xml | Bin 1544502 -> 1543368 bytes .../App_Data/Resources/Upgrade/en_240.xml | 3 + .../Grand.Web/Controllers/CommonController.cs | 2 +- .../Products/GetProductDetailsPageHandler.cs | 7 +- .../PdfTemplates/OrderPdfTemplate.cshtml | 2 +- 35 files changed, 118 insertions(+), 139 deletions(-) create mode 100644 src/Modules/Grand.Module.Migration/Migrations/2.4/MigrationStoreSingleUrl.cs diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidBlogComment.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidBlogComment.cs index 0cdea9ca07..b75e12d9fd 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidBlogComment.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidBlogComment.cs @@ -24,7 +24,7 @@ public LiquidBlogComment(BlogComment blogComment, BlogPost blogPost, Store store _host = host; _language = language; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidCustomer.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidCustomer.cs index b9e028985d..2defeb4286 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidCustomer.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidCustomer.cs @@ -22,7 +22,7 @@ public LiquidCustomer(Customer customer, Store store, DomainHost host, CustomerN _customerNote = customerNote; _store = store; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidKnowledgebase.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidKnowledgebase.cs index 095a66e932..57e69abed3 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidKnowledgebase.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidKnowledgebase.cs @@ -24,7 +24,7 @@ public LiquidKnowledgebase(KnowledgebaseArticle article, KnowledgebaseArticleCom _host = host; _language = language; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidMerchandiseReturn.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidMerchandiseReturn.cs index 7be28aa7c3..3c15c40e81 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidMerchandiseReturn.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidMerchandiseReturn.cs @@ -25,7 +25,7 @@ public LiquidMerchandiseReturn(MerchandiseReturn merchandiseReturn, Store store, _host = host; _merchandiseReturnNote = merchandiseReturnNote; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); Items = new List(); AdditionalTokens = new Dictionary(); diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsComment.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsComment.cs index eea16d7053..3b41433e13 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsComment.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsComment.cs @@ -24,7 +24,7 @@ public LiquidNewsComment(NewsItem newsItem, NewsComment newsComment, Store store _language = language; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsLetterSubscription.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsLetterSubscription.cs index 509cd034f7..10b5666f80 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsLetterSubscription.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidNewsLetterSubscription.cs @@ -18,7 +18,7 @@ public LiquidNewsLetterSubscription(NewsLetterSubscription subscription, Store s _store = store; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrder.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrder.cs index 4e4f0d1038..08f7fe0766 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrder.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrder.cs @@ -35,7 +35,7 @@ public LiquidOrder(Order order, Customer customer, Language language, Currency c _vendor = vendor; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); OrderItems = new List(); diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrderItem.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrderItem.cs index 8cc9d365ea..9b11dbd16d 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrderItem.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOrderItem.cs @@ -30,7 +30,7 @@ public LiquidOrderItem(OrderItem orderItem, Product product, Language language, _vendor = vendor; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOutOfStockSubscription.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOutOfStockSubscription.cs index deb4f82e62..db33a9bd63 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOutOfStockSubscription.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidOutOfStockSubscription.cs @@ -23,7 +23,7 @@ public LiquidOutOfStockSubscription(Product product, OutOfStockSubscription outO _language = language; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidProduct.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidProduct.cs index bfc6fc8c8c..45ff0fedf7 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidProduct.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidProduct.cs @@ -20,7 +20,7 @@ public LiquidProduct(Product product, Language language, Store store, DomainHost _language = language; _store = store; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); AdditionalTokens = new Dictionary(); } diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidShipment.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidShipment.cs index ffb36fe65b..12478f83fc 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidShipment.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidShipment.cs @@ -23,7 +23,7 @@ public LiquidShipment(Shipment shipment, Order order, Store store, DomainHost ho _store = store; _order = order; _host = host; - url = _host?.Url.Trim('/') ?? (_store.SslEnabled ? _store.SecureUrl.Trim('/') : _store.Url.Trim('/')); + url = _host?.Url.Trim('/') ?? _store.Url.Trim('/'); ShipmentItems = new List(); AdditionalTokens = new Dictionary(); diff --git a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidStore.cs b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidStore.cs index ab0b583565..6dfee08c82 100644 --- a/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidStore.cs +++ b/src/Business/Grand.Business.Core/Utilities/Messages/DotLiquidDrops/LiquidStore.cs @@ -24,7 +24,7 @@ public LiquidStore(Store store, Language language, EmailAccount emailAccount = n public string Shortcut => _store.Shortcut; - public string URL => _store.SslEnabled ? _store.SecureUrl : _store.Url; + public string URL => _store.Url; public string Email => _emailAccount.Email; diff --git a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs index b521710f56..1a32101a08 100644 --- a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs +++ b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs @@ -106,7 +106,7 @@ private async Task Generate(Stream stream, Language language, Store store) /// Protocol name as string private string GetHttpProtocol() { - return _request.Store.SslEnabled ? "https" : "http"; + return GetStoreUri().Scheme; } /// @@ -115,9 +115,7 @@ private string GetHttpProtocol() /// Protocol name as string private HostString GetHost() { - return _request.Store.SslEnabled - ? new HostString(_request.Store.SecureUrl.Replace("https://", "").Trim('/')) - : new HostString(_request.Store.Url.Replace("http://", "").Trim('/')); + return new HostString(GetStoreUri().Authority); } /// @@ -126,7 +124,12 @@ private HostString GetHost() /// Store url private string GetStoreLocation() { - return _request.Store.SslEnabled ? _request.Store.SecureUrl : _request.Store.Url; + return _request.Store.Url; + } + + private Uri GetStoreUri() + { + return new Uri(_request.Store.Url); } private async Task> GenerateUrls(Language language, Store store) diff --git a/src/Business/Grand.Business.Messages/Commands/Handlers/Messages/GetShoppingCartTokensCommandHandler.cs b/src/Business/Grand.Business.Messages/Commands/Handlers/Messages/GetShoppingCartTokensCommandHandler.cs index 0edac22ad6..2b1e0827d7 100644 --- a/src/Business/Grand.Business.Messages/Commands/Handlers/Messages/GetShoppingCartTokensCommandHandler.cs +++ b/src/Business/Grand.Business.Messages/Commands/Handlers/Messages/GetShoppingCartTokensCommandHandler.cs @@ -41,7 +41,7 @@ async Task ProductList() product.ProductPictures.OrderByDescending(p => p.IsDefault) .ThenBy(p => p.DisplayOrder) .FirstOrDefault()?.PictureId, 100, - storeLocation: request.Store.SslEnabled ? request.Store.SecureUrl : request.Store.Url); + storeLocation: request.Store.Url); //attributes if (item.Attributes != null && item.Attributes.Any()) diff --git a/src/Core/Grand.Domain/Stores/Store.cs b/src/Core/Grand.Domain/Stores/Store.cs index f1cb7a6615..95b18b0031 100644 --- a/src/Core/Grand.Domain/Stores/Store.cs +++ b/src/Core/Grand.Domain/Stores/Store.cs @@ -18,20 +18,10 @@ public class Store : BaseEntity, ITranslationEntity public string Shortcut { get; set; } /// - /// Gets or sets the store URL + /// Gets or sets the store URL, including the scheme /// public string Url { get; set; } - /// - /// Gets or sets a value indicating whether SSL is enabled - /// - public bool SslEnabled { get; set; } - - /// - /// Gets or sets the store secure URL (HTTPS) - /// - public string SecureUrl { get; set; } - /// /// Gets or sets the list of domains /// diff --git a/src/Modules/Grand.Module.Api/DTOs/Common/StoreDto.cs b/src/Modules/Grand.Module.Api/DTOs/Common/StoreDto.cs index 491f7031b0..e939ccc4b8 100644 --- a/src/Modules/Grand.Module.Api/DTOs/Common/StoreDto.cs +++ b/src/Modules/Grand.Module.Api/DTOs/Common/StoreDto.cs @@ -6,8 +6,6 @@ public class StoreDto : BaseApiEntityModel { public string Name { get; set; } public string Url { get; set; } - public bool SslEnabled { get; set; } - public string SecureUrl { get; set; } public string Hosts { get; set; } public string DefaultLanguageId { get; set; } public string DefaultWarehouseId { get; set; } diff --git a/src/Modules/Grand.Module.Installer/Services/InstallDataRobotsTxt.cs b/src/Modules/Grand.Module.Installer/Services/InstallDataRobotsTxt.cs index 75c76cdaf7..3f615d11fe 100644 --- a/src/Modules/Grand.Module.Installer/Services/InstallDataRobotsTxt.cs +++ b/src/Modules/Grand.Module.Installer/Services/InstallDataRobotsTxt.cs @@ -8,7 +8,7 @@ public partial class InstallationService protected virtual async Task InstallDataRobotsTxt( Store store) { - var url = store.SslEnabled ? store.SecureUrl : store.Url; + var url = store.Url; var robotsTxt = new RobotsTxt { Name = "RobotsTXT", diff --git a/src/Modules/Grand.Module.Installer/Services/InstallDataStores.cs b/src/Modules/Grand.Module.Installer/Services/InstallDataStores.cs index 7903083945..2e047e9df8 100644 --- a/src/Modules/Grand.Module.Installer/Services/InstallDataStores.cs +++ b/src/Modules/Grand.Module.Installer/Services/InstallDataStores.cs @@ -13,9 +13,7 @@ protected virtual async Task InstallStores( new Store { Name = "Your store name", Shortcut = "Store", - Url = $"http://{host}/", - SecureUrl = $"https://{host}/", - SslEnabled = httpscheme.ToLowerInvariant() == "https", + Url = $"{httpscheme}://{host}/", DisplayOrder = 1, CompanyName = companyName, CompanyAddress = companyAddress, diff --git a/src/Modules/Grand.Module.Migration/Migrations/1.1/MigrationUpdateStore.cs b/src/Modules/Grand.Module.Migration/Migrations/1.1/MigrationUpdateStore.cs index 6c9e5cddb4..b141608005 100644 --- a/src/Modules/Grand.Module.Migration/Migrations/1.1/MigrationUpdateStore.cs +++ b/src/Modules/Grand.Module.Migration/Migrations/1.1/MigrationUpdateStore.cs @@ -29,10 +29,9 @@ public bool UpgradeProcess(IServiceProvider serviceProvider) if (!store.Domains.Any()) { var storeUri = new Uri(store.Url); - var httpscheme = store.SslEnabled ? "https" : "http"; var domain = new DomainHost { HostName = storeUri.Authority, - Url = $"{httpscheme}://{storeUri.Authority}", + Url = $"{storeUri.Scheme}://{storeUri.Authority}", Primary = true }; store.Domains.Add(domain); diff --git a/src/Modules/Grand.Module.Migration/Migrations/2.4/MigrationStoreSingleUrl.cs b/src/Modules/Grand.Module.Migration/Migrations/2.4/MigrationStoreSingleUrl.cs new file mode 100644 index 0000000000..4f8a586b31 --- /dev/null +++ b/src/Modules/Grand.Module.Migration/Migrations/2.4/MigrationStoreSingleUrl.cs @@ -0,0 +1,75 @@ +using Grand.Data; +using Grand.Domain.Stores; +using Grand.Infrastructure.Migrations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Grand.Module.Migration.Migrations._2._4; + +/// +/// Collapses the store address onto a single . Until 2.4 a store carried +/// Url, SecureUrl and SslEnabled, and every consumer resolved the effective +/// address as SslEnabled ? SecureUrl : Url. Both extra fields are gone - Url now holds +/// the full address including the scheme, and forcing HTTPS belongs to the host (UseHttpsRedirection, +/// UseHsts, the reverse proxy), not to the store record. +/// The effective address of an existing installation is already stored on the primary +/// : the installer, the admin store editor and the 1.1 domains migration all +/// wrote exactly that resolved value there. This copies it back onto Url, so a store that ran on +/// HTTPS keeps generating HTTPS links in emails, sitemap.xml and robots.txt. +/// The now unmapped SslEnabled and SecureUrl elements are left in the documents - the +/// global IgnoreExtraElementsConvention skips them on read, and they disappear the next time the store +/// is saved. +/// +public class MigrationStoreSingleUrl : IMigration +{ + public int Priority => 1; + public DbVersion Version => new(2, 4); + public Guid Identity => new("9E3C7A21-08B4-4F6D-B5A2-7D14C6E39F80"); + public string Name => "Collapse store Url/SecureUrl/SslEnabled onto a single Url 2.4"; + + /// + /// Upgrade process + /// + /// + /// + public bool UpgradeProcess(IServiceProvider serviceProvider) + { + var repository = serviceProvider.GetRequiredService>(); + var logService = serviceProvider.GetRequiredService>(); + + try + { + foreach (var store in repository.Table.ToList()) + { + var effectiveUrl = store.Domains?.FirstOrDefault(x => x.Primary)?.Url; + if (string.IsNullOrWhiteSpace(effectiveUrl) || + !Uri.TryCreate(effectiveUrl.Trim(), UriKind.Absolute, out _)) + { + //nothing reliable to fall back to - the store keeps whatever Url it had + logService.LogWarning( + "Store {StoreName} has no usable primary domain - its Url was left as {StoreUrl}. Verify it in the admin area, it is used to build links in emails and sitemap.xml", + store.Name, store.Url); + continue; + } + + //the 1.1 domains migration wrote the primary domain without a trailing slash, while the store + //url is concatenated with relative paths (robots.txt, sitemap.xml) + effectiveUrl = effectiveUrl.Trim(); + if (!effectiveUrl.EndsWith("/")) + effectiveUrl += "/"; + + if (effectiveUrl == store.Url) + continue; + + store.Url = effectiveUrl; + repository.Update(store); + } + } + catch (Exception ex) + { + logService.LogError(ex, "UpgradeProcess - MigrationStoreSingleUrl (2.4)"); + } + + return true; + } +} diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt index 28ae0fd58e..f4836ab06c 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.StoreModel_ToDomain.verified.txt @@ -1,7 +1,6 @@ { Name: Grand Store, Url: https://grandstore.com, - SslEnabled: true, DefaultLanguageId: lang-001, DisplayOrder: 1, CompanyName: Grand LLC, diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt index 37baa26338..b43b3257c6 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.Store_ToModel.verified.txt @@ -1,7 +1,6 @@ { Name: Grand Store, Url: https://grandstore.com, - SslEnabled: true, DisplayOrder: 1, CompanyName: Grand LLC, CompanyAddress: 123 Commerce St, diff --git a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs index 19729d55fd..af7fcfc19a 100644 --- a/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs +++ b/src/Tests/Grand.Mapping.Tests/AdminShared/CommonMappingTests.cs @@ -341,7 +341,6 @@ public Task Store_ToModel() Id = "store-001", Name = "Grand Store", Url = "https://grandstore.com", - SslEnabled = true, CompanyName = "Grand LLC", CompanyAddress = "123 Commerce St", CompanyPhoneNumber = "+1-555-9999", @@ -362,7 +361,6 @@ public Task StoreModel_ToDomain() var model = new StoreModel { Name = "Grand Store", Url = "https://grandstore.com", - SslEnabled = true, CompanyName = "Grand LLC", DefaultLanguageId = "lang-001", DisplayOrder = 1 diff --git a/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.TabInfo.cshtml b/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.TabInfo.cshtml index e181df810c..c19cc9969c 100644 --- a/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.TabInfo.cshtml +++ b/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.TabInfo.cshtml @@ -48,28 +48,6 @@ -
- -
- - -
-
-
-
- @Loc["Admin.Configuration.Stores.Fields.SslEnabled.Hint2"] -
-
-
- -
- - -
-
diff --git a/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.cshtml b/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.cshtml index d23d6f02ff..e690ed9bd7 100644 --- a/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.cshtml +++ b/src/Web/Grand.Web.Admin/Areas/Admin/Views/Store/Partials/CreateOrUpdate.cshtml @@ -1,22 +1,5 @@ @model StoreModel -
diff --git a/src/Web/Grand.Web.AdminShared/Models/Stores/StoreModel.cs b/src/Web/Grand.Web.AdminShared/Models/Stores/StoreModel.cs index aac7946582..711fccd981 100644 --- a/src/Web/Grand.Web.AdminShared/Models/Stores/StoreModel.cs +++ b/src/Web/Grand.Web.AdminShared/Models/Stores/StoreModel.cs @@ -16,12 +16,6 @@ public class StoreModel : BaseEntityModel, ILocalizedModel [GrandResourceDisplayName("Admin.Configuration.Stores.Fields.Url")] public string Url { get; set; } - [GrandResourceDisplayName("Admin.Configuration.Stores.Fields.SslEnabled")] - public virtual bool SslEnabled { get; set; } - - [GrandResourceDisplayName("Admin.Configuration.Stores.Fields.SecureUrl")] - public virtual string SecureUrl { get; set; } - [GrandResourceDisplayName("Admin.Configuration.Stores.Fields.DisplayOrder")] public int DisplayOrder { get; set; } diff --git a/src/Web/Grand.Web.AdminShared/Services/StoreViewModelService.cs b/src/Web/Grand.Web.AdminShared/Services/StoreViewModelService.cs index f906fea074..73b2463035 100644 --- a/src/Web/Grand.Web.AdminShared/Services/StoreViewModelService.cs +++ b/src/Web/Grand.Web.AdminShared/Services/StoreViewModelService.cs @@ -114,14 +114,11 @@ public virtual async Task InsertStoreModel(StoreModel model) if (!store.Url.Trim().EndsWith("/")) store.Url = store.Url.Trim() + "/"; - if (!string.IsNullOrEmpty(store.SecureUrl) && !store.SecureUrl.Trim().EndsWith("/")) - store.SecureUrl = store.SecureUrl.Trim() + "/"; - var storeUri = new Uri(store.Url); store.Domains.Add(new DomainHost { HostName = storeUri.Host, - Url = store.SslEnabled ? store.SecureUrl : store.Url, + Url = store.Url, Primary = true }); @@ -136,24 +133,19 @@ public virtual async Task UpdateStoreModel(Store store, StoreModel model) //ensure we have "/" at the end if (!store.Url.Trim().EndsWith("/")) store.Url = store.Url.Trim() + "/"; - if (!string.IsNullOrEmpty(store.SecureUrl) && !store.SecureUrl.Trim().EndsWith("/")) - store.SecureUrl = store.SecureUrl.Trim() + "/"; + var storeUri = new Uri(store.Url); var domain = store.Domains.FirstOrDefault(x => x.Primary); if (domain == null) - { - var storeUri = new Uri(store.Url); store.Domains.Add(new DomainHost { HostName = storeUri.Host, - Url = store.SslEnabled ? store.SecureUrl : store.Url, + Url = store.Url, Primary = true }); - } else { - var storeUri = new Uri(store.Url); domain.HostName = storeUri.Host; - domain.Url = store.SslEnabled ? store.SecureUrl : store.Url; + domain.Url = store.Url; } await _storeService.UpdateStore(store); diff --git a/src/Web/Grand.Web.AdminShared/Validators/Stores/StoreValidator.cs b/src/Web/Grand.Web.AdminShared/Validators/Stores/StoreValidator.cs index 3d14a7e858..52f4625419 100644 --- a/src/Web/Grand.Web.AdminShared/Validators/Stores/StoreValidator.cs +++ b/src/Web/Grand.Web.AdminShared/Validators/Stores/StoreValidator.cs @@ -20,38 +20,13 @@ public StoreValidator( .WithMessage(translationService.GetResource("Admin.Configuration.Stores.Fields.Url.Required")); RuleFor(x => x.Url).Must((x, _, _) => { - try - { - var uri = new Uri(x.Url); - return uri != null; - } - catch - { + //the store url is used to build absolute links outside of a request (emails, sitemap, robots.txt), + //so it has to be an absolute http(s) url + if (!Uri.TryCreate(x.Url, UriKind.Absolute, out var uri)) return false; - } - }).WithMessage(translationService.GetResource("Admin.Configuration.Stores.Fields.Url.WrongFormat")); - RuleFor(x => x.SecureUrl).Must((x, _, _) => - { - try - { - if (!x.SslEnabled) - return true; - - var sslUri = new Uri(x.SecureUrl); - - if (!sslUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) - return false; - var storeUri = new Uri(x.Url); - if (sslUri.Host != storeUri.Host) - return false; - - return true; - } - catch - { - return false; - } - }).WithMessage(translationService.GetResource("Admin.Configuration.Stores.Fields.SecureUrl.WrongFormat")); + return uri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) || + uri.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase); + }).WithMessage(translationService.GetResource("Admin.Configuration.Stores.Fields.Url.WrongFormat")); } } \ No newline at end of file diff --git a/src/Web/Grand.Web.Common/StoreContextSetter.cs b/src/Web/Grand.Web.Common/StoreContextSetter.cs index 337ed8f261..93d27dee4f 100644 --- a/src/Web/Grand.Web.Common/StoreContextSetter.cs +++ b/src/Web/Grand.Web.Common/StoreContextSetter.cs @@ -71,7 +71,7 @@ protected DomainHost CurrentHost(Store store) if (store != null) return store.HostValue(host) ?? new DomainHost { Id = int.MinValue.ToString(), - Url = store.SslEnabled ? store.SecureUrl : store.Url, + Url = store.Url, HostName = "temporary-store" }; diff --git a/src/Web/Grand.Web.Vendor/Areas/Vendor/Views/PdfTemplates/OrderPdfTemplate.cshtml b/src/Web/Grand.Web.Vendor/Areas/Vendor/Views/PdfTemplates/OrderPdfTemplate.cshtml index e20589acff..a13dc71806 100644 --- a/src/Web/Grand.Web.Vendor/Areas/Vendor/Views/PdfTemplates/OrderPdfTemplate.cshtml +++ b/src/Web/Grand.Web.Vendor/Areas/Vendor/Views/PdfTemplates/OrderPdfTemplate.cshtml @@ -126,7 +126,7 @@

@store.CompanyName

if (logoExists) { - var url = await _pictureService.GetPictureUrl(logoPicture, 0, false, store.SslEnabled ? store.SecureUrl : store.Url); + var url = await _pictureService.GetPictureUrl(logoPicture, 0, false, store.Url);
logo
diff --git a/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml b/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml index f460e530fe1ca105b7eb2be4ed1844144d2bfbb9..3fc6e2659486b0287c61bf3be2628884e481e8a0 100644 GIT binary patch delta 191 zcmWN@y$-=(6b9hamQVe))oK@m&4#-$n0f_`L^_Iu8camK#9}`Yn@EV2=x$*!n-Pn| zPqQJYo(w-+@0U6LC3pvO` V0g7ON2^N&V1_#RE#>v-fZT|b-R4f1h delta 445 zcmX>xGj7|WxP}(S7N!>F7M2#)Eo@DXI8zyt8A=(77*eM@KIF8W9UU zO^z8P`aXhLa(Y+*pP6(xgCo#dKL$@Ab_ZIbz~BNjRDmInA%D8y5e{MgOrZS=K!4={ z=}e&3{zzs~)?fypk<;B z_JSvD%b4`Bd5uVq9cSZdw|Kz@#Oy%K0mPg@%mu{UKn#-O1!6uR<_BT{AQl8-As`k8 PVi6z~-EQ$hY{C)%2`7Ir diff --git a/src/Web/Grand.Web/App_Data/Resources/Upgrade/en_240.xml b/src/Web/Grand.Web/App_Data/Resources/Upgrade/en_240.xml index 05aa4dab58..719c4b60a5 100644 --- a/src/Web/Grand.Web/App_Data/Resources/Upgrade/en_240.xml +++ b/src/Web/Grand.Web/App_Data/Resources/Upgrade/en_240.xml @@ -48,4 +48,7 @@ You can't edit this gift voucher, because it can be used in many stores. + + Wrong format of the URL - provide the full address including the scheme (e.g. https://yourstore.com/) + diff --git a/src/Web/Grand.Web/Controllers/CommonController.cs b/src/Web/Grand.Web/Controllers/CommonController.cs index 43ed98a576..e0a36bd6cd 100644 --- a/src/Web/Grand.Web/Controllers/CommonController.cs +++ b/src/Web/Grand.Web/Controllers/CommonController.cs @@ -261,7 +261,7 @@ public virtual async Task SetStore( await _mediator.Publish(new ChangeStoreEvent(_contextAccessor.WorkContext.CurrentCustomer, selectedstore)); if (selectedstore.Url != _contextAccessor.StoreContext.CurrentStore.Url) - return Redirect(selectedstore.SslEnabled ? selectedstore.SecureUrl : selectedstore.Url); + return Redirect(selectedstore.Url); } } diff --git a/src/Web/Grand.Web/Features/Handlers/Products/GetProductDetailsPageHandler.cs b/src/Web/Grand.Web/Features/Handlers/Products/GetProductDetailsPageHandler.cs index 190fe63704..0b02a83a2e 100644 --- a/src/Web/Grand.Web/Features/Handlers/Products/GetProductDetailsPageHandler.cs +++ b/src/Web/Grand.Web/Features/Handlers/Products/GetProductDetailsPageHandler.cs @@ -171,12 +171,7 @@ private async Task PrepareProductDetailsModel(Domain.Stores if (_catalogSettings.ShowShareButton && !string.IsNullOrEmpty(_catalogSettings.PageShareCode)) { - var shareCode = _catalogSettings.PageShareCode; - if (store.SslEnabled) - //need to change the add this link to be https linked when the page is, so that the page doesnt ask about mixed mode when viewed in https... - shareCode = shareCode.Replace("http://", "https://"); - - model.PageShareCode = shareCode; + model.PageShareCode = _catalogSettings.PageShareCode; } #endregion diff --git a/src/Web/Grand.Web/Views/PdfTemplates/OrderPdfTemplate.cshtml b/src/Web/Grand.Web/Views/PdfTemplates/OrderPdfTemplate.cshtml index 208ff6cc7c..f6de98c094 100644 --- a/src/Web/Grand.Web/Views/PdfTemplates/OrderPdfTemplate.cshtml +++ b/src/Web/Grand.Web/Views/PdfTemplates/OrderPdfTemplate.cshtml @@ -126,7 +126,7 @@

@store.CompanyName

if (logoExists) { - var url = await _pictureService.GetPictureUrl(logoPicture, 0, false, store.SslEnabled ? store.SecureUrl : store.Url); + var url = await _pictureService.GetPictureUrl(logoPicture, 0, false, store.Url);
logo