From 3f893b7c06701e4cf4dade9251e3438c5de9d7a4 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 9 Jan 2019 16:09:35 +0100 Subject: [PATCH 001/575] Minor change --- src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs index 23cada0323..f00c5b76c3 100644 --- a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs +++ b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs @@ -98,9 +98,10 @@ public void Clear() Currencies.Clear(); StoreMappings.Clear(); CustomerRoleMappings.Clear(); - DeliveryTimes.Clear(); Manufacturers.Clear(); Categories.Clear(); + DeliveryTimes.Clear(); + Translations.Clear(); CustomProperties.Clear(); } } From 8a272bfd5815aff6741e00a04c8119ae8dc41279 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 9 Jan 2019 16:19:59 +0100 Subject: [PATCH 002/575] Range slider should trigger change for hidden element --- .../SmartStore.Web/Scripts/smartstore.rangeslider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Scripts/smartstore.rangeslider.js b/src/Presentation/SmartStore.Web/Scripts/smartstore.rangeslider.js index 56fa50b0d7..988a29c989 100644 --- a/src/Presentation/SmartStore.Web/Scripts/smartstore.rangeslider.js +++ b/src/Presentation/SmartStore.Web/Scripts/smartstore.rangeslider.js @@ -51,7 +51,7 @@ var g = SmartStore.globalization, nf = g.culture.numberFormat, formatted = val.replace('.', nf["."]); - $(el.data('target')).val(formatted); + $(el.data('target')).val(formatted).trigger('change'); } refreshBubblePosition(e, wrapper, el, bubble); From b3b4ad7915af1229a35b8207d33fbd759cfbebeb Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 9 Jan 2019 21:59:15 +0100 Subject: [PATCH 003/575] Theming: blur backdrop-filter for .modal-backdrop --- src/Presentation/SmartStore.Web/Content/shared/_modal.scss | 4 ++++ src/Presentation/SmartStore.Web/Global.asax | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Content/shared/_modal.scss b/src/Presentation/SmartStore.Web/Content/shared/_modal.scss index dc62ad85a1..4457d44872 100644 --- a/src/Presentation/SmartStore.Web/Content/shared/_modal.scss +++ b/src/Presentation/SmartStore.Web/Content/shared/_modal.scss @@ -6,9 +6,13 @@ $modal-transition-timing: cubic-bezier(0.165, 0.84, 0.44, 1); // easeOutQuart //$modal-transition-timing: cubic-bezier(0.19, 1, 0.22, 1); // easeOutExpo + // FLEX MODAL // ------------------------------------ +.modal-backdrop.show { + backdrop-filter: blur(3px); +} .modal-backdrop.fade { // fade out diff --git a/src/Presentation/SmartStore.Web/Global.asax b/src/Presentation/SmartStore.Web/Global.asax index ebaeaa3f40..612debf2a2 100644 --- a/src/Presentation/SmartStore.Web/Global.asax +++ b/src/Presentation/SmartStore.Web/Global.asax @@ -1 +1 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="SmartStore.Web.MvcApplication" Language="C#" %> +<%@ Application Codebehind="Global.asax.cs" Inherits="SmartStore.Web.MvcApplication" Language="C#" %> From f74e2ca556f6d648f1698bb5b81cb976411eb795 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 10 Jan 2019 00:41:29 +0100 Subject: [PATCH 004/575] Theming: trigger tooltip on hover only --- .../Administration/Scripts/admin.globalinit.js | 3 ++- src/Presentation/SmartStore.Web/Scripts/public.common.js | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Scripts/admin.globalinit.js b/src/Presentation/SmartStore.Web/Administration/Scripts/admin.globalinit.js index 100a131109..a7d89d9e5c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Scripts/admin.globalinit.js +++ b/src/Presentation/SmartStore.Web/Administration/Scripts/admin.globalinit.js @@ -112,7 +112,8 @@ }); $("#page").tooltip({ - selector: "a[rel=tooltip], .tooltip-toggle" + selector: "a[rel=tooltip], .tooltip-toggle", + trigger: 'hover' }); // Temp only diff --git a/src/Presentation/SmartStore.Web/Scripts/public.common.js b/src/Presentation/SmartStore.Web/Scripts/public.common.js index 90a925b795..d3fc90055a 100644 --- a/src/Presentation/SmartStore.Web/Scripts/public.common.js +++ b/src/Presentation/SmartStore.Web/Scripts/public.common.js @@ -34,7 +34,11 @@ if ($.fn.tooltip === undefined) return; if (!Modernizr.touchevents) { - ctx.tooltip({ selector: '[data-toggle=tooltip], .tooltip-toggle', animation: false }); + ctx.tooltip({ + selector: '[data-toggle=tooltip], .tooltip-toggle', + animation: false, + trigger: 'hover' + }); } }, // touch spin From 4c9400632ef00ab550d6450a4927d9933d44befd Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 10 Jan 2019 02:31:32 +0100 Subject: [PATCH 005/575] IconExplorer: generate CSS class --- .../UI/Icons/IconDescription.cs | 51 ++++++++++++++----- .../UI/Icons/IconExplorer.cs | 28 +++++++++- .../SmartStore.Web/Content/shared/_modal.scss | 4 +- .../Shared/Partials/FontAwesomeIcon.cshtml | 21 ++++---- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconDescription.cs b/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconDescription.cs index 6d791ca978..01f95a80f1 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconDescription.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconDescription.cs @@ -24,29 +24,52 @@ public class IconDescription [JsonIgnore] public bool IsBrandIcon { - get - { - if (Styles != null && Styles.Length == 1) - { - return Styles[0] == "brands"; - } - - return false; - } + get; + internal set; } [JsonIgnore] public bool HasRegularStyle { - get + get; + internal set; + } + + [JsonIgnore] + public bool IsPro + { + get; + internal set; + } + + public string GetCssClass(string style) + { + var prefix = "fa"; + + if (IsBrandIcon) { - if (Styles != null) + prefix = "fab"; + } + else + { + switch (style) { - return Styles.Contains("regular"); + case "solid": + case "fas": + prefix = "fas"; + break; + case "regular": + case "far": + prefix = "far"; + break; + case "light": + case "fal": + prefix = "fal"; + break; } - - return false; } + + return string.Concat(prefix, " fa-", Name); } public class Search diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconExplorer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconExplorer.cs index 77fa7ea653..fcda23274b 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconExplorer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Icons/IconExplorer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Linq; using Newtonsoft.Json; using SmartStore.Collections; using SmartStore.Core; @@ -63,6 +64,20 @@ private void EnsureIsLoaded() value.Name = key; + // Styles + var styles = value.Styles; + if (styles != null) + { + if (styles.Length == 1 && styles[0] == "brands") + { + value.IsBrandIcon = true; + } + else if (styles.Contains("regular")) + { + value.HasRegularStyle = true; + } + } + if (value.SearchInfo?.Terms?.Length > 0) { foreach (var term in value.SearchInfo.Terms) @@ -127,7 +142,18 @@ public IconDescription GetIconByName(string name) Guard.NotEmpty(name, nameof(name)); EnsureIsLoaded(); - _icons.TryGetValue(name, out var description); + if (!_icons.TryGetValue(name, out var description)) + { + description = new IconDescription + { + IsPro = true, + Name = name, + Label = name, + Styles = new[] { "solid", "regular", "light" }, + HasRegularStyle = true, + }; + } + return description; } diff --git a/src/Presentation/SmartStore.Web/Content/shared/_modal.scss b/src/Presentation/SmartStore.Web/Content/shared/_modal.scss index 4457d44872..566c385e77 100644 --- a/src/Presentation/SmartStore.Web/Content/shared/_modal.scss +++ b/src/Presentation/SmartStore.Web/Content/shared/_modal.scss @@ -10,9 +10,9 @@ $modal-transition-timing: cubic-bezier(0.165, 0.84, 0.44, 1); // easeOutQuart // FLEX MODAL // ------------------------------------ -.modal-backdrop.show { +/*.modal-backdrop.show { backdrop-filter: blur(3px); -} +}*/ .modal-backdrop.fade { // fade out diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml index 29d8b9a070..cf41000b4e 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml @@ -12,9 +12,9 @@ } } -
+
- @if (isCustom) { } @@ -53,16 +52,16 @@
-
From 7c052b9ebf8e727ca661f8ba10c2eaa2f2b95c9f Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 10 Jan 2019 03:09:22 +0100 Subject: [PATCH 006/575] Theming: minor fixes --- .../Views/Shared/Partials/FontAwesomeIcon.cshtml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml index cf41000b4e..660090c80a 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Partials/FontAwesomeIcon.cshtml @@ -44,7 +44,7 @@ terms = JsonConvert.SerializeObject(icon.SearchInfo.Terms); } - } @@ -52,16 +52,16 @@
-
From ab06b2e53c1e4188dd4d4066f7d6a6607626e902 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 10 Jan 2019 12:13:27 +0100 Subject: [PATCH 007/575] Search: removed obsolete AcquirementReason.Optimizing --- src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs index f00c5b76c3..e0df140361 100644 --- a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs +++ b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs @@ -11,7 +11,6 @@ namespace SmartStore.Core.Search public enum AcquirementReason { Indexing, - Optimizing, Deleting } From 034ec4e04fd127bddb34e5a6f462344883937610 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Fri, 11 Jan 2019 12:01:36 +0100 Subject: [PATCH 008/575] Fixes the redirection to the homepage for pages which are loaded while the application is restarted --- changelog.md | 3 ++- .../SmartStore.Web.Framework/WebStoreContext.cs | 6 +++--- .../Infrastructure/Installation/InstallDataSeeder.cs | 6 ++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index fc15d24f94..b19e41de11 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,4 @@ -# Release Notes +# Release Notes ## SmartStore.NET 3.2 @@ -104,6 +104,7 @@ * Debitoor: Wrong invoice total if a subtotal discount has been applied. * Import: Fixes invalid conversion "System.Double" to "SmartStore.Core.Domain.Catalog.QuantityControlType". * Topics: Fixes "Cannot insert duplicate key row in object 'dbo.UrlRecord' with unique index 'IX_UrlRecord_Slug'" +* Fixed redirection to the homepage for pages which are loaded while the application is restarted ## SmartStore.NET 3.1.5 diff --git a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs index 565e3f5bfe..50f424a130 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs @@ -21,11 +21,11 @@ public partial class WebStoreContext : IStoreContext private Store _currentStore; - public WebStoreContext(Work storeService, IWebHelper webHelper, HttpContextBase httpContext) + public WebStoreContext(Work storeService) { _storeService = storeService; - _webHelper = webHelper; - _httpContext = httpContext; + _httpContext = new HttpContextWrapper(HttpContext.Current); + _webHelper = new WebHelper(_httpContext); } public void SetRequestStore(int? storeId) diff --git a/src/Presentation/SmartStore.Web/Infrastructure/Installation/InstallDataSeeder.cs b/src/Presentation/SmartStore.Web/Infrastructure/Installation/InstallDataSeeder.cs index fdc61b1478..65cbc3b6a0 100644 --- a/src/Presentation/SmartStore.Web/Infrastructure/Installation/InstallDataSeeder.cs +++ b/src/Presentation/SmartStore.Web/Infrastructure/Installation/InstallDataSeeder.cs @@ -33,6 +33,7 @@ using SmartStore.Services.Stores; using SmartStore.Utilities; using SmartStore.Web.Framework; +using SmartStore.Core.Infrastructure.DependencyManagement; namespace SmartStore.Web.Infrastructure.Installation { @@ -482,8 +483,9 @@ protected ILocalizationService LocalizationService rsResources.AutoCommitEnabled = false; var storeMappingService = new StoreMappingService(NullCache.Instance, null, null, null); - var storeService = new StoreService(new EfRepository(_ctx)); - var storeContext = new WebStoreContext(new Work(x => storeService), new WebHelper(null), null); + var storeService = new StoreService(new EfRepository(_ctx)); + var storeContext = new WebStoreContext(new Work(x => storeService)); + var locSettings = new LocalizationSettings(); var languageService = new LanguageService( From 0780953198be09d29c2671b79f182a7789e9ac03 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 11 Jan 2019 13:31:42 +0100 Subject: [PATCH 009/575] Updated change log --- changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index b19e41de11..bac92db344 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,4 @@ -# Release Notes +# Release Notes ## SmartStore.NET 3.2 @@ -103,7 +103,8 @@ * Product display order on category and manufacturer pages sometimes wrong when using linq search. * Debitoor: Wrong invoice total if a subtotal discount has been applied. * Import: Fixes invalid conversion "System.Double" to "SmartStore.Core.Domain.Catalog.QuantityControlType". -* Topics: Fixes "Cannot insert duplicate key row in object 'dbo.UrlRecord' with unique index 'IX_UrlRecord_Slug'" +* Topics: Fixes "Cannot insert duplicate key row in object 'dbo.UrlRecord' with unique index 'IX_UrlRecord_Slug'". +* #1566 Santander: eliminate the 1 cent rounding difference at amountTotalNet. * Fixed redirection to the homepage for pages which are loaded while the application is restarted From 72b5b02ff874f5cefb3fef755b0ac159da7355cb Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 11 Jan 2019 15:24:01 +0100 Subject: [PATCH 010/575] Resolves #1563 QueuedMessagesClearTask: add a setting for the age of the mails to be deleted --- changelog.md | 3 ++- .../Domain/Common/CommonSettings.cs | 6 ++++++ .../Messages/QueuedMessagesClearTask.cs | 16 +++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index bac92db344..7d39f8c679 100644 --- a/changelog.md +++ b/changelog.md @@ -63,7 +63,8 @@ * Enhanced EntityPicker to pick from customers, manufacturers & categories * #1510 Breadcrumb of an associated product should include the grouped product if it has no assigned categories. * OpenTrans: added customer number to parties -* Do not filter cookie using resources if cookie usage has not yet been consented to +* Do not filter cookie using resources if cookie usage has not yet been consented to. +* #1563 QueuedMessagesClearTask: add a setting for the age of the mails to be deleted. ### Bugfixes * In a multi-store environment, multiple topics with the same system name cannot be resolved reliably. diff --git a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs index 46d94f8c98..523a5dbe52 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs @@ -11,6 +11,7 @@ public CommonSettings() EntityPickerPageSize = 48; MaxScheduleHistoryAgeInDays = 30; MaxNumberOfScheduleHistoryEntries = 100; + MaxQueuedMessagesAgeInDays = 14; } public bool UseSystemEmailForContactUsForm { get; set; } @@ -40,5 +41,10 @@ public CommonSettings() /// Gets or sets the maximum number of schedule history entries per task. /// public int MaxNumberOfScheduleHistoryEntries { get; set; } + + /// + /// Gets or sets the maximum age of sent queued messages (in days). + /// + public int MaxQueuedMessagesAgeInDays { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs index b31f3b4ce3..a21102901b 100644 --- a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs +++ b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs @@ -1,28 +1,30 @@ using System; -using System.Linq; -using System.Linq.Expressions; using SmartStore.Core.Data; +using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Messages; -using SmartStore.Data; using SmartStore.Services.Tasks; namespace SmartStore.Services.Messages { /// - /// Represents a task for deleting sent emails from the message queue + /// Represents a task for deleting sent emails from the message queue. /// public partial class QueuedMessagesClearTask : ITask { private readonly IRepository _qeRepository; + private readonly CommonSettings _commonSettings; - public QueuedMessagesClearTask(IRepository qeRepository) + public QueuedMessagesClearTask( + IRepository qeRepository, + CommonSettings commonSettings) { - this._qeRepository = qeRepository; + _qeRepository = qeRepository; + _commonSettings = commonSettings; } public void Execute(TaskExecutionContext ctx) { - var olderThan = DateTime.UtcNow.AddDays(-14); + var olderThan = DateTime.UtcNow.AddDays(-Math.Abs(_commonSettings.MaxQueuedMessagesAgeInDays)); _qeRepository.DeleteAll(x => x.SentOnUtc.HasValue && x.CreatedOnUtc < olderThan); _qeRepository.Context.ShrinkDatabase(); From 60a6f47f2a70189f7f130a0fed34f92378725de7 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sat, 12 Jan 2019 02:36:59 +0100 Subject: [PATCH 011/575] Theming: smaller padding for login box --- src/Presentation/SmartStore.Web/Views/Customer/Login.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Views/Customer/Login.cshtml b/src/Presentation/SmartStore.Web/Views/Customer/Login.cshtml index 5313d46186..ed8c99a57a 100644 --- a/src/Presentation/SmartStore.Web/Views/Customer/Login.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Customer/Login.cshtml @@ -24,7 +24,7 @@
-
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml index 0d8722653d..a5bd650d60 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml @@ -46,7 +46,7 @@
}
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/List.cshtml index cc55977547..da65f0c747 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/List.cshtml @@ -97,20 +97,15 @@
@Html.SmartLabelFor(model => model.SearchCustomerRoleIds) - @*@Html.DropDownListFor(model => model.SearchCustomerRoleIds, Model.AvailableCustomerRoles, null, new { multiple = "multiple", @class = "form-control" })*@ @Html.DropDownList("SearchCustomerRoleIds", Model.AvailableCustomerRoles, null, new { multiple = "multiple", @class = "form-control" })
- -
- @Html.CheckBoxFor(model => model.SearchDeletedOnly, new { @class = "form-check-input" }) - -
+ @Html.SmartLabelFor(model => model.SearchActiveOnly) + @Html.EditorFor(model => Model.SearchActiveOnly, new { @class = "form-control sf" })
- @@ -122,7 +117,8 @@ .Name("customers-grid") .ClientEvents(events => events .OnDataBinding("onDataBinding") - .OnDataBound("onDataBound")) + .OnDataBound("onDataBound") + .OnRowDataBound("onRowDataBound")) .Columns(columns => { columns.Bound(x => x.Id) @@ -216,8 +212,7 @@ }); }); - function onDataBound(e) { - + function onDataBound() { $('#customers-grid input[type=checkbox][id!=mastercheckbox]').each(function () { var currentId = $(this).val(); var checked = jQuery.inArray(currentId, selectedIds); @@ -226,7 +221,13 @@ }); updateMasterCheckbox(); - } + } + + function onRowDataBound(e) { + if (!e.dataItem.Active) { + $(e.row).find('td').wrapInner(''); + } + } function updateMasterCheckbox() { var numChkBoxes = $('#customers-grid input[type=checkbox][id!=mastercheckbox]').length; @@ -244,7 +245,7 @@ SearchMonthOfBirth: $('#@Html.FieldIdFor(model => model.SearchMonthOfBirth)').val(), SearchPhone: $('#@Html.FieldIdFor(model => model.SearchPhone)').val(), SearchZipPostalCode: $('#@Html.FieldIdFor(model => model.SearchZipPostalCode)').val(), - SearchDeletedOnly: $('#@Html.FieldIdFor(model => model.SearchDeletedOnly)').prop('checked') + SearchActiveOnly: $('#@Html.FieldIdFor(model => model.SearchActiveOnly)').val() }; e.data = searchModel; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml index 389e0ffc36..5da15e34b7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml @@ -24,7 +24,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml index 7e07fd7b5c..a693c83bce 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml @@ -24,7 +24,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml index a22c72cf7e..2cc47b5526 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml @@ -28,7 +28,7 @@ @Html.TextBoxFor(model => model.CouponCode, new { @class = "form-control" })
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml index 7409eae3f8..82a4890ee1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml @@ -45,7 +45,7 @@ @Html.DropDownList("LogLevelId", Model.AvailableLogLevels, T("Admin.Common.All"))
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml index ce22947113..543372a417 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml @@ -33,7 +33,7 @@
}
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml index 5b75096ee9..7ad2be365a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml @@ -25,7 +25,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml index a88278a11e..2a45db4bf7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml @@ -37,7 +37,7 @@ @Html.DropDownListFor(model => model.SearchStoreId, Model.AvailableStores, T("Admin.Common.All"), new { @class = "form-control" })
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml index 6ea6c224f0..982e5f05e2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml @@ -28,7 +28,7 @@
}
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml index ff4f515541..100b11a8d0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml @@ -100,7 +100,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/NeverSoldReport.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/NeverSoldReport.cshtml index 86f13c1590..3762130c58 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/NeverSoldReport.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/NeverSoldReport.cshtml @@ -25,9 +25,9 @@ @Html.SmartLabelFor(model => model.EndDate) @Html.EditorFor(model => model.EndDate)
- +
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentList.cshtml index 61330f9f09..a340893b3c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentList.cshtml @@ -43,7 +43,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml index 7e1ebc3ba5..6884fb03d0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml @@ -46,7 +46,7 @@ @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, allString)
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml index 1fe243f910..3b8924e689 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml @@ -84,7 +84,7 @@
}
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/ProductReview/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/ProductReview/List.cshtml index 098d1c78dc..6fc0887407 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/ProductReview/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/ProductReview/List.cshtml @@ -40,7 +40,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/QueuedEmail/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/QueuedEmail/List.cshtml index 6a6d0f4123..5038df4680 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/QueuedEmail/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/QueuedEmail/List.cshtml @@ -74,7 +74,7 @@
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml index 1649806229..3ee82d345f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml @@ -30,7 +30,7 @@ @Html.DropDownListFor(model => model.SearchReturnRequestStatusId, Model.AvailableReturnRequestStatus, T("Admin.Common.All"))
- + @Html.SmartLabel(string.Empty, string.Empty) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml index 9d85d4d644..3da1caad9d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml @@ -48,7 +48,7 @@
@if (Model.AvailableStores.Count > 1) { - + @Html.SmartLabel(string.Empty, string.Empty) }
- + @Html.SmartLabel(string.Empty, string.Empty) From f5b62c3a5aea2c5dd76e329851a784ecad2fcb0b Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 8 Feb 2019 11:46:12 +0100 Subject: [PATCH 028/575] Fixes broken unit tests --- changelog.md | 1 + .../Discounts/DiscountService.cs | 22 +---- .../Infrastructure/AutoMapperAdminProfile.cs | 24 +++-- .../Discounts/DiscountServiceTests.cs | 92 ++++++++----------- .../Helpers/DateTimeHelperTests.cs | 35 +++---- 5 files changed, 68 insertions(+), 106 deletions(-) diff --git a/changelog.md b/changelog.md index 13db42f67b..2b93b75d58 100644 --- a/changelog.md +++ b/changelog.md @@ -44,6 +44,7 @@ * Added options for alternating price display (in badges). * #1515 Poll: Add result tab with a list of answers and customers for a poll * BMEcat: Added export and import of product tags. +* Santander instalment purchase. ### Improvements * (Perf) Significantly increased query performance for products with a lot of category assignments (> 10). diff --git a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs index 666c53dfdb..660e5f23a1 100644 --- a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs +++ b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs @@ -1,19 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; +using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; -using SmartStore.Core.Events; -using SmartStore.Core.Plugins; using SmartStore.Core.Domain.Orders; -using SmartStore.Services.Customers; +using SmartStore.Core.Plugins; using SmartStore.Services.Common; -using SmartStore.Services.Configuration; -using SmartStore.Collections; -using SmartStore.Core.Data.Hooks; +using SmartStore.Services.Customers; namespace SmartStore.Services.Discounts { @@ -28,21 +25,16 @@ public partial class DiscountService : IDiscountService private readonly IRequestCache _requestCache; private readonly IStoreContext _storeContext; private readonly IGenericAttributeService _genericAttributeService; - private readonly IPluginFinder _pluginFinder; - private readonly IEventPublisher _eventPublisher; - private readonly ISettingService _settingService; private readonly IProviderManager _providerManager; private readonly IDictionary _discountValidityCache; - public DiscountService(IRequestCache requestCache, + public DiscountService( + IRequestCache requestCache, IRepository discountRepository, IRepository discountRequirementRepository, IRepository discountUsageHistoryRepository, IStoreContext storeContext, IGenericAttributeService genericAttributeService, - IPluginFinder pluginFinder, - IEventPublisher eventPublisher, - ISettingService settingService, IProviderManager providerManager) { _requestCache = requestCache; @@ -51,9 +43,6 @@ public DiscountService(IRequestCache requestCache, _discountUsageHistoryRepository = discountUsageHistoryRepository; _storeContext = storeContext; _genericAttributeService = genericAttributeService; - _pluginFinder = pluginFinder; - _eventPublisher = eventPublisher; - _settingService = settingService; _providerManager = providerManager; _discountValidityCache = new Dictionary(); } @@ -207,7 +196,6 @@ public virtual Discount GetDiscountByCouponCode(string couponCode, bool showHidd return discount; } - private System.Diagnostics.Stopwatch _watch = new System.Diagnostics.Stopwatch(); public virtual bool IsDiscountValid(Discount discount, Customer customer) { var couponCodeToValidate = ""; diff --git a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperAdminProfile.cs b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperAdminProfile.cs index 068081f3dd..7833616bb4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperAdminProfile.cs +++ b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperAdminProfile.cs @@ -431,16 +431,19 @@ public AutoMapperAdminProfile() CreateMap() .ForMember(dest => dest.CreatedOnUtc, mo => mo.Ignore()) .ForMember(dest => dest.NewsLetterSubscriptionGuid, mo => mo.Ignore()) - .ForMember(dest => dest.StoreId, mo => mo.Ignore()); - //forums - CreateMap() - .ForMember(dest => dest.Locales, mo => mo.Ignore()) - .ForMember(dest => dest.SeName, mo => mo.MapFrom(src => src.GetSeName(0, true, false))) - .ForMember(dest => dest.AvailableStores, mo => mo.Ignore()) - .ForMember(dest => dest.SelectedStoreIds, mo => mo.Ignore()) - .ForMember(dest => dest.CreatedOn, mo => mo.Ignore()) - .ForMember(dest => dest.ForumModels, mo => mo.Ignore()); - CreateMap() + .ForMember(dest => dest.StoreId, mo => mo.Ignore()) + .ForMember(dest => dest.WorkingLanguageId, mo => mo.Ignore()); + //forums + CreateMap() + .ForMember(dest => dest.Locales, mo => mo.Ignore()) + .ForMember(dest => dest.SeName, mo => mo.MapFrom(src => src.GetSeName(0, true, false))) + .ForMember(dest => dest.AvailableStores, mo => mo.Ignore()) + .ForMember(dest => dest.SelectedStoreIds, mo => mo.Ignore()) + .ForMember(dest => dest.AvailableCustomerRoles, mo => mo.Ignore()) + .ForMember(dest => dest.SelectedCustomerRoleIds, mo => mo.Ignore()) + .ForMember(dest => dest.CreatedOn, mo => mo.Ignore()) + .ForMember(dest => dest.ForumModels, mo => mo.Ignore()); + CreateMap() .ForMember(dest => dest.CreatedOnUtc, mo => mo.Ignore()) .ForMember(dest => dest.UpdatedOnUtc, mo => mo.Ignore()) .ForMember(dest => dest.Forums, mo => mo.Ignore()); @@ -499,6 +502,7 @@ public AutoMapperAdminProfile() .ForMember(dest => dest.EndDate, mo => mo.Ignore()) .ForMember(dest => dest.AvailableStores, mo => mo.Ignore()) .ForMember(dest => dest.SelectedStoreIds, mo => mo.Ignore()) + .ForMember(dest => dest.AvailableLanguages, mo => mo.Ignore()) .ForMember(dest => dest.UsernamesEnabled, mo => mo.Ignore()) .ForMember(dest => dest.GridPageSize, mo => mo.Ignore()); CreateMap() diff --git a/src/Tests/SmartStore.Services.Tests/Discounts/DiscountServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Discounts/DiscountServiceTests.cs index 5c3fb0e389..44368552fb 100644 --- a/src/Tests/SmartStore.Services.Tests/Discounts/DiscountServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Discounts/DiscountServiceTests.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; +using Rhino.Mocks; +using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; -using SmartStore.Core.Plugins; +using SmartStore.Core.Domain.Stores; +using SmartStore.Services.Common; using SmartStore.Services.Discounts; -using SmartStore.Core.Events; using SmartStore.Tests; -using NUnit.Framework; -using Rhino.Mocks; -using SmartStore.Core; -using SmartStore.Services.Common; -using SmartStore.Core.Domain.Common; -using SmartStore.Services.Configuration; -using SmartStore.Core.Domain.Stores; namespace SmartStore.Services.Tests.Discounts { @@ -25,11 +21,9 @@ public class DiscountServiceTests : ServiceTest IRepository _discountRepo; IRepository _discountRequirementRepo; IRepository _discountUsageHistoryRepo; - IEventPublisher _eventPublisher; IGenericAttributeService _genericAttributeService; IDiscountService _discountService; IStoreContext _storeContext; - ISettingService _settingService; [SetUp] public new void SetUp() @@ -62,9 +56,6 @@ public class DiscountServiceTests : ServiceTest _discountRepo.Expect(x => x.Table).Return(new List() { discount1, discount2 }.AsQueryable()); - _eventPublisher = MockRepository.GenerateMock(); - _eventPublisher.Expect(x => x.Publish(Arg.Is.Anything)); - _storeContext = MockRepository.GenerateMock(); _storeContext.Expect(x => x.CurrentStore).Return(new Store { @@ -72,17 +63,12 @@ public class DiscountServiceTests : ServiceTest Name = "MyStore" }); - _settingService = MockRepository.GenerateMock(); - - var cacheManager = new NullCache(); _discountRequirementRepo = MockRepository.GenerateMock>(); _discountUsageHistoryRepo = MockRepository.GenerateMock>(); - var pluginFinder = new PluginFinder(); _genericAttributeService = MockRepository.GenerateMock(); _discountService = new DiscountService(NullRequestCache.Instance, _discountRepo, _discountRequirementRepo, - _discountUsageHistoryRepo, _storeContext, _genericAttributeService, pluginFinder, _eventPublisher, - _settingService, base.ProviderManager); + _discountUsageHistoryRepo, _storeContext, _genericAttributeService, ProviderManager); } [Test] @@ -133,17 +119,8 @@ public void Should_accept_valid_discount_code() LastActivityDateUtc = new DateTime(2010, 01, 02) }; - _genericAttributeService.Expect(x => x.GetAttributesForEntity(customer.Id, "Customer")) - .Return(new List() - { - new GenericAttribute() - { - EntityId = customer.Id, - Key = SystemCustomerAttributeNames.DiscountCouponCode, - KeyGroup = "Customer", - Value = "CouponCode 1" - } - }); + _genericAttributeService.Expect(x => x.GetAttribute(nameof(Customer), customer.Id, SystemCustomerAttributeNames.DiscountCouponCode, 0)) + .Return("CouponCode 1"); var result1 = _discountService.IsDiscountValid(discount, customer); result1.ShouldEqual(true); @@ -174,29 +151,30 @@ public void Should_not_accept_wrong_discount_code() LastActivityDateUtc = new DateTime(2010, 01, 02) }; - _genericAttributeService.Expect(x => x.GetAttributesForEntity(customer.Id, "Customer")) - .Return(new List() - { - new GenericAttribute() - { - EntityId = customer.Id, - Key = SystemCustomerAttributeNames.DiscountCouponCode, - KeyGroup = "Customer", - Value = "CouponCode 2" - } - }); - - var result2 = _discountService.IsDiscountValid(discount, customer); + _genericAttributeService.Expect(x => x.GetAttribute(nameof(Customer), customer.Id, SystemCustomerAttributeNames.DiscountCouponCode, 0)) + .Return("CouponCode 2"); + + var result2 = _discountService.IsDiscountValid(discount, customer); result2.ShouldEqual(false); } [Test] public void Can_validate_discount_dateRange() { - var discount = new Discount + var customer = new Customer + { + CustomerGuid = Guid.NewGuid(), + AdminComment = "", + Active = true, + Deleted = false, + CreatedOnUtc = new DateTime(2010, 01, 01), + LastActivityDateUtc = new DateTime(2010, 01, 02) + }; + + var discount1 = new Discount { DiscountType = DiscountType.AssignedToSkus, - Name = "Discount 2", + Name = "Discount 1", UsePercentage = false, DiscountPercentage = 0, DiscountAmount = 5, @@ -206,21 +184,23 @@ public void Can_validate_discount_dateRange() DiscountLimitation = DiscountLimitationType.Unlimited, }; - var customer = new Customer + var discount2 = new Discount { - CustomerGuid = Guid.NewGuid(), - AdminComment = "", - Active = true, - Deleted = false, - CreatedOnUtc = new DateTime(2010, 01, 01), - LastActivityDateUtc = new DateTime(2010, 01, 02) + DiscountType = DiscountType.AssignedToSkus, + Name = "Discount 2", + UsePercentage = false, + DiscountPercentage = 0, + DiscountAmount = 5, + StartDateUtc = DateTime.UtcNow.AddDays(1), + EndDateUtc = DateTime.UtcNow.AddDays(2), + RequiresCouponCode = false, + DiscountLimitation = DiscountLimitationType.Unlimited, }; - var result1 = _discountService.IsDiscountValid(discount, customer); + var result1 = _discountService.IsDiscountValid(discount1, customer); result1.ShouldEqual(true); - discount.StartDateUtc = DateTime.UtcNow.AddDays(1); - var result2 = _discountService.IsDiscountValid(discount, customer); + var result2 = _discountService.IsDiscountValid(discount2, customer); result2.ShouldEqual(false); } } diff --git a/src/Tests/SmartStore.Services.Tests/Helpers/DateTimeHelperTests.cs b/src/Tests/SmartStore.Services.Tests/Helpers/DateTimeHelperTests.cs index 29160fe4b9..ed5a793f53 100644 --- a/src/Tests/SmartStore.Services.Tests/Helpers/DateTimeHelperTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Helpers/DateTimeHelperTests.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; +using NUnit.Framework; +using Rhino.Mocks; using SmartStore.Core; +using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Domain.Common; +using SmartStore.Services.Common; using SmartStore.Services.Configuration; -using SmartStore.Services.Customers; using SmartStore.Services.Helpers; -using SmartStore.Services.Common; using SmartStore.Tests; -using NUnit.Framework; -using Rhino.Mocks; namespace SmartStore.Services.Tests.Helpers { @@ -33,18 +32,17 @@ public class DateTimeHelperTests : ServiceTest _workContext = MockRepository.GenerateMock(); - _store = new Store() { Id = 1 }; + _store = new Store { Id = 1 }; _storeContext = MockRepository.GenerateMock(); _storeContext.Expect(x => x.CurrentStore).Return(_store); - _dateTimeSettings = new DateTimeSettings() + _dateTimeSettings = new DateTimeSettings { AllowCustomersToSetTimeZone = false, DefaultStoreTimeZoneId = "" }; - _dateTimeHelper = new DateTimeHelper(_workContext, _genericAttributeService, - _settingService, _dateTimeSettings); + _dateTimeHelper = new DateTimeHelper(_workContext, _genericAttributeService, _settingService, _dateTimeSettings); } [Test] @@ -67,25 +65,16 @@ public void Can_get_all_systemTimeZones() public void Can_get_customer_timeZone_with_customTimeZones_enabled() { _dateTimeSettings.AllowCustomersToSetTimeZone = true; - _dateTimeSettings.DefaultStoreTimeZoneId = "E. Europe Standard Time"; //(GMT+02:00) Minsk; + _dateTimeSettings.DefaultStoreTimeZoneId = "E. Europe Standard Time"; // (GMT+02:00) Minsk; - var customer = new Customer() + var customer = new Customer { Id = 10 }; - _genericAttributeService.Expect(x => x.GetAttributesForEntity(customer.Id, "Customer")) - .Return(new List() - { - new GenericAttribute() - { - StoreId = 0, - EntityId = customer.Id, - Key = SystemCustomerAttributeNames.TimeZoneId, - KeyGroup = "Customer", - Value = "Russian Standard Time" //(GMT+03:00) Moscow, St. Petersburg, Volgograd - } - }); + _genericAttributeService + .Expect(x => x.GetAttribute(nameof(Customer), customer.Id, SystemCustomerAttributeNames.TimeZoneId, 0)) + .Return("Russian Standard Time"); // (GMT+03:00) Moscow, St. Petersburg, Volgograd var timeZone = _dateTimeHelper.GetCustomerTimeZone(customer); timeZone.ShouldNotBeNull(); From 7fe1ffbd9690b4b82f283ab75d22e8a46cdde2de Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 8 Feb 2019 17:35:10 +0100 Subject: [PATCH 029/575] Resolves #1569 Show login note if no prices are displayed due to customer group permissions --- changelog.md | 1 + .../Domain/Catalog/CatalogSettings.cs | 15 ++- .../Migrations/MigrationsConfiguration.cs | 10 ++ .../Security/PermissionService.cs | 52 +++++---- .../Models/Settings/CatalogSettingsModel.cs | 3 + .../Views/Setting/Catalog.cshtml | 9 ++ .../Controllers/CatalogHelper.cs | 86 +++++++-------- .../Models/Catalog/ProductDetailsModel.cs | 3 +- .../Themes/Flex/Content/_product.scss | 3 +- .../Partials/Product.Offer.Price.cshtml | 103 +++++++++--------- .../Product/Partials/Product.Offer.cshtml | 4 +- 11 files changed, 154 insertions(+), 135 deletions(-) diff --git a/changelog.md b/changelog.md index 2b93b75d58..2a54b1a8c6 100644 --- a/changelog.md +++ b/changelog.md @@ -66,6 +66,7 @@ * OpenTrans: added customer number to parties * Do not filter cookie using resources if cookie usage has not yet been consented to. * #1563 QueuedMessagesClearTask: add a setting for the age of the mails to be deleted. +* #1569 Added a setting to show login note if no prices are displayed due to customer group permissions. ### Bugfixes * In a multi-store environment, multiple topics with the same system name cannot be resolved reliably. diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs index c6d208659f..394e9e4e9e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs @@ -68,7 +68,7 @@ public CatalogSettings() ShowColorSquaresInLists = true; ShowDiscountSign = true; ShowVariantCombinationPriceAdjustment = true; - ShowLinkedAttributeValueImage = true; + ShowLinkedAttributeValueImage = true; EnableDynamicPriceUpdate = true; ShowProductReviewsInProductDetail = true; EnableHtmlTextCollapser = true; @@ -129,10 +129,15 @@ public CatalogSettings() /// public bool ShowVariantCombinationPriceAdjustment { get; set; } - /// - /// Gets or sets a value indicating whether to display quantity of linked product at attribute values - /// - public bool ShowLinkedAttributeValueQuantity { get; set; } + /// + /// Indicates whether to show a login note if the user is not authorized to see prices. + /// + public bool ShowLoginForPriceNote { get; set; } + + /// + /// Gets or sets a value indicating whether to display quantity of linked product at attribute values + /// + public bool ShowLinkedAttributeValueQuantity { get; set; } /// /// Gets or sets a value indicating whether to display the image of linked product at attribute values diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 2fc48aa446..977bc6ccb4 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -654,6 +654,16 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.Delete("Admin.Customers.Customers.List.SearchDeletedOnly"); builder.AddOrUpdate("Admin.Customers.Customers.List.SearchActiveOnly", "Only activated customers", "Nur aktivierte Kunden"); + + builder.AddOrUpdate("Products.LoginForPrice", + "Prices will be displayed after login.", + "Preise werden nach Anmeldung angezeigt."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowLoginForPriceNote", + "Show login for price note", + "Hinweis \"Preis nach Anmeldung\" anzeigen", + "Specifies whether to display a message stating that prices will not be displayed until login.", + "Legt fest, ob ein Hinweis erscheinen soll, dass Preise erst nach Anmeldung angezeigt werden."); } } } diff --git a/src/Libraries/SmartStore.Services/Security/PermissionService.cs b/src/Libraries/SmartStore.Services/Security/PermissionService.cs index 16d0fa0ac0..dec98fd0fe 100644 --- a/src/Libraries/SmartStore.Services/Security/PermissionService.cs +++ b/src/Libraries/SmartStore.Services/Security/PermissionService.cs @@ -10,12 +10,11 @@ namespace SmartStore.Services.Security { - /// - /// Permission service - /// - public partial class PermissionService : IPermissionService + /// + /// Permission service + /// + public partial class PermissionService : IPermissionService { - #region Constants /// /// Cache key for storing a valie indicating whether a certain customer role has a permission /// @@ -25,20 +24,12 @@ public partial class PermissionService : IPermissionService /// private const string PERMISSIONS_ALLOWED_KEY = "permission:allowed-{0}-{1}"; private const string PERMISSIONS_PATTERN_KEY = "permission:*"; - #endregion - - #region Fields private readonly IRepository _permissionRecordRepository; - private readonly IRepository _customerRoleRepository; private readonly ICustomerService _customerService; private readonly IWorkContext _workContext; private readonly ICacheManager _cacheManager; - #endregion - - #region Ctor - /// /// Ctor /// @@ -48,19 +39,16 @@ public partial class PermissionService : IPermissionService /// Cache manager public PermissionService( IRepository permissionRecordRepository, - IRepository customerRoleRepository, ICustomerService customerService, - IWorkContext workContext, ICacheManager cacheManager) + IWorkContext workContext, + ICacheManager cacheManager) { - this._permissionRecordRepository = permissionRecordRepository; - this._customerRoleRepository = customerRoleRepository; - this._customerService = customerService; - this._workContext = workContext; - this._cacheManager = cacheManager; + _permissionRecordRepository = permissionRecordRepository; + _customerService = customerService; + _workContext = workContext; + _cacheManager = cacheManager; } - #endregion - #region Utilities /// @@ -71,15 +59,21 @@ public PermissionService( /// true - authorized; otherwise, false protected virtual bool Authorize(string permissionRecordSystemName, CustomerRole customerRole) { - if (String.IsNullOrEmpty(permissionRecordSystemName)) + if (string.IsNullOrEmpty(permissionRecordSystemName)) + { return false; + } - string key = string.Format(PERMISSIONS_ALLOWED_KEY, customerRole.Id, permissionRecordSystemName); + var key = string.Format(PERMISSIONS_ALLOWED_KEY, customerRole.Id, permissionRecordSystemName); return _cacheManager.Get(key, () => { foreach (var permission1 in customerRole.PermissionRecords) + { if (permission1.SystemName.Equals(permissionRecordSystemName, StringComparison.InvariantCultureIgnoreCase)) + { return true; + } + } return false; }); @@ -311,16 +305,20 @@ public virtual bool Authorize(string permissionRecordSystemName) /// true - authorized; otherwise, false public virtual bool Authorize(string permissionRecordSystemName, Customer customer) { - if (String.IsNullOrEmpty(permissionRecordSystemName)) + if (string.IsNullOrEmpty(permissionRecordSystemName)) + { return false; + } var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); foreach (var role in customerRoles) + { if (Authorize(permissionRecordSystemName, role)) - //yes, we have such permission + { return true; + } + } - //no permission found return false; } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs index 8c2e1acf84..d842fbdda7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs @@ -300,6 +300,9 @@ public CatalogSettingsModel() [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowVariantCombinationPriceAdjustment")] public bool ShowVariantCombinationPriceAdjustment { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowLoginForPriceNote")] + public bool ShowLoginForPriceNote { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowLinkedAttributeValueQuantity")] public bool ShowLinkedAttributeValueQuantity { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Catalog.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Catalog.cshtml index 44f9f4db7f..b0cdb0d98e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Catalog.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Catalog.cshtml @@ -799,6 +799,15 @@ @Html.ValidationMessageFor(model => model.ShowVariantCombinationPriceAdjustment) + + + @Html.SmartLabelFor(model => model.ShowLoginForPriceNote) + + + @Html.SettingEditorFor(model => model.ShowLoginForPriceNote) + @Html.ValidationMessageFor(model => model.ShowLoginForPriceNote) + + @Html.SmartLabelFor(model => model.ShowLinkedAttributeValueQuantity) diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index 4fffd68631..b563a34d37 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -634,36 +634,34 @@ public ProductDetailsModel PrepareProductDetailModel( IList productBundleItems = null, int selectedQuantity = 1) { - if (product == null) - throw new ArgumentNullException("product"); - - if (model == null) - throw new ArgumentNullException("model"); + Guard.NotNull(model, nameof(model)); + Guard.NotNull(product, nameof(product)); var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var currency = _services.WorkContext.WorkingCurrency; - decimal preSelectedPriceAdjustmentBase = decimal.Zero; - decimal preSelectedWeightAdjustment = decimal.Zero; - bool displayPrices = _services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices); - bool isBundle = (product.ProductType == ProductType.BundledProduct); - bool isBundleItemPricing = (productBundleItem != null && productBundleItem.Item.BundleProduct.BundlePerItemPricing); - bool isBundlePricing = (productBundleItem != null && !productBundleItem.Item.BundleProduct.BundlePerItemPricing); - int bundleItemId = (productBundleItem == null ? 0 : productBundleItem.Item.Id); + var preSelectedPriceAdjustmentBase = decimal.Zero; + var preSelectedWeightAdjustment = decimal.Zero; + var displayPrices = _services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices); + var isBundle = product.ProductType == ProductType.BundledProduct; + var isBundleItemPricing = productBundleItem != null && productBundleItem.Item.BundleProduct.BundlePerItemPricing; + var isBundlePricing = productBundleItem != null && !productBundleItem.Item.BundleProduct.BundlePerItemPricing; + var bundleItemId = productBundleItem == null ? 0 : productBundleItem.Item.Id; - bool hasSelectedAttributesValues = false; - bool hasSelectedAttributes = query.Variants.Count > 0; + var hasSelectedAttributesValues = false; + var hasSelectedAttributes = query.Variants.Count > 0; List selectedAttributeValues = null; - - var variantAttributes = (isBundle ? new List() : _productAttributeService.GetProductVariantAttributesByProductId(product.Id)); + var variantAttributes = isBundle ? new List() : _productAttributeService.GetProductVariantAttributesByProductId(product.Id); model.IsBundlePart = product.ProductType != ProductType.BundledProduct && productBundleItem != null; model.ProductPrice.DynamicPriceUpdate = _catalogSettings.EnableDynamicPriceUpdate; model.ProductPrice.BundleItemShowBasePrice = _catalogSettings.BundleItemShowBasePrice; - if (!model.ProductPrice.DynamicPriceUpdate) - selectedQuantity = 1; + if (!model.ProductPrice.DynamicPriceUpdate) + { + selectedQuantity = 1; + } #region Product attributes @@ -1075,11 +1073,11 @@ public ProductDetailsModel PrepareProductDetailModel( #region Product price model.ProductPrice.ProductId = product.Id; + model.ProductPrice.HidePrices = !displayPrices; + model.ProductPrice.ShowLoginNote = !displayPrices && productBundleItem == null && _catalogSettings.ShowLoginForPriceNote; - if (displayPrices) + if (displayPrices) { - model.ProductPrice.HidePrices = false; - if (product.CustomerEntersPrice && !isBundleItemPricing) { model.ProductPrice.CustomerEntersPrice = true; @@ -1092,26 +1090,25 @@ public ProductDetailsModel PrepareProductDetailModel( } else { - decimal taxRate = decimal.Zero; - decimal oldPrice = decimal.Zero; - decimal finalPriceWithoutDiscountBase = decimal.Zero; - decimal finalPriceWithDiscountBase = decimal.Zero; - decimal attributesTotalPriceBase = decimal.Zero; - decimal attributesTotalPriceBaseOrig = decimal.Zero; - decimal finalPriceWithoutDiscount = decimal.Zero; - decimal finalPriceWithDiscount = decimal.Zero; - - decimal oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate); + var taxRate = decimal.Zero; + var oldPrice = decimal.Zero; + var finalPriceWithoutDiscountBase = decimal.Zero; + var finalPriceWithDiscountBase = decimal.Zero; + var attributesTotalPriceBase = decimal.Zero; + var attributesTotalPriceBaseOrig = decimal.Zero; + var finalPriceWithoutDiscount = decimal.Zero; + var finalPriceWithDiscount = decimal.Zero; + var oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate); if (model.ProductPrice.DynamicPriceUpdate && !isBundlePricing) { if (selectedAttributeValues != null) { selectedAttributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(x, - product, _services.WorkContext.CurrentCustomer, null, selectedQuantity)); + product, customer, null, selectedQuantity)); selectedAttributeValues.Each(x => attributesTotalPriceBaseOrig += _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(x, - product, _services.WorkContext.CurrentCustomer, null, 1)); + product, customer, null, 1)); } else { @@ -1208,36 +1205,32 @@ public ProductDetailsModel PrepareProductDetailModel( } else { - model.ProductPrice.HidePrices = true; - model.ProductPrice.OldPrice = null; + model.ProductPrice.OldPrice = null; model.ProductPrice.Price = null; } + #endregion #region 'Add to cart' model model.AddToCart.ProductId = product.Id; - - //quantity model.AddToCart.EnteredQuantity = product.OrderMinimumQuantity; - model.AddToCart.MinOrderAmount = product.OrderMinimumQuantity; model.AddToCart.MaxOrderAmount = product.OrderMaximumQuantity; model.AddToCart.QuantityUnitName = model.QuantityUnitName; // TODO: (mc) remove 'QuantityUnitName' from parent model later model.AddToCart.QuantityStep = product.QuantityStep > 0 ? product.QuantityStep : 1; model.AddToCart.HideQuantityControl = product.HideQuantityControl; model.AddToCart.QuantiyControlType = product.QuantiyControlType; + model.AddToCart.AvailableForPreOrder = product.AvailableForPreOrder; - //'add to cart', 'add to wishlist' buttons - model.AddToCart.DisableBuyButton = !displayPrices || product.DisableBuyButton || !_services.Permissions.Authorize(StandardPermissionProvider.EnableShoppingCart); - model.AddToCart.DisableWishlistButton = !displayPrices || product.DisableWishlistButton - || !_services.Permissions.Authorize(StandardPermissionProvider.EnableWishlist) - || product.ProductType == ProductType.GroupedProduct; + // 'add to cart', 'add to wishlist' buttons. + model.AddToCart.DisableBuyButton = !displayPrices || product.DisableBuyButton || + !_services.Permissions.Authorize(StandardPermissionProvider.EnableShoppingCart); - //pre-order - model.AddToCart.AvailableForPreOrder = product.AvailableForPreOrder; + model.AddToCart.DisableWishlistButton = !displayPrices || product.DisableWishlistButton + || product.ProductType == ProductType.GroupedProduct + || !_services.Permissions.Authorize(StandardPermissionProvider.EnableWishlist); - //customer entered price model.AddToCart.CustomerEntersPrice = product.CustomerEntersPrice; if (model.AddToCart.CustomerEntersPrice) { @@ -1251,7 +1244,6 @@ public ProductDetailsModel PrepareProductDetailModel( _priceFormatter.FormatPrice(maximumCustomerEnteredPrice, true, false)); } - //allowed quantities var allowedQuantities = product.ParseAllowedQuatities(); foreach (var qty in allowedQuantities) { diff --git a/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs b/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs index 0eca3e49ef..dd24d6b44e 100644 --- a/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs @@ -200,8 +200,9 @@ public partial class ProductPriceModel : ModelBase public int ProductId { get; set; } public bool HidePrices { get; set; } + public bool ShowLoginNote { get; set; } - public bool DynamicPriceUpdate { get; set; } + public bool DynamicPriceUpdate { get; set; } public bool BundleItemShowBasePrice { get; set; } public string NoteWithDiscount { get; set; } diff --git a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_product.scss b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_product.scss index 61eaf65e91..f98a98a2dd 100644 --- a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_product.scss +++ b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_product.scss @@ -240,7 +240,8 @@ $pd-offer-spacing: $grid-gutter-width / 2; color: $danger; } -.pd-callforprice { +.pd-callforprice, +.pd-loginforprice { font-size: 1.2rem; } diff --git a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.Price.cshtml b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.Price.cshtml index 082bb6b0e5..aaf802985b 100644 --- a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.Price.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.Price.cshtml @@ -6,71 +6,70 @@ @using SmartStore.Core.Domain.Catalog; @{ - if (Model.AddToCart.DisableBuyButton && Model.AddToCart.DisableWishlistButton) + if (Model.AddToCart.DisableBuyButton && Model.AddToCart.DisableWishlistButton && !Model.ProductPrice.ShowLoginNote) { return; } +} - var isBundleItem = Model.IsBundlePart; - var priceModel = Model.ProductPrice; - - var currentPrice = priceModel.PriceWithDiscount.NullEmpty() ?? priceModel.Price; - var currentPriceValue = priceModel.PriceWithDiscountValue > 0 ? priceModel.PriceWithDiscountValue : priceModel.PriceValue; - var currentPriceLabel = priceModel.NoteWithDiscount; +
+ @if (Model.ProductPrice.ShowLoginNote) + { +

@T("Products.LoginForPrice")

+ } + else if (Model.ProductPrice.CallForPrice) + { +

@T("Products.CallForPrice")

+ } + else + { + var isBundleItem = Model.IsBundlePart; + var priceModel = Model.ProductPrice; + var currentPrice = priceModel.PriceWithDiscount.NullEmpty() ?? priceModel.Price; + var currentPriceValue = priceModel.PriceWithDiscountValue > 0 ? priceModel.PriceWithDiscountValue : priceModel.PriceValue; + var currentPriceLabel = priceModel.NoteWithDiscount; - var oldPrice1 = priceModel.OldPrice; - var oldPrice1Label = T("Products.Price.OldPrice").Text; + var oldPrice1 = priceModel.OldPrice; + var oldPrice1Label = T("Products.Price.OldPrice").Text; - var oldPrice2 = priceModel.PriceWithDiscountValue < priceModel.PriceValue ? priceModel.Price : ""; - var oldPrice2Label = priceModel.NoteWithoutDiscount; + var oldPrice2 = priceModel.PriceWithDiscountValue < priceModel.PriceValue ? priceModel.Price : ""; + var oldPrice2Label = priceModel.NoteWithoutDiscount; - var basePriceEnabled = isBundleItem ? priceModel.BundleItemShowBasePrice : Model.IsBasePriceEnabled; + var basePriceEnabled = isBundleItem ? priceModel.BundleItemShowBasePrice : Model.IsBasePriceEnabled; - if (oldPrice2.HasValue() && !oldPrice1.HasValue()) - { - oldPrice1 = oldPrice2; - oldPrice2 = null; - oldPrice2Label = null; - } + if (oldPrice2.HasValue() && !oldPrice1.HasValue()) + { + oldPrice1 = oldPrice2; + oldPrice2 = null; + oldPrice2Label = null; + } - if (priceModel.PriceValue == 0 && Model.DisplayTextForZeroPrices) - { - currentPrice = T("Products.Free").Text; - oldPrice1 = null; - oldPrice2 = null; - oldPrice2Label = null; - } + if (priceModel.PriceValue == 0 && Model.DisplayTextForZeroPrices) + { + currentPrice = T("Products.Free").Text; + oldPrice1 = null; + oldPrice2 = null; + oldPrice2Label = null; + } - var blocksHaveLabel = currentPriceLabel.HasValue() && oldPrice2Label.HasValue(); + var blocksHaveLabel = currentPriceLabel.HasValue() && oldPrice2Label.HasValue(); - var artPriceClasses = "pd-price"; - if (isBundleItem) - { - artPriceClasses += " pd-price-sm"; - } - if (priceModel.OldPrice.HasValue()) - { - artPriceClasses += " pd-price--offer"; - } - if (priceModel.CallForPrice) - { - artPriceClasses += " pd-price--call"; - } + var artPriceClasses = "pd-price"; + if (isBundleItem) + { + artPriceClasses += " pd-price-sm"; + } + if (priceModel.OldPrice.HasValue()) + { + artPriceClasses += " pd-price--offer"; + } - var badgeClasses = ""; - if (Model.PriceDisplayStyle == PriceDisplayStyle.BadgeAll || (Model.PriceDisplayStyle == PriceDisplayStyle.BadgeFreeProductsOnly && priceModel.PriceValue == 0)) - { - badgeClasses = Model.PriceDisplayStyle == PriceDisplayStyle.BadgeAll && priceModel.SavingAmount.HasValue() ? "text-danger" : "text-success"; - } -} + var badgeClasses = ""; + if (Model.PriceDisplayStyle == PriceDisplayStyle.BadgeAll || (Model.PriceDisplayStyle == PriceDisplayStyle.BadgeFreeProductsOnly && priceModel.PriceValue == 0)) + { + badgeClasses = Model.PriceDisplayStyle == PriceDisplayStyle.BadgeAll && priceModel.SavingAmount.HasValue() ? "text-danger" : "text-success"; + } -
- @if (priceModel.CallForPrice) - { -

@T("Products.CallForPrice")

- } - else - {
diff --git a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml index 38c74770a6..2f3a25865e 100644 --- a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml @@ -7,9 +7,9 @@
- @if (!Model.AddToCart.DisableBuyButton || !Model.AddToCart.DisableWishlistButton) + @if (!Model.AddToCart.DisableBuyButton || !Model.AddToCart.DisableWishlistButton || Model.ProductPrice.ShowLoginNote) { - if (Model.AddToCart.CustomerEntersPrice) + if (Model.AddToCart.CustomerEntersPrice && !Model.ProductPrice.HidePrices) { var dataDictCustomerPrice = new ViewDataDictionary(); dataDictCustomerPrice.TemplateInfo.HtmlFieldPrefix = string.Format("addtocart_{0}", Model.Id); From c2ff8a41f62c05b3dd4e0a1d1c952115d01b842e Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 11 Feb 2019 15:18:18 +0100 Subject: [PATCH 030/575] Link builder --- .../Migrations/MigrationsConfiguration.cs | 2 + .../Components/EntityPicker/EntityPicker.cs | 8 +- .../EntityPicker/EntityPickerBuilder.cs | 16 +- .../Controllers/EntityController.cs | 344 ++++++++++-------- .../Models/Entity/EntityPickerModel.cs | 14 +- .../Scripts/smartstore.entityPicker.js | 54 +-- .../Views/Entity/Partials/Picker.List.cshtml | 29 +- .../Views/Entity/Partials/Picker.cshtml | 3 +- .../Shared/Components/EntityPicker.cshtml | 6 +- .../Views/Shared/EditorTemplates/Link.cshtml | 125 +++++-- 10 files changed, 363 insertions(+), 238 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 977bc6ccb4..620cabff20 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -642,6 +642,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Click Download to install a new language including all localized resources. On translate.smartstore.com you will find more details about available resources.", "Klicken Sie auf Download, um eine neue Sprache mit allen lokalisierten Ressourcen zu installieren. Auf translate.smartstore.com finden Sie weitere Details zu verfügbaren Ressourcen."); + builder.AddOrUpdate("Common.Url", "URL", "URL"); + builder.AddOrUpdate("Common.File", "File", "Datei"); builder.AddOrUpdate("Common.Entity.Product", "Product", "Produkt"); builder.AddOrUpdate("Common.Entity.Category", "Category", "Warengruppe"); builder.AddOrUpdate("Common.Entity.Manufacturer", "Manufacturer", "Hersteller"); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs index 161f88beca..fcca6a595b 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs @@ -1,5 +1,4 @@ -using System; -namespace SmartStore.Web.Framework.UI +namespace SmartStore.Web.Framework.UI { public class EntityPicker : Component { @@ -16,8 +15,9 @@ public EntityPicker() } public string EntityType { get; set; } + public int LanguageId { get; set; } - public string TargetInputSelector + public string TargetInputSelector { get { return HtmlAttributes["data-target"] as string; } set { HtmlAttributes["data-target"] = value; } @@ -32,7 +32,7 @@ public string TargetInputSelector public bool DisableGroupedProducts { get; set; } public bool DisableBundleProducts { get; set; } public int[] DisabledEntityIds { get; set; } - public int[] SelectedEntityIds { get; set; } + public string[] Selected { get; set; } public bool EnableThumbZoomer { get; set; } public bool HighlightSearchTerm { get; set; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs index a7b8503942..78b3d4867b 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Text; using System.Web.Mvc; using System.Web.Routing; -using System.Web.WebPages; namespace SmartStore.Web.Framework.UI { @@ -31,7 +27,13 @@ public EntityPickerBuilder EntityType(string value) return this; } - public EntityPickerBuilder Caption(string value) + public EntityPickerBuilder LanguageId(int value) + { + base.Component.LanguageId = value; + return this; + } + + public EntityPickerBuilder Caption(string value) { base.Component.Caption = value; return this; @@ -94,9 +96,9 @@ public EntityPickerBuilder DisabledEntityIds(params int[] values) return this; } - public EntityPickerBuilder SelectedEntityIds(params int[] values) + public EntityPickerBuilder Selected(params string[] values) { - base.Component.SelectedEntityIds = values; + base.Component.Selected = values; return this; } diff --git a/src/Presentation/SmartStore.Web/Controllers/EntityController.cs b/src/Presentation/SmartStore.Web/Controllers/EntityController.cs index 4c32bd524f..84c6ce1e7d 100644 --- a/src/Presentation/SmartStore.Web/Controllers/EntityController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/EntityController.cs @@ -7,6 +7,7 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Topics; using SmartStore.Core.Search; using SmartStore.Services.Catalog; using SmartStore.Services.Customers; @@ -14,6 +15,7 @@ using SmartStore.Services.Search; using SmartStore.Services.Security; using SmartStore.Services.Seo; +using SmartStore.Services.Topics; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Models.Entity; @@ -31,8 +33,9 @@ public partial class EntityController : PublicControllerBase private readonly ICustomerService _customerService; private readonly ICategoryService _categoryService; private readonly IUrlRecordService _urlRecordService; + private readonly ITopicService _topicService; - public EntityController( + public EntityController( ICatalogSearchService catalogSearchService, CatalogSettings catalogSettings, MediaSettings mediaSettings, @@ -41,7 +44,8 @@ public EntityController( IManufacturerService manufacturerService, ICustomerService customerService, ICategoryService categoryService, - IUrlRecordService urlRecordService) + IUrlRecordService urlRecordService, + ITopicService topicService) { _catalogSearchService = catalogSearchService; _catalogSettings = catalogSettings; @@ -52,14 +56,13 @@ public EntityController( _customerService = customerService; _categoryService = categoryService; _urlRecordService = urlRecordService; + _topicService = topicService; } #region Entity Picker public ActionResult Picker(EntityPickerModel model) { - model.PageSize = 96; - if (model.EntityType.IsCaseInsensitiveEqual("product")) { ViewBag.AvailableCategories = _categoryService.GetCategoryTree(includeHidden: true) @@ -79,7 +82,8 @@ public ActionResult Picker(EntityPickerModel model) } else if (model.EntityType.IsCaseInsensitiveEqual("customer")) { - ViewBag.AvailableCustomerSearchTypes = new List { + ViewBag.AvailableCustomerSearchTypes = new List + { new SelectListItem { Text = "Name", Value = "Name", Selected = true }, new SelectListItem { Text = "Email", Value = "Email" } }; @@ -96,87 +100,87 @@ public ActionResult Picker(EntityPickerModel model) [HttpPost] public ActionResult Picker(EntityPickerModel model, FormCollection form) { - model.PageSize = 96; - try { - var language = Services.WorkContext.WorkingLanguage; - var disableIf = model.DisableIf.SplitSafe(",").Select(x => x.ToLower().Trim()).ToList(); + var languageId = model.LanguageId == 0 ? Services.WorkContext.WorkingLanguage.Id : model.LanguageId; + var disableIf = model.DisableIf.SplitSafe(",").Select(x => x.ToLower().Trim()).ToList(); var disableIds = model.DisableIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); - var selIds = new HashSet(model.PreselectedEntityIds.ToIntArray()); + var selected = model.Selected.SplitSafe(","); + var returnLink = model.ReturnField.IsCaseInsensitiveEqual("link"); + var returnSku = model.ReturnField.IsCaseInsensitiveEqual("sku"); - using (var scope = new DbContextScope(Services.DbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) + using (var scope = new DbContextScope(Services.DbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) { - if (model.EntityType.IsCaseInsensitiveEqual("product")) - { - model.SearchTerm = model.SearchTerm.TrimSafe(); - - var hasPermission = Services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog); - var disableIfNotSimpleProduct = disableIf.Contains("notsimpleproduct"); - var disableIfGroupedProduct = disableIf.Contains("groupedproduct"); - var labelTextGrouped = T("Admin.Catalog.Products.ProductType.GroupedProduct.Label").Text; - var labelTextBundled = T("Admin.Catalog.Products.ProductType.BundledProduct.Label").Text; - var sku = T("Products.Sku").Text; - - var fields = new List { "name" }; - if (_searchSettings.SearchFields.Contains("sku")) - fields.Add("sku"); - if (_searchSettings.SearchFields.Contains("shortdescription")) - fields.Add("shortdescription"); - - var searchQuery = new CatalogSearchQuery(fields.ToArray(), model.SearchTerm) - .HasStoreId(model.StoreId); - - if (!hasPermission) - { - searchQuery = searchQuery.VisibleOnly(Services.WorkContext.CurrentCustomer); - } - - if (model.ProductTypeId > 0) - { - searchQuery = searchQuery.IsProductType((ProductType)model.ProductTypeId); - } - - if (model.ManufacturerId != 0) - { - searchQuery = searchQuery.WithManufacturerIds(null, model.ManufacturerId); - } - - if (model.CategoryId != 0) - { - var node = _categoryService.GetCategoryTree(model.CategoryId, true); - if (node != null) - { - searchQuery = searchQuery.WithCategoryIds(null, node.Flatten(true).Select(x => x.Id).ToArray()); - } - } + if (model.EntityType.IsCaseInsensitiveEqual("product")) + { + model.SearchTerm = model.SearchTerm.TrimSafe(); + + var hasPermission = Services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog); + var disableIfNotSimpleProduct = disableIf.Contains("notsimpleproduct"); + var disableIfGroupedProduct = disableIf.Contains("groupedproduct"); + var labelTextGrouped = T("Admin.Catalog.Products.ProductType.GroupedProduct.Label").Text; + var labelTextBundled = T("Admin.Catalog.Products.ProductType.BundledProduct.Label").Text; + var sku = T("Products.Sku").Text; + + var fields = new List { "name" }; + if (_searchSettings.SearchFields.Contains("sku")) + fields.Add("sku"); + if (_searchSettings.SearchFields.Contains("shortdescription")) + fields.Add("shortdescription"); + + var searchQuery = new CatalogSearchQuery(fields.ToArray(), model.SearchTerm) + .HasStoreId(model.StoreId); + + if (!hasPermission) + { + searchQuery = searchQuery.VisibleOnly(Services.WorkContext.CurrentCustomer); + } + + if (model.ProductTypeId > 0) + { + searchQuery = searchQuery.IsProductType((ProductType)model.ProductTypeId); + } + + if (model.ManufacturerId != 0) + { + searchQuery = searchQuery.WithManufacturerIds(null, model.ManufacturerId); + } + + if (model.CategoryId != 0) + { + var node = _categoryService.GetCategoryTree(model.CategoryId, true); + if (node != null) + { + searchQuery = searchQuery.WithCategoryIds(null, node.Flatten(true).Select(x => x.Id).ToArray()); + } + } var skip = model.PageIndex * model.PageSize; var query = _catalogSearchService.PrepareQuery(searchQuery); - var products = query - .Select(x => new - { - x.Id, - x.Sku, - x.Name, - x.Published, - x.ProductTypeId, - x.MainPictureId - }) - .OrderBy(x => x.Name) - .Skip(() => skip) - .Take(() => model.PageSize) - .ToList(); - - var allPictureIds = products.Select(x => x.MainPictureId.GetValueOrDefault()); - var allPictureInfos = _pictureService.GetPictureInfos(allPictureIds); - var slugs = model.ReturnField.IsCaseInsensitiveEqual("link") + var products = query + .Select(x => new + { + x.Id, + x.Sku, + x.Name, + x.Published, + x.ProductTypeId, + x.MainPictureId + }) + .OrderBy(x => x.Name) + .Skip(() => skip) + .Take(() => model.PageSize) + .ToList(); + + var allPictureIds = products.Select(x => x.MainPictureId.GetValueOrDefault()); + var allPictureInfos = _pictureService.GetPictureInfos(allPictureIds); + var slugs = returnLink ? _urlRecordService.GetUrlRecordCollection(nameof(Product), null, products.Select(x => x.Id).ToArray()) : null; model.SearchResult = products - .Select(x => + .Select(x => { var item = new EntityPickerModel.SearchResultModel { @@ -184,80 +188,94 @@ public ActionResult Picker(EntityPickerModel model, FormCollection form) Title = x.Name, Summary = x.Sku, SummaryTitle = "{0}: {1}".FormatInvariant(sku, x.Sku.NaIfEmpty()), - Published = hasPermission ? x.Published : (bool?)null, - Selected = selIds.Contains(x.Id) + Published = hasPermission ? x.Published : (bool?)null }; - item.ReturnValue = slugs != null - ? Url.RouteUrl("Product", new { SeName = slugs.GetSlug(language.Id, x.Id) }) - : (model.ReturnField.IsCaseInsensitiveEqual("sku") ? x.Sku : x.Id.ToString()); - - if (disableIfNotSimpleProduct) - { - item.Disable = x.ProductTypeId != (int)ProductType.SimpleProduct; - } - else if (disableIfGroupedProduct) - { - item.Disable = x.ProductTypeId == (int)ProductType.GroupedProduct; - } - - if (!item.Disable && disableIds.Contains(x.Id)) - { - item.Disable = true; - } - - if (x.ProductTypeId == (int)ProductType.GroupedProduct) - { - item.LabelText = labelTextGrouped; - item.LabelClassName = "badge-success"; - } - else if (x.ProductTypeId == (int)ProductType.BundledProduct) - { - item.LabelText = labelTextBundled; - item.LabelClassName = "badge-info"; - } - - var pictureInfo = allPictureInfos.Get(x.MainPictureId.GetValueOrDefault()); - var fallbackType = _catalogSettings.HideProductDefaultPictures ? FallbackPictureType.NoFallback : FallbackPictureType.Entity; - - item.ImageUrl = _pictureService.GetUrl( - allPictureInfos.Get(x.MainPictureId.GetValueOrDefault()), - _mediaSettings.ProductThumbPictureSizeOnProductDetailsPage, - fallbackType); - - return item; - }) - .ToList(); - } + if (returnLink) + { + item.ReturnValue = Url.RouteUrl("Product", new { SeName = slugs.GetSlug(languageId, x.Id) }); + } + else if (returnSku) + { + item.ReturnValue = x.Sku; + } + else + { + item.ReturnValue = x.Id.ToString(); + } + + item.Selected = selected.Contains(item.ReturnValue); + + if (disableIfNotSimpleProduct) + { + item.Disable = x.ProductTypeId != (int)ProductType.SimpleProduct; + } + else if (disableIfGroupedProduct) + { + item.Disable = x.ProductTypeId == (int)ProductType.GroupedProduct; + } + + if (!item.Disable && disableIds.Contains(x.Id)) + { + item.Disable = true; + } + + if (x.ProductTypeId == (int)ProductType.GroupedProduct) + { + item.LabelText = labelTextGrouped; + item.LabelClassName = "badge-success"; + } + else if (x.ProductTypeId == (int)ProductType.BundledProduct) + { + item.LabelText = labelTextBundled; + item.LabelClassName = "badge-info"; + } + + var pictureInfo = allPictureInfos.Get(x.MainPictureId.GetValueOrDefault()); + var fallbackType = _catalogSettings.HideProductDefaultPictures ? FallbackPictureType.NoFallback : FallbackPictureType.Entity; + + item.ImageUrl = _pictureService.GetUrl( + allPictureInfos.Get(x.MainPictureId.GetValueOrDefault()), + _mediaSettings.ProductThumbPictureSizeOnProductDetailsPage, + fallbackType); + + return item; + }) + .ToList(); + } else if (model.EntityType.IsCaseInsensitiveEqual("category")) - { + { var categories = _categoryService.GetAllCategories(model.SearchTerm, showHidden: true); var allPictureIds = categories.Select(x => x.PictureId.GetValueOrDefault()); var allPictureInfos = _pictureService.GetPictureInfos(allPictureIds); - var slugs = model.ReturnField.IsCaseInsensitiveEqual("link") + var slugs = returnLink ? _urlRecordService.GetUrlRecordCollection(nameof(Category), null, categories.Select(x => x.Id).ToArray()) : null; model.SearchResult = categories .Select(x => { + var path = ((ICategoryNode)x).GetCategoryPath(_categoryService, languageId, "({0})"); + var returnValue = returnLink + ? Url.RouteUrl("Category", new { SeName = slugs.GetSlug(languageId, x.Id) }) + : x.Id.ToString(); + var item = new EntityPickerModel.SearchResultModel { Id = x.Id, Title = x.Name, - Summary = x.Description.Truncate(120, "…"), - SummaryTitle = x.Name, + Summary = path, + SummaryTitle = path, Published = x.Published, - Selected = selIds.Contains(x.Id) + ReturnValue = returnValue, + Selected = selected.Contains(returnValue), + Disable = disableIds.Contains(x.Id) }; - item.ReturnValue = slugs != null - ? Url.RouteUrl("Category", new { SeName = slugs.GetSlug(language.Id, x.Id) }) - : x.Id.ToString(); - - if (!item.Disable && disableIds.Contains(x.Id)) + if (x.Alias.HasValue()) { - item.Disable = true; + item.LabelText = x.Alias; + item.LabelClassName = "badge-secondary"; } var pictureInfo = allPictureInfos.Get(x.PictureId.GetValueOrDefault()); @@ -272,37 +290,32 @@ public ActionResult Picker(EntityPickerModel model, FormCollection form) }) .ToList(); } - else if (model.EntityType.IsCaseInsensitiveEqual("manufacturer")) + else if (model.EntityType.IsCaseInsensitiveEqual("manufacturer")) { var manufacturers = _manufacturerService.GetAllManufacturers(model.SearchTerm, model.PageIndex, model.PageSize, showHidden: true); var allPictureIds = manufacturers.Select(x => x.PictureId.GetValueOrDefault()); var allPictureInfos = _pictureService.GetPictureInfos(allPictureIds); - var slugs = model.ReturnField.IsCaseInsensitiveEqual("link") + var slugs = returnLink ? _urlRecordService.GetUrlRecordCollection(nameof(Manufacturer), null, manufacturers.Select(x => x.Id).ToArray()) : null; model.SearchResult = manufacturers .Select(x => { + var returnValue = returnLink + ? Url.RouteUrl("Manufacturer", new { SeName = slugs.GetSlug(languageId, x.Id) }) + : x.Id.ToString(); + var item = new EntityPickerModel.SearchResultModel { Id = x.Id, - ReturnValue = x.Id.ToString(), Title = x.Name, - SummaryTitle = x.Name, Published = x.Published, - Selected = selIds.Contains(x.Id) + ReturnValue = returnValue, + Selected = selected.Contains(returnValue), + Disable = disableIds.Contains(x.Id) }; - item.ReturnValue = slugs != null - ? Url.RouteUrl("Manufacturer", new { SeName = slugs.GetSlug(language.Id, x.Id) }) - : x.Id.ToString(); - - if (!item.Disable && disableIds.Contains(x.Id)) - { - item.Disable = true; - } - var pictureInfo = allPictureInfos.Get(x.PictureId.GetValueOrDefault()); var fallbackType = _catalogSettings.HideProductDefaultPictures ? FallbackPictureType.NoFallback : FallbackPictureType.Entity; @@ -318,14 +331,13 @@ public ActionResult Picker(EntityPickerModel model, FormCollection form) else if (model.EntityType.IsCaseInsensitiveEqual("customer")) { var registeredRoleId = _customerService.GetCustomerRoleBySystemName("Registered").Id; - var searchTermName = string.Empty; var searchTermEmail = string.Empty; var searchTermCustomerNumber = string.Empty; if (model.CustomerSearchType.IsCaseInsensitiveEqual("Name")) searchTermName = model.SearchTerm; - else if(model.CustomerSearchType.IsCaseInsensitiveEqual("Email")) + else if (model.CustomerSearchType.IsCaseInsensitiveEqual("Email")) searchTermEmail = model.SearchTerm; else if (model.CustomerSearchType.IsCaseInsensitiveEqual("CustomerNumber")) searchTermCustomerNumber = model.SearchTerm; @@ -341,26 +353,54 @@ public ActionResult Picker(EntityPickerModel model, FormCollection form) }; var customers = _customerService.SearchCustomers(q); - + model.SearchResult = customers .Select(x => { + var fullName = x.GetFullName(); + var item = new EntityPickerModel.SearchResultModel { Id = x.Id, ReturnValue = x.Id.ToString(), - Title = x.Username, - Summary = x.GetFullName(), - SummaryTitle = x.GetFullName(), + Title = x.Username.NullEmpty() ?? x.Email, + Summary = fullName, + SummaryTitle = fullName, Published = true, - Selected = selIds.Contains(x.Id) + Selected = selected.Contains(x.Id.ToString()), + Disable = disableIds.Contains(x.Id) }; - if (!item.Disable && disableIds.Contains(x.Id)) + return item; + }) + .ToList(); + } + else if (model.EntityType.IsCaseInsensitiveEqual("topic")) + { + var topics = _topicService.GetAllTopics(0, model.PageIndex, model.PageSize, true); + var slugs = returnLink + ? _urlRecordService.GetUrlRecordCollection(nameof(Topic), null, topics.Select(x => x.Id).ToArray()) + : null; + + model.SearchResult = topics + .Select(x => + { + var returnValue = returnLink + ? Url.RouteUrl("Topic", new { SeName = slugs.GetSlug(languageId, x.Id) }) + : x.Id.ToString(); + + var item = new EntityPickerModel.SearchResultModel { - item.Disable = true; - } - + Id = x.Id, + Published = x.IsPublished, + Title = x.Title.NullEmpty() ?? x.SystemName, + Summary = x.SystemName, + SummaryTitle = x.SystemName, + ReturnValue = returnValue, + Selected = selected.Contains(returnValue), + Disable = disableIds.Contains(x.Id) + }; + return item; }) .ToList(); diff --git a/src/Presentation/SmartStore.Web/Models/Entity/EntityPickerModel.cs b/src/Presentation/SmartStore.Web/Models/Entity/EntityPickerModel.cs index 643c0128ec..eb6a248e31 100644 --- a/src/Presentation/SmartStore.Web/Models/Entity/EntityPickerModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Entity/EntityPickerModel.cs @@ -6,20 +6,26 @@ namespace SmartStore.Web.Models.Entity { public class EntityPickerModel : ModelBase { - public string EntityType { get; set; } + public EntityPickerModel() + { + PageSize = 96; + } + + public string EntityType { get; set; } public bool HighligtSearchTerm { get; set; } public string DisableIf { get; set; } public string DisableIds { get; set; } public string SearchTerm { get; set; } public string ReturnField { get; set; } public int MaxItems { get; set; } - public string PreselectedEntityIds { get; set; } + public string Selected { get; set; } public int PageIndex { get; set; } public int PageSize { get; set; } + public int LanguageId { get; set; } - public List SearchResult { get; set; } + public List SearchResult { get; set; } - #region SearchProperties + #region Search properties [SmartResourceDisplayName("Admin.Catalog.Products.List.SearchProductName")] public string ProductName { get; set; } diff --git a/src/Presentation/SmartStore.Web/Scripts/smartstore.entityPicker.js b/src/Presentation/SmartStore.Web/Scripts/smartstore.entityPicker.js index 856834fc66..e775021c03 100644 --- a/src/Presentation/SmartStore.Web/Scripts/smartstore.entityPicker.js +++ b/src/Presentation/SmartStore.Web/Scripts/smartstore.entityPicker.js @@ -76,7 +76,7 @@ returnField: 'id', delim: ',', targetInput: null, - preselectedIds: null, + selected: null, appendMode: true, maxItems: 0, onDialogLoading: null, @@ -84,7 +84,10 @@ onSelectionCompleted: null }; - options = $.extend({}, defaults, options); + // Use self.attr, not self.data! + options = $.extend({}, defaults, options); + options.entityType = self.attr('data-entitytype') || options.entityType; + options.caption = self.attr('data-caption') || options.caption; if (_.isEmpty(options.url)) { options.url = self.attr('data-url'); @@ -98,20 +101,22 @@ options.targetInput = $(self.data('target')).first(); } - if (options.targetInput && !_.isArray(options.preselectedIds)) { - var val = $(options.targetInput).val(); + if (options.targetInput && !_.isArray(options.selected)) { + var val = $(options.targetInput).val(); if (val.length > 0) { - options.preselectedIds = _.map(val.split(options.delim), function (x) { + options.selected = _.map(val.split(options.delim), function (x) { var result = x.trim(); - if (options.returnField.toLowerCase() === 'id') result = toInt(result); + if (options.returnField.toLowerCase() === 'id') { + result = toInt(result); + } return result; - }); + }); } } - if (!_.isArray(options.preselectedIds)) { - options.preselectedIds = []; - } + if (!_.isArray(options.selected)) { + options.selected = []; + } return options; } @@ -132,7 +137,7 @@ } function loadDialog(context /* button */, opt) { - var btn = $(context); + var btn = $(context); var dialog = $('#entpicker-' + opt.entityType + '-dialog'); function showAndFocusDialog() { @@ -151,16 +156,16 @@ if (dialog.length) { showAndFocusDialog(); } - else { + else { $.ajax({ cache: false, type: 'GET', data: { - "EntityType": opt.entityType, + "EntityType": opt.entityType, "HighligtSearchTerm": opt.highligtSearchTerm, "ReturnField": opt.returnField, "MaxItems": opt.maxItems, - "PreselectedEntityIds": opt.preselectedIds.join(), + "Selected": opt.selected.join(), "DisableIf": opt.disableIf, "DisableIds": opt.disableIds }, @@ -238,8 +243,8 @@ // lazy loading dialog.find('.modal-body').on('scroll', function (e) { - if ($('.load-more:not(.loading)').visible(true, false, 'vertical')) { - fillList(this, { append: true }); + if (dialog.find('.load-more:not(.loading)').visible(true, false, 'vertical')) { + fillList(this, { append: true }); } }); @@ -298,25 +303,27 @@ }; }); - var selectedIds = _.uniq(_.map(selectedItems, function (x) { + var selectedValues = _.uniq(_.map(selectedItems, function (x) { var result = x.id; - if (opts.returnField.toLowerCase() === 'id' && !_.isNumber(result)) result = toInt(result); + if (opts.returnField.toLowerCase() === 'id' && !_.isNumber(result)) { + result = toInt(result); + } return result; })); - if (opts.appendMode && _.isArray(opts.preselectedIds)) { - selectedIds = _.union(opts.preselectedIds, selectedIds); + if (opts.appendMode && _.isArray(opts.selected)) { + selectedValues = _.union(opts.selected, selectedValues); } if (opts.targetInput) { $(opts.targetInput) - .val(selectedIds.join(opts.delim)) + .val(selectedValues.join(opts.delim)) .focus() .blur(); } if (_.isFunction(opts.onSelectionCompleted)) { - if (opts.onSelectionCompleted(selectedIds, selectedItems, dialog)) { + if (opts.onSelectionCompleted(selectedValues, selectedItems, dialog)) { dialog.modal('hide'); } } @@ -340,7 +347,7 @@ var pageElement = dialog.find('input[name=PageIndex]'), pageIndex = parseInt(pageElement.val()); - pageElement.val(pageIndex + 1); + pageElement.val(pageIndex + 1); } else { dialog.find('input[name=PageIndex]').val('0'); @@ -370,7 +377,6 @@ list.stop().append(response); if (!opt.append) { - //dialog.find('.entpicker-filter').slideUp(200); showStatus(dialog); } diff --git a/src/Presentation/SmartStore.Web/Views/Entity/Partials/Picker.List.cshtml b/src/Presentation/SmartStore.Web/Views/Entity/Partials/Picker.List.cshtml index a56418423e..d598add3d4 100644 --- a/src/Presentation/SmartStore.Web/Views/Entity/Partials/Picker.List.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Entity/Partials/Picker.List.cshtml @@ -1,6 +1,5 @@ -@model EntityPickerModel - -@using SmartStore.Web.Models.Entity; +@using SmartStore.Web.Models.Entity; +@model EntityPickerModel @helper HighlightSearchTermInTitle(EntityPickerModel.SearchResultModel item) { @@ -18,16 +17,16 @@ {
-
-
- @if (item.ImageUrl.HasValue()) - { -
- -
- } -
-
+ @if (item.ImageUrl.HasValue()) + { +
+
+
+ +
+
+
+ }
@@ -43,13 +42,13 @@ } @item.Summary -
- +
} +
public partial class ResolveLinksHelper { - #region Fields /// /// The regular expression used to parse links. /// private static readonly Regex regex = new Regex("((http://|https://|www\\.)([A-Z0-9.\\-]{1,})\\.[0-9A-Z?;~&\\(\\)#,=\\-_\\./\\+]{2,})", RegexOptions.Compiled | RegexOptions.IgnoreCase); private const string link = "{2}"; private const int MAX_LENGTH = 50; - #endregion - - #region Utilities /// /// Shortens any absolute URL to a specified maximum length @@ -80,9 +76,7 @@ private static string ShortenUrl(string url, int max) return url; } - #endregion - #region Methods /// /// Formats the text /// @@ -93,21 +87,20 @@ public static string FormatText(string text) if (String.IsNullOrEmpty(text)) return string.Empty; - var info = CultureInfo.InvariantCulture; + var ci = CultureInfo.InvariantCulture; foreach (Match match in regex.Matches(text)) { if (!match.Value.Contains("://")) { - text = text.Replace(match.Value, string.Format(info, link, "http://", match.Value, ShortenUrl(match.Value, MAX_LENGTH))); + text = text.Replace(match.Value, string.Format(ci, link, "http://", match.Value, ShortenUrl(match.Value, MAX_LENGTH))); } else { - text = text.Replace(match.Value, string.Format(info, link, string.Empty, match.Value, ShortenUrl(match.Value, MAX_LENGTH))); + text = text.Replace(match.Value, string.Format(ci, link, string.Empty, match.Value, ShortenUrl(match.Value, MAX_LENGTH))); } } return text; } - #endregion } } diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 95a89237e6..429909dd73 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -80,6 +80,9 @@ ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll True + + ..\..\packages\HtmlSanitizer.4.0.205\lib\net45\HtmlSanitizer.dll + ..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll True diff --git a/src/Libraries/SmartStore.Core/packages.config b/src/Libraries/SmartStore.Core/packages.config index 657dc15b86..19d5cd9737 100644 --- a/src/Libraries/SmartStore.Core/packages.config +++ b/src/Libraries/SmartStore.Core/packages.config @@ -4,6 +4,7 @@ + diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs index 1cfb95ee34..7d3c6db822 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs @@ -113,7 +113,7 @@ public string FormatAttributes(Product product, string attributes, //encode (if required) if (htmlEncode) attributeName = HttpUtility.HtmlEncode(attributeName); - pvaAttribute = string.Format("{0}: {1}", attributeName, HtmlUtils.FormatText(valueStr, false, true, false, false, false, false)); + pvaAttribute = string.Format("{0}: {1}", attributeName, HtmlUtils.ConvertPlainTextToHtml(valueStr.HtmlEncode())); //we never encode multiline textbox input } else if (pva.AttributeControlType == AttributeControlType.FileUpload) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs index eb31743c5f..0a172ff592 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs @@ -11,9 +11,6 @@ using System.Collections.Generic; using System.Data; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using LumenWorks.Framework.IO.Csv; namespace SmartStore.Services.DataExchange.Csv diff --git a/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs b/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs index 41aa0c15bc..54a67d93b6 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs @@ -23,21 +23,12 @@ public static string FormatPostText(this ForumPost post) return string.Empty; } - switch (EngineContext.Current.Resolve().ForumEditor) - { - case EditorType.SimpleTextBox: - { - text = HtmlUtils.FormatText(text, false, true, false, false, false, false); - } - break; - case EditorType.BBCodeEditor: - { - text = HtmlUtils.FormatText(text, false, true, false, true, false, false); - } - break; - default: - break; - } + text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + + if (EngineContext.Current.Resolve().ForumEditor == EditorType.BBCodeEditor) + { + text = BBCodeHelper.ToHtml(text); + } return text; } @@ -83,8 +74,8 @@ public static string FormatForumSignatureText(this string text) return string.Empty; } - return HtmlUtils.FormatText(text, false, true, false, false, false, false); - } + return HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + } /// /// Formats the private message text @@ -101,7 +92,8 @@ public static string FormatPrivateMessageText(this PrivateMessage message) return string.Empty; } - return HtmlUtils.FormatText(text, false, true, false, true, false, false); + text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + return BBCodeHelper.ToHtml(text); } /// diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs index b328ee6563..b88fda1d9a 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs @@ -451,8 +451,8 @@ protected virtual object CreateModelPart(ReturnRequest part, MessageContext mess { "Reason", part.ReasonForReturn.NullEmpty() }, { "Status", part.ReturnRequestStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id) }, { "RequestedAction", part.RequestedAction.NullEmpty() }, - { "CustomerComments", HtmlUtils.FormatText(part.CustomerComments, true, false, false, false, false, false).NullEmpty() }, - { "StaffNotes", HtmlUtils.FormatText(part.StaffNotes, true, false, false, false, false, false).NullEmpty() }, + { "CustomerComments", HtmlUtils.StripTags(part.CustomerComments).NullEmpty() }, + { "StaffNotes", HtmlUtils.StripTags(part.StaffNotes).NullEmpty() }, { "Quantity", part.Quantity }, { "RefundToWallet", part.RefundToWallet }, { "Url", BuildActionUrl("Edit", "ReturnRequest", new { id = part.Id, area = "admin" }, messageContext) } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs index b28a37af0b..7d18234bba 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs @@ -668,7 +668,7 @@ protected virtual object CreateModelPart(GiftCard part, MessageContext messageCo var message = (string)null; if (part.Message.HasValue()) { - message = HtmlUtils.FormatText(part.Message, true, false, false, false, false, false); + message = HtmlUtils.StripTags(part.Message); } m["Message"] = message; @@ -746,7 +746,7 @@ protected virtual object CreateModelPart(ProductReview part, MessageContext mess var m = new Dictionary { { "Title", part.Title.NullEmpty() }, - { "Text", HtmlUtils.FormatText(part.ReviewText, true, false, false, false, false, false).NullEmpty() }, + { "Text", HtmlUtils.StripTags(part.ReviewText).NullEmpty() }, { "Rating", part.Rating } }; @@ -840,7 +840,7 @@ protected virtual object CreateModelPart(NewsComment part, MessageContext messag { { "NewsTitle", part.NewsItem.Title.NullEmpty() }, { "Title", part.CommentTitle.NullEmpty() }, - { "Text", HtmlUtils.FormatText(part.CommentText, true, false, false, false, false, false).NullEmpty() }, + { "Text", HtmlUtils.StripTags(part.CommentText).NullEmpty() }, { "NewsUrl", BuildRouteUrl("NewsItem", new { SeName = part.NewsItem.GetSeName(messageContext.Language.Id) }, messageContext) } }; diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs index 45da5bef58..627a5b692d 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs @@ -102,7 +102,7 @@ public string FormatAttributes(string attributes, attributeName = HttpUtility.HtmlEncode(attributeName); caAttribute = string.Format("{0}: {1}", attributeName, - HtmlUtils.FormatText(valueStr.EmptyNull().Replace(":", ""), false, true, false, false, false, false)); + HtmlUtils.ConvertPlainTextToHtml(valueStr.EmptyNull().Replace(":", "").HtmlEncode())); //we never encode multiline textbox input } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs b/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs index e896a7c12e..5a5803d728 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs @@ -40,7 +40,7 @@ public static string FormatOrderNoteText(this OrderNote orderNote) if (orderNote.Note.IsEmpty()) return string.Empty; - return HtmlUtils.FormatText(orderNote.Note, false, true, true, false, false, false); + return HtmlUtils.ConvertPlainTextToHtml(orderNote.Note); } public static List GetBundleData(this OrderItem orderItem) diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index d5f655e858..97efdb94a4 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -231,6 +231,7 @@ + diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs index 13a4f221b4..21c4b304f1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs @@ -4,6 +4,7 @@ using System.Web.Mvc; using SmartStore.Admin.Models.Blogs; using SmartStore.Core.Domain.Blogs; +using SmartStore.Core.Html; using SmartStore.Services.Blogs; using SmartStore.Services.Customers; using SmartStore.Services.Helpers; @@ -301,7 +302,7 @@ public ActionResult Comments(int? filterByBlogPostId, GridCommand command) commentModel.CustomerId = blogComment.CustomerId; commentModel.IpAddress = blogComment.IpAddress; commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(blogComment.CreatedOnUtc, DateTimeKind.Utc); - commentModel.Comment = Core.Html.HtmlUtils.FormatText(blogComment.CommentText, false, true, false, false, false, false); + commentModel.Comment = HtmlUtils.ConvertPlainTextToHtml(blogComment.CommentText.HtmlEncode()); if (customer == null) commentModel.CustomerName = "".NaIfEmpty(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs index c54dd3d1ad..15a65c212f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs @@ -4,6 +4,7 @@ using System.Web.Mvc; using SmartStore.Admin.Models.News; using SmartStore.Core.Domain.News; +using SmartStore.Core.Html; using SmartStore.Services.Customers; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; @@ -328,7 +329,7 @@ public ActionResult Comments(int? filterByNewsItemId, GridCommand command) commentModel.IpAddress = newsComment.IpAddress; commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(newsComment.CreatedOnUtc, DateTimeKind.Utc); commentModel.CommentTitle = newsComment.CommentTitle; - commentModel.CommentText = Core.Html.HtmlUtils.FormatText(newsComment.CommentText, false, true, false, false, false, false); + commentModel.CommentText = HtmlUtils.ConvertPlainTextToHtml(newsComment.CommentText.HtmlEncode()); if (customer == null) commentModel.CustomerName = "".NaIfEmpty(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs index 29ed0402f2..8e8acf50f5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs @@ -4,6 +4,7 @@ using System.Web.Mvc; using SmartStore.Admin.Models.Catalog; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Html; using SmartStore.Services.Catalog; using SmartStore.Services.Customers; using SmartStore.Services.Helpers; @@ -51,8 +52,7 @@ public ProductReviewController(ICustomerContentService customerContentService, #region Utilities [NonAction] - private void PrepareProductReviewModel(ProductReviewModel model, - ProductReview productReview, bool excludeProperties, bool formatReviewText) + private void PrepareProductReviewModel(ProductReviewModel model, ProductReview productReview, bool excludeProperties, bool formatReviewText) { if (model == null) throw new ArgumentNullException("model"); @@ -80,7 +80,7 @@ private void PrepareProductReviewModel(ProductReviewModel model, { model.Title = productReview.Title; if (formatReviewText) - model.ReviewText = Core.Html.HtmlUtils.FormatText(productReview.ReviewText, false, true, false, false, false, false); + model.ReviewText = HtmlUtils.ConvertPlainTextToHtml(productReview.ReviewText.HtmlEncode()); else model.ReviewText = productReview.ReviewText; model.IsApproved = productReview.IsApproved; diff --git a/src/Presentation/SmartStore.Web/Controllers/HomeController.cs b/src/Presentation/SmartStore.Web/Controllers/HomeController.cs index 9d6369178a..e1f639975e 100644 --- a/src/Presentation/SmartStore.Web/Controllers/HomeController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/HomeController.cs @@ -106,7 +106,7 @@ public ActionResult ContactUsSend(ContactUsModel model, bool captchaValid) var email = model.Email.Trim(); var fullName = model.FullName; var subject = T("ContactUs.EmailSubject", Services.StoreContext.CurrentStore.Name); - var body = Core.Html.HtmlUtils.FormatText(model.Enquiry, false, true, false, false, false, false); + var body = Core.Html.HtmlUtils.ConvertPlainTextToHtml(model.Enquiry.HtmlEncode()); // Required for some SMTP servers EmailAddress sender = null; diff --git a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs index 2591bfcc9b..cec7b89b5e 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs @@ -914,7 +914,7 @@ public ActionResult AskQuestionSend(ProductAskQuestionModel model, bool captchaV model.SenderEmail, model.SenderName, model.SenderPhone, - Core.Html.HtmlUtils.FormatText(model.Question, false, true, false, false, false, false)); + Core.Html.HtmlUtils.ConvertPlainTextToHtml(model.Question.HtmlEncode())); if (msg?.Email?.Id != null) { @@ -987,7 +987,7 @@ public ActionResult EmailAFriendSend(ProductEmailAFriendModel model, int id, boo product, model.YourEmailAddress, model.FriendEmail, - Core.Html.HtmlUtils.FormatText(model.PersonalMessage, false, true, false, false, false, false)); + Core.Html.HtmlUtils.ConvertPlainTextToHtml(model.PersonalMessage.HtmlEncode())); model.ProductId = product.Id; model.ProductName = product.GetLocalized(x => x.Name); diff --git a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs index a72168575e..9883377cc8 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs @@ -2551,7 +2551,7 @@ public ActionResult EmailWishlistSend(WishlistEmailAFriendModel model, bool capt _workContext.CurrentCustomer, model.YourEmailAddress, model.FriendEmail, - Core.Html.HtmlUtils.FormatText(model.PersonalMessage, false, true, false, false, false, false)); + Core.Html.HtmlUtils.ConvertPlainTextToHtml(model.PersonalMessage.HtmlEncode())); model.SuccessfullySent = true; model.Result = _localizationService.GetResource("Wishlist.EmailAFriend.SuccessfullySent"); diff --git a/src/Presentation/SmartStore.Web/Models/Common/ContactUsModel.cs b/src/Presentation/SmartStore.Web/Models/Common/ContactUsModel.cs index ed5b631564..7ae286f17c 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/ContactUsModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/ContactUsModel.cs @@ -3,6 +3,7 @@ using SmartStore.Core.Domain.Customers; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Security; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; @@ -11,16 +12,14 @@ namespace SmartStore.Web.Models.Common [Validator(typeof(ContactUsValidator))] public partial class ContactUsModel : ModelBase { - [AllowHtml] [SmartResourceDisplayName("ContactUs.Email")] [DataType(DataType.EmailAddress)] public string Email { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("ContactUs.Enquiry")] public string Enquiry { get; set; } - [AllowHtml] [SmartResourceDisplayName("ContactUs.FullName")] public string FullName { get; set; } public bool FullNameRequired { get; set; } diff --git a/src/Tests/SmartStore.Core.Tests/ExtensionsTests.cs b/src/Tests/SmartStore.Core.Tests/ExtensionsTests.cs index 79e4bd471d..a0ceb0110e 100644 --- a/src/Tests/SmartStore.Core.Tests/ExtensionsTests.cs +++ b/src/Tests/SmartStore.Core.Tests/ExtensionsTests.cs @@ -31,7 +31,7 @@ public void Can_check_IsNullOrDefault() public void Can_Strip_Html() { var html = GetFileStream("testdata.html").AsString(); - var text = html.RemoveHtml().Trim(); + var text = html.RemoveHtml(); Assert.IsTrue(text.StartsWith("Produktmerkmale"), "StartsWith Produktmerkmale"); Assert.IsFalse(text.Contains("function()"), "No function()"); diff --git a/src/Tests/SmartStore.Core.Tests/Files/testdata.html b/src/Tests/SmartStore.Core.Tests/Files/testdata.html index 7fb6d933a3..1b9a9217b4 100644 --- a/src/Tests/SmartStore.Core.Tests/Files/testdata.html +++ b/src/Tests/SmartStore.Core.Tests/Files/testdata.html @@ -21,7 +21,7 @@

Produktmerkmale

  • SAR-Wert:0,95 W/kg (Körper) / 0,9 W/kg (Kopf)
  • Formfaktor:Touch
  • Gehäusematerial:Aluminium, Glas
  • -
  • Widerstand:Fingerabdruckbeständige fettabweisende Beschichtung
  • +
  • Widerstand:Fingerabdruckbeständige fettabweisende Beschichtung
  • @@ -51,7 +51,7 @@

    Produktinformation

    Neues Design.

    -

    Mit 7,6 mm und 112 g3 hat das iPhone 5 ein bemerkenswert dünnes und leichtes Design. Es ist aus eloxiertem Aluminium gefertigt. Die abgeschrägten Kanten wurden präzise mit einem Diamanten geschnitten.

    +

    Mit 7,6 mm und 112 g3 hat das iPhone 5 ein bemerkenswert dünnes und leichtes Design. Es ist aus eloxiertem Aluminium gefertigt. Die abgeschrägten Kanten wurden präzise mit einem Diamanten geschnitten.

    Brillantes 4" Retina Display.

    @@ -83,35 +83,26 @@

    Produktinformation

    Wähl das iPhone, das zu dir passt.

    -

    16 GB, 32 GB oder 64 GB Kapazität?

    Das iPhone gibt es in drei Kapazitäten: 16, 32 und 64 GB. „GB“ steht für Gigabyte. Je mehr Gigabyte dein iPhone hat, desto mehr Platz hast du für Inhalte wie Apps, Spiele, Fotos, HD Videos, Musik, Filme und mehr. Bei einer umfangreichen Musik- oder Fotobibliothek oder wenn du häufig HD Filme lädst, empfiehlt sich ein iPhone mit einer größeren Speicherkapazität. Wenn deine Musik- oder Fotobibliothek eher klein ist oder du selten Filme machst oder kaufst, reicht ein iPhone mit einer kleineren Speichergröße für dich aus. Mit der Zeit kommt Einiges an Inhalten auf deinem iPhone zusammen. Deshalb solltest du deinen wachsenden Speicherbedarf beim iPhone Kauf berücksichtigen.

    Das entsperrte iPhone

    -

    Das entsperrte iPhone ist an kein Mobilfunknetz gebunden, das heißt, du kannst dich für jedes unterstützte GSM Netz weltweit entscheiden. Kaufe ein iPhone ohne Vertragsbindung im Apple Online Store und suche dann einen Tarif für das iPhone, der nur die SIM-Karte umfasst. Oder wende dich an deinen bisherigen Anbieter, wenn du deinen derzeitigen Tarif behalten möchtest.

    PrePaid

    -

    Wenn du keinen Vertrag willst, kannst du eines der „PrePaid“ Starter-Pakete der verfügbaren Netzbetreiber im Apple Online Store wählen. Mit „PrePaid“ bekommst du dein iPhone einsatzbereit und mit einer kompatiblen SIM-Karte. Eine Bonitätsprüfung ist nicht erforderlich. Du bezahlst einfach nur online für das, was du verbrauchst, und kannst den Netzbetreiber jederzeit wechseln. „PrePaid“ ist eine gute Option, wenn du bei Auslandsreisen lieber einen lokalen Anbieter nutzen willst.

    Welche SIM-Karte brauche ich?

    -

    Es ist egal, ob du einen neuen Vertrag abschließt, deinen vorhandenen Vertrag weiterführst oder „PrePaid“ wählst – du brauchst eine kompatible SIM-Karte. Für das iPhone 5 brauchst du eine Nano-SIM-Karte, für das iPhone 4S und das iPhone 4 eine Micro-SIM-Karte. Nähere Einzelheiten bekommst du bei deinem Mobilfunkanbieter.

    Mobilfunknetz und Wi-Fi

    -

    GSM Modell A1428: UMTS/HSPA+/DC-HSDPA (850, 900, 1900, 2100 MHz), GSM/EDGE (850, 900, 1800, 1900 MHz), LTE (Band 4 und 17)

    -

    CDMA Modell A1429*: CDMA EV-DO Rev. A und Rev. B (800, 1900, 2100 MHz), UMTS/HSPA+/DC-HSDPA (850, 900, 1900, 2100 MHz), GSM/EDGE (850, 900, 1800, 1900 MHz), LTE (Band 1, 3, 5, 13, 25)

    -

    GSM Modell A1429*: UMTS/HSPA+/DC-HSDPA (850, 900, 1900, 2100 MHz), GSM/EDGE (850, 900, 1800, 1900 MHz), LTE (Band 1, 3, 5)

    -

    802.11a/b/g/n Wi-Fi (802.11n 2,4 GHz und 5 GHz)

    Bluetooth 4.0

    -

    Display

    From b2bbae7bf57353067af5305e827834a9c378a5dc Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sat, 16 Feb 2019 02:06:44 +0100 Subject: [PATCH 058/575] Revise ValidateInput & AllowHtml --- .../Controllers/BoardsController.cs | 4 --- .../Controllers/CheckoutController.cs | 4 +-- .../Controllers/CustomerController.cs | 1 - .../Controllers/NewsletterController.cs | 1 - .../Controllers/PollController.cs | 1 - .../Controllers/ShoppingCartController.cs | 12 +------ .../Controllers/TopicController.cs | 2 +- .../Models/Blogs/AddBlogCommentModel.cs | 8 ++--- .../Models/Boards/EditForumPostModel.cs | 8 ++--- .../Models/Boards/EditForumTopicModel.cs | 12 +++---- .../Models/Catalog/ProductAskQuestionModel.cs | 9 ++--- .../Models/Catalog/ProductDetailsModel.cs | 11 +++--- .../Catalog/ProductEmailAFriendModel.cs | 8 ++--- .../Models/Catalog/ProductReviewModel.cs | 11 +++--- .../Models/Common/AccountDropdownModel.cs | 5 +-- .../Models/Common/AddressModel.cs | 13 +------ .../Models/Customer/ChangePasswordModel.cs | 10 ++---- .../Models/Customer/CustomerInfoModel.cs | 28 +++++---------- .../Models/Customer/LoginModel.cs | 6 +--- .../Customer/PasswordRecoveryConfirmModel.cs | 9 ++--- .../Models/Customer/RegisterModel.cs | 35 ++++++++----------- .../Models/Install/InstallModel.cs | 17 ++++----- .../Models/News/AddNewsCommentModel.cs | 9 +++-- .../Models/Order/SubmitReturnRequestModel.cs | 5 ++- .../SendPrivateMessageModel.cs | 7 ++-- .../ShoppingCart/WishlistEmailAFriendModel.cs | 8 ++--- 26 files changed, 84 insertions(+), 160 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs b/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs index 35707cde1d..06eb2e838c 100644 --- a/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs @@ -923,7 +923,6 @@ public ActionResult TopicCreate(int id) } [HttpPost] - [ValidateInput(false)] [ValidateCaptcha] [GdprConsent] public ActionResult TopicCreate(EditForumTopicModel model, bool captchaValid) @@ -1089,7 +1088,6 @@ public ActionResult TopicEdit(int id) } [HttpPost] - [ValidateInput(false)] [ValidateCaptcha] public ActionResult TopicEdit(EditForumTopicModel model, bool captchaValid) { @@ -1315,7 +1313,6 @@ public ActionResult PostCreate(int id, int? quote) } [HttpPost] - [ValidateInput(false)] [ValidateCaptcha] [GdprConsent] public ActionResult PostCreate(EditForumPostModel model, bool captchaValid) @@ -1478,7 +1475,6 @@ public ActionResult PostEdit(int id) } [HttpPost] - [ValidateInput(false)] [ValidateCaptcha] public ActionResult PostEdit(EditForumPostModel model, bool captchaValid) { diff --git a/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs b/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs index eb45eca2ff..002fe5fa15 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs @@ -658,7 +658,6 @@ public ActionResult ShippingMethod() [HttpPost, ActionName("ShippingMethod")] [FormValueRequired("nextstep")] - [ValidateInput(false)] public ActionResult SelectShippingMethod(string shippingoption) { //validation @@ -771,7 +770,6 @@ public ActionResult PaymentMethod() [HttpPost, ActionName("PaymentMethod")] [FormValueRequired("nextstep")] - [ValidateInput(false)] public ActionResult SelectPaymentMethod(string paymentmethod, CheckoutPaymentMethodModel model, FormCollection form) { // validation @@ -845,8 +843,8 @@ public ActionResult Confirm() return View(model); } + [HttpPost, ActionName("Confirm")] - [ValidateInput(false)] public ActionResult ConfirmOrder(FormCollection form) { //validation diff --git a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs index 181811ce55..798f8f6ead 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs @@ -884,7 +884,6 @@ public ActionResult RegisterResult(int resultId) } [HttpPost] - [ValidateInput(false)] public ActionResult CheckUsernameAvailability(string username) { var usernameAvailable = false; diff --git a/src/Presentation/SmartStore.Web/Controllers/NewsletterController.cs b/src/Presentation/SmartStore.Web/Controllers/NewsletterController.cs index 5deb512aec..0de3f86e3b 100644 --- a/src/Presentation/SmartStore.Web/Controllers/NewsletterController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/NewsletterController.cs @@ -35,7 +35,6 @@ public NewsletterController( } [HttpPost] - [ValidateInput(false)] [GdprConsent] public ActionResult Subscribe(bool subscribe, string email) { diff --git a/src/Presentation/SmartStore.Web/Controllers/PollController.cs b/src/Presentation/SmartStore.Web/Controllers/PollController.cs index ac4c98e596..8368c531a4 100644 --- a/src/Presentation/SmartStore.Web/Controllers/PollController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/PollController.cs @@ -110,7 +110,6 @@ public ActionResult PollBlock(string systemKeyword) } [HttpPost] - [ValidateInput(false)] public ActionResult Vote(int pollAnswerId) { var pollAnswer = _pollService.GetPollAnswerById(pollAnswerId); diff --git a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs index 9883377cc8..1caeb9124a 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs @@ -1407,7 +1407,6 @@ public ActionResult AddProductSimple(int productId, int shoppingCartTypeId = 1, //add product to cart using AJAX //currently we use this method on the product details pages [HttpPost] - [ValidateInput(false)] public ActionResult AddProduct(int productId, int shoppingCartTypeId, ProductVariantQuery query, FormCollection form) { var product = _productService.GetProductById(productId); @@ -1696,7 +1695,6 @@ public ActionResult DeleteCartItem(int cartItemId, bool? wishlistItem) }); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("continueshopping")] public ActionResult ContinueShopping() @@ -1705,7 +1703,6 @@ public ActionResult ContinueShopping() return RedirectToReferrer(returnUrl); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("startcheckout")] public ActionResult StartCheckout(ProductVariantQuery query, bool? useRewardPoints) @@ -1742,7 +1739,7 @@ public ActionResult StartCheckout(ProductVariantQuery query, bool? useRewardPoin } // Ajax. Required for cart payment buttons. - [HttpPost, ValidateInput(false)] + [HttpPost] public ActionResult SaveCartData(ProductVariantQuery query, bool? useRewardPoints) { var warnings = ValidateAndSaveCartData(query, useRewardPoints); @@ -1754,7 +1751,6 @@ public ActionResult SaveCartData(ProductVariantQuery query, bool? useRewardPoint }); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("applydiscountcouponcode")] public ActionResult ApplyDiscountCoupon(string discountcouponcode, ProductVariantQuery query) @@ -1795,7 +1791,6 @@ public ActionResult ApplyDiscountCoupon(string discountcouponcode, ProductVarian return View(model); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("applygiftcardcouponcode")] public ActionResult ApplyGiftCard(string giftcardcouponcode, ProductVariantQuery query) @@ -1841,7 +1836,6 @@ public ActionResult ApplyGiftCard(string giftcardcouponcode, ProductVariantQuery return View(model); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("applyrewardpoints")] public ActionResult ApplyRewardPoints(bool useRewardPoints, ProductVariantQuery query) @@ -1860,7 +1854,6 @@ public ActionResult ApplyRewardPoints(bool useRewardPoints, ProductVariantQuery return View(model); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("estimateshipping")] public ActionResult GetEstimateShipping(EstimateShippingModel shippingModel, ProductVariantQuery query) @@ -2138,7 +2131,6 @@ public ActionResult OrderTotals(bool isEditable) return PartialView(model); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("removesubtotaldiscount", "removeordertotaldiscount", "removediscountcouponcode")] public ActionResult RemoveDiscountCoupon() @@ -2153,7 +2145,6 @@ public ActionResult RemoveDiscountCoupon() return View(model); } - [ValidateInput(false)] [HttpPost, ActionName("Cart")] [FormValueRequired("removegiftcard")] public ActionResult RemoveGiftardCode(int giftCardId) @@ -2352,7 +2343,6 @@ public ActionResult Wishlist(Guid? customerGuid) return View(model); } - [ValidateInput(false)] [HttpPost, ActionName("Wishlist")] [FormValueRequired("addtocartbutton")] public ActionResult AddItemstoCartFromWishlist(Guid? customerGuid, FormCollection form) diff --git a/src/Presentation/SmartStore.Web/Controllers/TopicController.cs b/src/Presentation/SmartStore.Web/Controllers/TopicController.cs index ce49ffb0de..3b697b0d15 100644 --- a/src/Presentation/SmartStore.Web/Controllers/TopicController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/TopicController.cs @@ -164,7 +164,7 @@ public ActionResult TopicWidget(TopicWidgetModel model) return PartialView(model); } - [HttpPost, ValidateInput(false)] + [HttpPost] public ActionResult Authenticate(int id, string password) { var authResult = false; diff --git a/src/Presentation/SmartStore.Web/Models/Blogs/AddBlogCommentModel.cs b/src/Presentation/SmartStore.Web/Models/Blogs/AddBlogCommentModel.cs index e3208d7ab0..9144ca3a35 100644 --- a/src/Presentation/SmartStore.Web/Models/Blogs/AddBlogCommentModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Blogs/AddBlogCommentModel.cs @@ -1,13 +1,13 @@ -using System.Web.Mvc; -using SmartStore.Web.Framework; +using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Blogs { - public partial class AddBlogCommentModel : EntityModelBase + public partial class AddBlogCommentModel : EntityModelBase { [SmartResourceDisplayName("Blog.Comments.CommentText")] - [AllowHtml] + [SanitizeHtml] public string CommentText { get; set; } public bool DisplayCaptcha { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Boards/EditForumPostModel.cs b/src/Presentation/SmartStore.Web/Models/Boards/EditForumPostModel.cs index 18cc57c8a8..558c14a7e7 100644 --- a/src/Presentation/SmartStore.Web/Models/Boards/EditForumPostModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Boards/EditForumPostModel.cs @@ -1,13 +1,13 @@ -using System.Web.Mvc; -using FluentValidation; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Forums; using SmartStore.Services.Localization; using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Boards { - [Validator(typeof(EditForumPostValidator))] + [Validator(typeof(EditForumPostValidator))] public partial class EditForumPostModel : EntityModelBase { public int ForumTopicId { get; set; } @@ -16,7 +16,7 @@ public partial class EditForumPostModel : EntityModelBase public bool DisplayCaptcha { get; set; } public bool IsFirstPost { get; set; } - [AllowHtml] + [SanitizeHtml] public string Text { get; set; } public EditorType ForumEditor { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Boards/EditForumTopicModel.cs b/src/Presentation/SmartStore.Web/Models/Boards/EditForumTopicModel.cs index 4f27772b58..4f58ccf5fe 100644 --- a/src/Presentation/SmartStore.Web/Models/Boards/EditForumTopicModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Boards/EditForumTopicModel.cs @@ -1,14 +1,15 @@ -using FluentValidation; +using System.Collections.Generic; +using System.Web.Mvc; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Forums; using SmartStore.Services.Localization; using SmartStore.Web.Framework.Modelling; -using System.Collections.Generic; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Boards { - [Validator(typeof(EditForumTopicValidator))] + [Validator(typeof(EditForumTopicValidator))] public partial class EditForumTopicModel : EntityModelBase { public EditForumTopicModel() @@ -28,10 +29,9 @@ public EditForumTopicModel() public int TopicTypeId { get; set; } public EditorType ForumEditor { get; set; } - [AllowHtml] public string Subject { get; set; } - [AllowHtml] + [SanitizeHtml] public string Text { get; set; } public bool IsModerator { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Catalog/ProductAskQuestionModel.cs b/src/Presentation/SmartStore.Web/Models/Catalog/ProductAskQuestionModel.cs index 0df801fa07..3b9bb1e2f5 100644 --- a/src/Presentation/SmartStore.Web/Models/Catalog/ProductAskQuestionModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Catalog/ProductAskQuestionModel.cs @@ -4,31 +4,28 @@ using SmartStore.Services.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Catalog { - [Validator(typeof(ProductAskQuestionValidator))] + [Validator(typeof(ProductAskQuestionValidator))] public partial class ProductAskQuestionModel : EntityModelBase { public LocalizedValue ProductName { get; set; } public string ProductSeName { get; set; } - [AllowHtml] [SmartResourceDisplayName("Account.Fields.Email")] public string SenderEmail { get; set; } - [AllowHtml] [SmartResourceDisplayName("Account.Fields.FullName")] public string SenderName { get; set; } public bool SenderNameRequired { get; set; } - [AllowHtml] [SmartResourceDisplayName("Account.Fields.Phone")] public string SenderPhone { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("Common.Question")] public string Question { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs b/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs index dd24d6b44e..9282cfee7c 100644 --- a/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Catalog/ProductDetailsModel.cs @@ -6,6 +6,7 @@ using SmartStore.Services.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.UI; using SmartStore.Web.Framework.UI.Choices; using SmartStore.Web.Models.Media; @@ -214,19 +215,19 @@ public partial class GiftCardModel : ModelBase public bool IsGiftCard { get; set; } [SmartResourceDisplayName("Products.GiftCard.RecipientName")] - [AllowHtml] public string RecipientName { get; set; } + [SmartResourceDisplayName("Products.GiftCard.RecipientEmail")] - [AllowHtml] public string RecipientEmail { get; set; } + [SmartResourceDisplayName("Products.GiftCard.SenderName")] - [AllowHtml] public string SenderName { get; set; } + [SmartResourceDisplayName("Products.GiftCard.SenderEmail")] - [AllowHtml] public string SenderEmail { get; set; } + [SmartResourceDisplayName("Products.GiftCard.Message")] - [AllowHtml] + [SanitizeHtml] public string Message { get; set; } public GiftCardType GiftCardType { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Catalog/ProductEmailAFriendModel.cs b/src/Presentation/SmartStore.Web/Models/Catalog/ProductEmailAFriendModel.cs index f33d4d4fa6..c2233fa600 100644 --- a/src/Presentation/SmartStore.Web/Models/Catalog/ProductEmailAFriendModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Catalog/ProductEmailAFriendModel.cs @@ -3,11 +3,11 @@ using SmartStore.Services.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Catalog { - [Validator(typeof(ProductEmailAFriendValidator))] + [Validator(typeof(ProductEmailAFriendValidator))] public partial class ProductEmailAFriendModel : ModelBase { public int ProductId { get; set; } @@ -16,15 +16,13 @@ public partial class ProductEmailAFriendModel : ModelBase public string ProductSeName { get; set; } - [AllowHtml] [SmartResourceDisplayName("Products.EmailAFriend.FriendEmail")] public string FriendEmail { get; set; } - [AllowHtml] [SmartResourceDisplayName("Products.EmailAFriend.YourEmailAddress")] public string YourEmailAddress { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("Products.EmailAFriend.PersonalMessage")] public string PersonalMessage { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Catalog/ProductReviewModel.cs b/src/Presentation/SmartStore.Web/Models/Catalog/ProductReviewModel.cs index 25490d9294..16ae343cec 100644 --- a/src/Presentation/SmartStore.Web/Models/Catalog/ProductReviewModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Catalog/ProductReviewModel.cs @@ -1,14 +1,14 @@ -using FluentValidation; +using System.Collections.Generic; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Services.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Collections.Generic; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Catalog { - public partial class ProductReviewOverviewModel : ModelBase + public partial class ProductReviewOverviewModel : ModelBase { public int ProductId { get; set; } @@ -36,11 +36,10 @@ public ProductReviewsModel() #region Add - [AllowHtml] [SmartResourceDisplayName("Reviews.Fields.Title")] public string Title { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("Reviews.Fields.ReviewText")] public string ReviewText { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Common/AccountDropdownModel.cs b/src/Presentation/SmartStore.Web/Models/Common/AccountDropdownModel.cs index 46686d6cf9..193829466b 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/AccountDropdownModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/AccountDropdownModel.cs @@ -1,5 +1,4 @@ using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; @@ -21,17 +20,15 @@ public partial class AccountDropdownModel : EntityModelBase public string AlertMessage { get; set; } [SmartResourceDisplayName("Account.Login.Fields.Email")] - [AllowHtml] public string Email { get; set; } public bool UsernamesEnabled { get; set; } + [SmartResourceDisplayName("Account.Login.Fields.UserName")] - [AllowHtml] public string Username { get; set; } [DataType(DataType.Password)] [SmartResourceDisplayName("Account.Login.Fields.Password")] - [AllowHtml] public string Password { get; set; } [SmartResourceDisplayName("Account.Login.Fields.RememberMe")] diff --git a/src/Presentation/SmartStore.Web/Models/Common/AddressModel.cs b/src/Presentation/SmartStore.Web/Models/Common/AddressModel.cs index fc1b1d210d..ca66c89669 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/AddressModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/AddressModel.cs @@ -30,15 +30,12 @@ public AddressModel() public bool TitleEnabled { get; set; } [SmartResourceDisplayName("Address.Fields.FirstName")] - [AllowHtml] public string FirstName { get; set; } [SmartResourceDisplayName("Address.Fields.LastName")] - [AllowHtml] public string LastName { get; set; } [SmartResourceDisplayName("Address.Fields.Email")] - [AllowHtml] [DataType(DataType.EmailAddress)] public string Email { get; set; } @@ -48,8 +45,8 @@ public AddressModel() public bool ValidateEmailAddress { get; set; } [SmartResourceDisplayName("Address.Fields.Company")] - [AllowHtml] public string Company { get; set; } + public bool CompanyEnabled { get; set; } public bool CompanyRequired { get; set; } @@ -57,7 +54,6 @@ public AddressModel() public int? CountryId { get; set; } [SmartResourceDisplayName("Address.Fields.Country")] - [AllowHtml] public string CountryName { get; set; } public bool CountryEnabled { get; set; } public bool CountryRequired { get; set; } @@ -68,42 +64,35 @@ public AddressModel() public bool StateProvinceRequired { get; set; } [SmartResourceDisplayName("Address.Fields.StateProvince")] - [AllowHtml] public string StateProvinceName { get; set; } [SmartResourceDisplayName("Address.Fields.City")] - [AllowHtml] public string City { get; set; } public bool CityEnabled { get; set; } public bool CityRequired { get; set; } [SmartResourceDisplayName("Address.Fields.Address1")] - [AllowHtml] public string Address1 { get; set; } public bool StreetAddressEnabled { get; set; } public bool StreetAddressRequired { get; set; } [SmartResourceDisplayName("Address.Fields.Address2")] - [AllowHtml] public string Address2 { get; set; } public bool StreetAddress2Enabled { get; set; } public bool StreetAddress2Required { get; set; } [SmartResourceDisplayName("Address.Fields.ZipPostalCode")] - [AllowHtml] public string ZipPostalCode { get; set; } public bool ZipPostalCodeEnabled { get; set; } public bool ZipPostalCodeRequired { get; set; } [SmartResourceDisplayName("Address.Fields.PhoneNumber")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string PhoneNumber { get; set; } public bool PhoneEnabled { get; set; } public bool PhoneRequired { get; set; } [SmartResourceDisplayName("Address.Fields.FaxNumber")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string FaxNumber { get; set; } public bool FaxEnabled { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Customer/ChangePasswordModel.cs b/src/Presentation/SmartStore.Web/Models/Customer/ChangePasswordModel.cs index d70825a3ef..51e63140df 100644 --- a/src/Presentation/SmartStore.Web/Models/Customer/ChangePasswordModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Customer/ChangePasswordModel.cs @@ -1,28 +1,24 @@ -using FluentValidation; +using System.ComponentModel.DataAnnotations; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; namespace SmartStore.Web.Models.Customer { - [Validator(typeof(ChangePasswordValidator))] + [Validator(typeof(ChangePasswordValidator))] public partial class ChangePasswordModel : ModelBase { - [AllowHtml] [DataType(DataType.Password)] [SmartResourceDisplayName("Account.ChangePassword.Fields.OldPassword")] public string OldPassword { get; set; } - [AllowHtml] [DataType(DataType.Password)] [SmartResourceDisplayName("Account.ChangePassword.Fields.NewPassword")] public string NewPassword { get; set; } - [AllowHtml] [DataType(DataType.Password)] [SmartResourceDisplayName("Account.ChangePassword.Fields.ConfirmNewPassword")] public string ConfirmNewPassword { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Customer/CustomerInfoModel.cs b/src/Presentation/SmartStore.Web/Models/Customer/CustomerInfoModel.cs index 0372e94742..76d151cfa8 100644 --- a/src/Presentation/SmartStore.Web/Models/Customer/CustomerInfoModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Customer/CustomerInfoModel.cs @@ -1,15 +1,16 @@ -using FluentValidation; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Customers; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Customer { - [Validator(typeof(CustomerInfoValidator))] + [Validator(typeof(CustomerInfoValidator))] public partial class CustomerInfoModel : ModelBase { public CustomerInfoModel() @@ -21,12 +22,10 @@ public CustomerInfoModel() } [SmartResourceDisplayName("Account.Fields.Email")] - [AllowHtml] [DataType(DataType.EmailAddress)] public string Email { get; set; } [SmartResourceDisplayName("Account.Fields.CustomerNumber")] - [AllowHtml] public string CustomerNumber { get; set; } public bool CustomerNumberEnabled { get; set; } public bool DisplayCustomerNumber { get; set; } @@ -34,8 +33,8 @@ public CustomerInfoModel() public bool CheckUsernameAvailabilityEnabled { get; set; } public bool AllowUsersToChangeUsernames { get; set; } public bool UsernamesEnabled { get; set; } + [SmartResourceDisplayName("Account.Fields.Username")] - [AllowHtml] public string Username { get; set; } //form fields & properties @@ -48,10 +47,9 @@ public CustomerInfoModel() public string Title { get; set; } [SmartResourceDisplayName("Account.Fields.FirstName")] - [AllowHtml] public string FirstName { get; set; } + [SmartResourceDisplayName("Account.Fields.LastName")] - [AllowHtml] public string LastName { get; set; } @@ -66,31 +64,26 @@ public CustomerInfoModel() public bool CompanyEnabled { get; set; } public bool CompanyRequired { get; set; } [SmartResourceDisplayName("Account.Fields.Company")] - [AllowHtml] public string Company { get; set; } public bool StreetAddressEnabled { get; set; } public bool StreetAddressRequired { get; set; } [SmartResourceDisplayName("Account.Fields.StreetAddress")] - [AllowHtml] public string StreetAddress { get; set; } public bool StreetAddress2Enabled { get; set; } public bool StreetAddress2Required { get; set; } [SmartResourceDisplayName("Account.Fields.StreetAddress2")] - [AllowHtml] public string StreetAddress2 { get; set; } public bool ZipPostalCodeEnabled { get; set; } public bool ZipPostalCodeRequired { get; set; } [SmartResourceDisplayName("Account.Fields.ZipPostalCode")] - [AllowHtml] public string ZipPostalCode { get; set; } public bool CityEnabled { get; set; } public bool CityRequired { get; set; } [SmartResourceDisplayName("Account.Fields.City")] - [AllowHtml] public string City { get; set; } public bool CountryEnabled { get; set; } @@ -106,14 +99,12 @@ public CustomerInfoModel() public bool PhoneEnabled { get; set; } public bool PhoneRequired { get; set; } [SmartResourceDisplayName("Account.Fields.Phone")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string Phone { get; set; } public bool FaxEnabled { get; set; } public bool FaxRequired { get; set; } [SmartResourceDisplayName("Account.Fields.Fax")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string Fax { get; set; } @@ -124,7 +115,7 @@ public CustomerInfoModel() //preferences public bool SignatureEnabled { get; set; } [SmartResourceDisplayName("Account.Fields.Signature")] - [AllowHtml] + [SanitizeHtml] public string Signature { get; set; } //time zone @@ -135,7 +126,6 @@ public CustomerInfoModel() //EU VAT [SmartResourceDisplayName("Account.Fields.VatNumber")] - [AllowHtml] public string VatNumber { get; set; } public string VatNumberStatusNote { get; set; } public bool DisplayVatNumber { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Customer/LoginModel.cs b/src/Presentation/SmartStore.Web/Models/Customer/LoginModel.cs index c99aaf03e2..97b5488047 100644 --- a/src/Presentation/SmartStore.Web/Models/Customer/LoginModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Customer/LoginModel.cs @@ -1,26 +1,22 @@ using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Models.Customer { - public partial class LoginModel : ModelBase + public partial class LoginModel : ModelBase { public bool CheckoutAsGuest { get; set; } [SmartResourceDisplayName("Account.Login.Fields.Email")] - [AllowHtml] public string Email { get; set; } public bool UsernamesEnabled { get; set; } [SmartResourceDisplayName("Account.Login.Fields.UserName")] - [AllowHtml] public string Username { get; set; } [DataType(DataType.Password)] [SmartResourceDisplayName("Account.Login.Fields.Password")] - [AllowHtml] public string Password { get; set; } [SmartResourceDisplayName("Account.Login.Fields.RememberMe")] diff --git a/src/Presentation/SmartStore.Web/Models/Customer/PasswordRecoveryConfirmModel.cs b/src/Presentation/SmartStore.Web/Models/Customer/PasswordRecoveryConfirmModel.cs index 19179d4b56..59a2d3fbda 100644 --- a/src/Presentation/SmartStore.Web/Models/Customer/PasswordRecoveryConfirmModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Customer/PasswordRecoveryConfirmModel.cs @@ -1,23 +1,20 @@ -using FluentValidation; +using System.ComponentModel.DataAnnotations; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; namespace SmartStore.Web.Models.Customer { - [Validator(typeof(PasswordRecoveryConfirmValidator))] + [Validator(typeof(PasswordRecoveryConfirmValidator))] public partial class PasswordRecoveryConfirmModel : ModelBase { - [AllowHtml] [DataType(DataType.Password)] [SmartResourceDisplayName("Account.PasswordRecovery.NewPassword")] public string NewPassword { get; set; } - [AllowHtml] [DataType(DataType.Password)] [SmartResourceDisplayName("Account.PasswordRecovery.ConfirmNewPassword")] public string ConfirmNewPassword { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Customer/RegisterModel.cs b/src/Presentation/SmartStore.Web/Models/Customer/RegisterModel.cs index dd997400dd..0e41776eec 100644 --- a/src/Presentation/SmartStore.Web/Models/Customer/RegisterModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Customer/RegisterModel.cs @@ -1,46 +1,42 @@ -using FluentValidation; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Tax; using SmartStore.Core.Localization; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; namespace SmartStore.Web.Models.Customer { - [Validator(typeof(RegisterValidator))] + [Validator(typeof(RegisterValidator))] public partial class RegisterModel : ModelBase { public RegisterModel() { - this.AvailableTimeZones = new List(); - this.AvailableCountries = new List(); - this.AvailableStates = new List(); + AvailableTimeZones = new List(); + AvailableCountries = new List(); + AvailableStates = new List(); } [SmartResourceDisplayName("Account.Fields.Email")] - [AllowHtml] [DataType(DataType.EmailAddress)] public string Email { get; set; } public bool UsernamesEnabled { get; set; } [SmartResourceDisplayName("Account.Fields.Username")] - [AllowHtml] public string Username { get; set; } public bool CheckUsernameAvailabilityEnabled { get; set; } [DataType(DataType.Password)] [SmartResourceDisplayName("Account.Fields.Password")] - [AllowHtml] public string Password { get; set; } [DataType(DataType.Password)] [SmartResourceDisplayName("Account.Fields.ConfirmPassword")] - [AllowHtml] public string ConfirmPassword { get; set; } //form fields & properties @@ -50,49 +46,48 @@ public RegisterModel() public bool FirstNameRequired { get; set; } public bool LastNameRequired { get; set; } + [SmartResourceDisplayName("Account.Fields.FirstName")] - [AllowHtml] public string FirstName { get; set; } + [SmartResourceDisplayName("Account.Fields.LastName")] - [AllowHtml] public string LastName { get; set; } public bool DateOfBirthEnabled { get; set; } + [SmartResourceDisplayName("Account.Fields.DateOfBirth")] public int? DateOfBirthDay { get; set; } + [SmartResourceDisplayName("Account.Fields.DateOfBirth")] public int? DateOfBirthMonth { get; set; } + [SmartResourceDisplayName("Account.Fields.DateOfBirth")] public int? DateOfBirthYear { get; set; } public bool CompanyEnabled { get; set; } public bool CompanyRequired { get; set; } + [SmartResourceDisplayName("Account.Fields.Company")] - [AllowHtml] public string Company { get; set; } public bool StreetAddressEnabled { get; set; } public bool StreetAddressRequired { get; set; } [SmartResourceDisplayName("Account.Fields.StreetAddress")] - [AllowHtml] public string StreetAddress { get; set; } public bool StreetAddress2Enabled { get; set; } public bool StreetAddress2Required { get; set; } [SmartResourceDisplayName("Account.Fields.StreetAddress2")] - [AllowHtml] public string StreetAddress2 { get; set; } public bool ZipPostalCodeEnabled { get; set; } public bool ZipPostalCodeRequired { get; set; } [SmartResourceDisplayName("Account.Fields.ZipPostalCode")] - [AllowHtml] public string ZipPostalCode { get; set; } public bool CityEnabled { get; set; } public bool CityRequired { get; set; } [SmartResourceDisplayName("Account.Fields.City")] - [AllowHtml] public string City { get; set; } public bool CountryEnabled { get; set; } @@ -108,14 +103,12 @@ public RegisterModel() public bool PhoneEnabled { get; set; } public bool PhoneRequired { get; set; } [SmartResourceDisplayName("Account.Fields.Phone")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string Phone { get; set; } public bool FaxEnabled { get; set; } public bool FaxRequired { get; set; } [SmartResourceDisplayName("Account.Fields.Fax")] - [AllowHtml] [DataType(DataType.PhoneNumber)] public string Fax { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Install/InstallModel.cs b/src/Presentation/SmartStore.Web/Models/Install/InstallModel.cs index 4c8aadf1db..3e2d62093b 100644 --- a/src/Presentation/SmartStore.Web/Models/Install/InstallModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Install/InstallModel.cs @@ -1,14 +1,14 @@ -using FluentValidation; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; +using FluentValidation; using FluentValidation.Attributes; using SmartStore.Web.Framework.Modelling; using SmartStore.Web.Infrastructure.Installation; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; namespace SmartStore.Web.Models.Install { - [Validator(typeof(InstallValidator))] + [Validator(typeof(InstallValidator))] public partial class InstallModel : ModelBase { public InstallModel() @@ -18,16 +18,12 @@ public InstallModel() this.AvailableMediaStorages = new List(); } - [AllowHtml] public string AdminEmail { get; set; } - [AllowHtml] [DataType(DataType.Password)] public string AdminPassword { get; set; } - [AllowHtml] [DataType(DataType.Password)] public string ConfirmPassword { get; set; } - [AllowHtml] public string DatabaseConnectionString { get; set; } public string DataProvider { get; set; } @@ -35,7 +31,7 @@ public InstallModel() public string SqlConnectionInfo { get; set; } [AllowHtml] public string SqlServerName { get; set; } - [AllowHtml] + public string SqlDatabaseName { get; set; } [AllowHtml] public string SqlServerUsername { get; set; } @@ -45,7 +41,6 @@ public InstallModel() public bool SqlServerCreateDatabase { get; set; } public bool UseCustomCollation { get; set; } - [AllowHtml] public string Collation { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/News/AddNewsCommentModel.cs b/src/Presentation/SmartStore.Web/Models/News/AddNewsCommentModel.cs index 8fd2495661..d0daabe992 100644 --- a/src/Presentation/SmartStore.Web/Models/News/AddNewsCommentModel.cs +++ b/src/Presentation/SmartStore.Web/Models/News/AddNewsCommentModel.cs @@ -1,17 +1,16 @@ -using System.Web.Mvc; -using SmartStore.Web.Framework; +using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.News { - public partial class AddNewsCommentModel : ModelBase + public partial class AddNewsCommentModel : ModelBase { [SmartResourceDisplayName("News.Comments.CommentTitle")] - [AllowHtml] public string CommentTitle { get; set; } [SmartResourceDisplayName("News.Comments.CommentText")] - [AllowHtml] + [SanitizeHtml] public string CommentText { get; set; } public bool DisplayCaptcha { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/Order/SubmitReturnRequestModel.cs b/src/Presentation/SmartStore.Web/Models/Order/SubmitReturnRequestModel.cs index d49210750c..05299e316d 100644 --- a/src/Presentation/SmartStore.Web/Models/Order/SubmitReturnRequestModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Order/SubmitReturnRequestModel.cs @@ -3,6 +3,7 @@ using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; using SmartStore.Services.Localization; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.Order { @@ -20,17 +21,15 @@ public SubmitReturnRequestModel() public IList Items { get; set; } public IList AddedReturnRequestIds { get; set; } - [AllowHtml] [SmartResourceDisplayName("ReturnRequests.ReturnReason")] public string ReturnReason { get; set; } public IList AvailableReturnReasons { get; set; } - [AllowHtml] [SmartResourceDisplayName("ReturnRequests.ReturnAction")] public string ReturnAction { get; set; } public IList AvailableReturnActions { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("ReturnRequests.Comments")] public string Comments { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/PrivateMessages/SendPrivateMessageModel.cs b/src/Presentation/SmartStore.Web/Models/PrivateMessages/SendPrivateMessageModel.cs index 5ffdec9d29..67c3d6fff6 100644 --- a/src/Presentation/SmartStore.Web/Models/PrivateMessages/SendPrivateMessageModel.cs +++ b/src/Presentation/SmartStore.Web/Models/PrivateMessages/SendPrivateMessageModel.cs @@ -1,11 +1,11 @@ using FluentValidation; using FluentValidation.Attributes; using SmartStore.Web.Framework.Modelling; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.PrivateMessages { - [Validator(typeof(SendPrivateMessageValidator))] + [Validator(typeof(SendPrivateMessageValidator))] public partial class SendPrivateMessageModel : EntityModelBase { public int ToCustomerId { get; set; } @@ -14,10 +14,9 @@ public partial class SendPrivateMessageModel : EntityModelBase public int ReplyToMessageId { get; set; } - [AllowHtml] public string Subject { get; set; } - [AllowHtml] + [SanitizeHtml] public string Message { get; set; } } diff --git a/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistEmailAFriendModel.cs b/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistEmailAFriendModel.cs index 2d383d3d4b..0904986c3c 100644 --- a/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistEmailAFriendModel.cs +++ b/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistEmailAFriendModel.cs @@ -2,22 +2,20 @@ using FluentValidation.Attributes; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.Web.Mvc; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Models.ShoppingCart { - [Validator(typeof(WishlistEmailAFriendValidator))] + [Validator(typeof(WishlistEmailAFriendValidator))] public partial class WishlistEmailAFriendModel : ModelBase { - [AllowHtml] [SmartResourceDisplayName("Wishlist.EmailAFriend.FriendEmail")] public string FriendEmail { get; set; } - [AllowHtml] [SmartResourceDisplayName("Wishlist.EmailAFriend.YourEmailAddress")] public string YourEmailAddress { get; set; } - [AllowHtml] + [SanitizeHtml] [SmartResourceDisplayName("Wishlist.EmailAFriend.PersonalMessage")] public string PersonalMessage { get; set; } From f9bf55ad9ef76aec75c4d14bba6e802aa32520cb Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 18 Feb 2019 09:21:10 +0100 Subject: [PATCH 059/575] Link builder --- .../Migrations/MigrationsConfiguration.cs | 1 + .../SmartStore.Services/Cms/ILinkResolver.cs | 4 +- .../SmartStore.Services/Cms/LinkResolver.cs | 231 +++++++++--------- .../SmartStore.Services/Cms/TokenizeResult.cs | 9 +- .../Views/Shared/EditorTemplates/Link.cshtml | 157 +++++++++--- 5 files changed, 249 insertions(+), 153 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 61c3309732..fa733bda51 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -642,6 +642,7 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Click Download to install a new language including all localized resources. On translate.smartstore.com you will find more details about available resources.", "Klicken Sie auf Download, um eine neue Sprache mit allen lokalisierten Ressourcen zu installieren. Auf translate.smartstore.com finden Sie weitere Details zu verfügbaren Ressourcen."); + builder.AddOrUpdate("Common.BrowseFiles", "Browse", "Durchsuchen"); builder.AddOrUpdate("Common.Url", "URL", "URL"); builder.AddOrUpdate("Common.File", "File", "Datei"); builder.AddOrUpdate("Common.Media", "Media", "Medien"); diff --git a/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs index 0330b149d9..e5b41638e3 100644 --- a/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs @@ -10,7 +10,7 @@ public partial interface ILinkResolver ///

    /// Link expression. /// Language identifier. 0 to use current working language. - /// Tokenize result. + /// Tokenize result. Never returns null. TokenizeResult GetDisplayName(string linkExpression, int languageId = 0); /// @@ -18,7 +18,7 @@ public partial interface ILinkResolver /// /// Link expression. /// Language identifier. 0 to use current working language./// - /// Tokenize result. + /// Tokenize result. Never returns null. TokenizeResult GetLink(string linkExpression, int languageId = 0); } } diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index a364969034..040dc34a98 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -1,13 +1,13 @@ using System; using System.Linq; +using System.Linq.Expressions; using System.Web; using System.Web.Mvc; using SmartStore.Core; -using SmartStore.Core.Caching; -using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Topics; using SmartStore.Services.Localization; -using SmartStore.Services.Media; using SmartStore.Services.Seo; namespace SmartStore.Services.Cms @@ -17,116 +17,125 @@ public partial class LinkResolver : ILinkResolver private const string LINKRESOLVER_NAME_KEY = "SmartStore.linkresolver.name-{0}-{1}"; private const string LINKRESOLVER_LINK_KEY = "SmartStore.linkresolver.link-{0}-{1}"; - protected readonly IRepository _pictureRepository; + protected readonly ICommonServices _services; protected readonly IUrlRecordService _urlRecordService; protected readonly ILanguageService _languageService; - protected readonly IPictureService _pictureService; protected readonly ILocalizedEntityService _localizedEntityService; - protected readonly IWorkContext _workContext; - protected readonly ICacheManager _cache; protected readonly UrlHelper _urlHelper; public LinkResolver( - IRepository pictureRepository, + ICommonServices services, IUrlRecordService urlRecordService, ILanguageService languageService, - IPictureService pictureService, ILocalizedEntityService localizedEntityService, - IWorkContext workContext, - ICacheManager cache, UrlHelper urlHelper) { - _pictureRepository = pictureRepository; + _services = services; _urlRecordService = urlRecordService; _languageService = languageService; - _pictureService = pictureService; _localizedEntityService = localizedEntityService; - _workContext = workContext; - _cache = cache; _urlHelper = urlHelper; } protected virtual TokenizeResult Parse(string linkExpression) { - var arr = linkExpression.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); - if (arr.Length != 2) + if (!string.IsNullOrWhiteSpace(linkExpression)) { - return null; - } - - if (!Enum.TryParse(arr[0], true, out TokenizeType type)) - { - return null; - } - - switch (type) - { - case TokenizeType.Product: - case TokenizeType.Category: - case TokenizeType.Manufacturer: - case TokenizeType.Topic: - return new TokenizeResult(type, arr[1].ToInt()); - case TokenizeType.Media: - // Picture.Id, more to come later. - return new TokenizeResult(type, arr[1].ToInt()); - - case TokenizeType.Url: - case TokenizeType.File: - default: - return new TokenizeResult(type, arr[1]); - } - } + var index = linkExpression.IndexOf(':'); - public virtual TokenizeResult GetDisplayName(string linkExpression, int languageId = 0) - { - Guard.NotEmpty(linkExpression, nameof(linkExpression)); - - if (languageId == 0) - { - languageId = _workContext.WorkingLanguage.Id; - } - - var data = _cache.Get(LINKRESOLVER_NAME_KEY.FormatInvariant(linkExpression, languageId), () => - { - var result = Parse(linkExpression); - if (result != null) + if (index != -1 && Enum.TryParse(linkExpression.Substring(0, index), true, out TokenizeType type)) { - var entityName = result.Type.ToString(); + var value = linkExpression.Substring(index + 1); - switch (result.Type) + switch (type) { case TokenizeType.Product: case TokenizeType.Category: case TokenizeType.Manufacturer: - result.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)result.Value, entityName, "Name"); - break; case TokenizeType.Topic: - result.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)result.Value, entityName, "ShortTitle"); - if (string.IsNullOrEmpty(result.Result)) - { - result.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)result.Value, entityName, "Title"); - } - break; case TokenizeType.Media: - var entityId = (int)result.Value; - result.Result = _pictureRepository.TableUntracked.Where(x => x.Id == entityId).Select(x => x.SeoFilename).FirstOrDefault(); - break; - case TokenizeType.Url: - var url = result.Value.ToString(); - if (url.EmptyNull().StartsWith("~")) + var id = value.ToInt(); + if (id != 0) { - url = VirtualPathUtility.ToAbsolute(url); + return new TokenizeResult(type, id); } - result.Result = url; break; + case TokenizeType.Url: case TokenizeType.File: default: - result.Result = result.Value.ToString(); - break; + return new TokenizeResult(type, value); } } + } - return result; + // Fallback to default. + return new TokenizeResult(TokenizeType.Url, linkExpression.EmptyNull()); + } + + protected virtual string GetFromDatabase(Expression> selector, int entityId) where T : BaseEntity + { + var dbSet = _services.DbContext.Set(); + return dbSet.AsNoTracking().Where(x => x.Id == entityId).Select(selector).FirstOrDefault().EmptyNull(); + } + + public virtual TokenizeResult GetDisplayName(string linkExpression, int languageId = 0) + { + if (languageId == 0) + { + languageId = _services.WorkContext.WorkingLanguage.Id; + } + + var data = _services.Cache.Get(LINKRESOLVER_NAME_KEY.FormatInvariant(linkExpression, languageId), () => + { + var r = Parse(linkExpression); + var entityName = r.Type.ToString(); + + switch (r.Type) + { + case TokenizeType.Product: + case TokenizeType.Category: + case TokenizeType.Manufacturer: + r.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)r.Value, entityName, "Name"); + if (string.IsNullOrEmpty(r.Result)) + { + if (r.Type == TokenizeType.Product) + r.Result = GetFromDatabase(x => x.Name, (int)r.Value); + else if (r.Type == TokenizeType.Category) + r.Result = GetFromDatabase(x => x.Name, (int)r.Value); + else + r.Result = GetFromDatabase(x => x.Name, (int)r.Value); + } + break; + case TokenizeType.Topic: + r.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)r.Value, entityName, "ShortTitle"); + if (string.IsNullOrEmpty(r.Result)) + { + r.Result = _localizedEntityService.GetLocalizedValue(languageId, (int)r.Value, entityName, "Title"); + } + if (string.IsNullOrEmpty(r.Result)) + { + r.Result = GetFromDatabase(x => x.SystemName, (int)r.Value); + } + break; + case TokenizeType.Media: + var entityId = (int)r.Value; + r.Result = GetFromDatabase(x => x.SeoFilename, (int)r.Value); + break; + case TokenizeType.Url: + var url = r.Value.ToString(); + if (url.EmptyNull().StartsWith("~")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + r.Result = url; + break; + case TokenizeType.File: + default: + r.Result = r.Value.ToString(); + break; + } + + return r; }); return data; @@ -134,48 +143,48 @@ public virtual TokenizeResult GetDisplayName(string linkExpression, int language public virtual TokenizeResult GetLink(string linkExpression, int languageId = 0) { - Guard.NotEmpty(linkExpression, nameof(linkExpression)); - if (languageId == 0) { - languageId = _workContext.WorkingLanguage.Id; + languageId = _services.WorkContext.WorkingLanguage.Id; } - var data = _cache.Get(LINKRESOLVER_LINK_KEY.FormatInvariant(linkExpression, languageId), () => + var data = _services.Cache.Get(LINKRESOLVER_LINK_KEY.FormatInvariant(linkExpression, languageId), () => { var result = Parse(linkExpression); - if (result != null) + + switch (result.Type) { - switch (result.Type) - { - case TokenizeType.Product: - case TokenizeType.Category: - case TokenizeType.Manufacturer: - case TokenizeType.Topic: - var entityName = result.Type.ToString(); - // Perf: GetActiveSlug only fetches UrlRecord.Slug from database. - var slug = _urlRecordService.GetActiveSlug((int)result.Value, entityName, languageId); - if (!string.IsNullOrEmpty(slug)) - { - result.Result = _urlHelper.RouteUrl(entityName, new { SeName = slug }); - } - break; - case TokenizeType.Media: - result.Result = _pictureService.GetUrl((int)result.Value); - break; - case TokenizeType.Url: - var url = result.Value.ToString(); - if (url.EmptyNull().StartsWith("~")) - { - url = VirtualPathUtility.ToAbsolute(url); - } - result.Result = url; - break; - case TokenizeType.File: - default: - result.Result = result.Value.ToString(); - break; - } + case TokenizeType.Product: + case TokenizeType.Category: + case TokenizeType.Manufacturer: + case TokenizeType.Topic: + var entityName = result.Type.ToString(); + // Perf: GetActiveSlug only fetches UrlRecord.Slug from database. + var slug = _urlRecordService.GetActiveSlug((int)result.Value, entityName, languageId); + if (string.IsNullOrEmpty(slug)) + { + slug = _urlRecordService.GetActiveSlug((int)result.Value, entityName, 0); + } + if (!string.IsNullOrEmpty(slug)) + { + result.Result = _urlHelper.RouteUrl(entityName, new { SeName = slug }); + } + break; + case TokenizeType.Media: + result.Result = _services.PictureService.GetUrl((int)result.Value); + break; + case TokenizeType.Url: + var url = result.Value.ToString(); + if (url.EmptyNull().StartsWith("~")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + result.Result = url; + break; + case TokenizeType.File: + default: + result.Result = result.Value.ToString(); + break; } return result; diff --git a/src/Libraries/SmartStore.Services/Cms/TokenizeResult.cs b/src/Libraries/SmartStore.Services/Cms/TokenizeResult.cs index 2bb265d1c6..0cf47af24a 100644 --- a/src/Libraries/SmartStore.Services/Cms/TokenizeResult.cs +++ b/src/Libraries/SmartStore.Services/Cms/TokenizeResult.cs @@ -13,14 +13,9 @@ public TokenizeResult(TokenizeType type, object value) public static implicit operator string (TokenizeResult obj) { - if (!string.IsNullOrEmpty(obj.Result)) + if (obj != null) { - return obj.Result; - } - - if (obj.Value != null) - { - return obj.Value.ToString().EmptyNull(); + return obj.Result.EmptyNull(); } return string.Empty; diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml index 3852580b1d..5891586249 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml @@ -25,11 +25,12 @@ return new { icon = "fa fa-link", name = T("Common.Url").Text }; } } - } +} @{ var rnd = CommonHelper.GenerateRandomInteger(); var id = ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty); + var token = this.CommonServices.Resolve().GetDisplayName(Value); var arr = GetMetadata("allowedTypes").SplitSafe(","); var allowedTypes = arr.Any() @@ -39,8 +40,10 @@