From 919ad208c4293f1ea5f05fe388342d93bca20f44 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 5 Mar 2019 20:46:38 +0100 Subject: [PATCH 001/460] Closes #1579 Structured data: Google complains about missing price element --- .../SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2f3a25865e..c25bbbdd9d 100644 --- a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Offer.cshtml @@ -4,7 +4,7 @@ @using SmartStore.Core.Domain.Catalog; @using SmartStore.Web; -
+
@if (!Model.AddToCart.DisableBuyButton || !Model.AddToCart.DisableWishlistButton || Model.ProductPrice.ShowLoginNote) From f10067322df95672d9932e98a955000223699deb Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 5 Mar 2019 21:10:32 +0100 Subject: [PATCH 002/460] Added AnchorPosition query to ImageProcessor --- .../Media/DefaultImageProcessor.cs | 32 +++++++++++++++++-- .../Media/ProcessImageQuery.cs | 13 +++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs index 3e94172aec..e912b6324a 100644 --- a/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs +++ b/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs @@ -143,8 +143,11 @@ protected virtual void ProcessImageCore(ProcessImageQuery query, ImageFactory pr if (!size.IsEmpty) { - var scaleMode = ConvertScaleMode(query.ScaleMode); - processor.Resize(new ResizeLayer(size, resizeMode: scaleMode, upscale: false)); + processor.Resize(new ResizeLayer( + size, + resizeMode: ConvertScaleMode(query.ScaleMode), + anchorPosition: ConvertAnchorPosition(query.AnchorPosition), + upscale: false)); } if (query.BackgroundColor.HasValue()) @@ -219,5 +222,30 @@ private ResizeMode ConvertScaleMode(string mode) return ResizeMode.Max; } } + + private AnchorPosition ConvertAnchorPosition(string anchor) + { + switch (anchor.EmptyNull().ToLower()) + { + case "top": + return AnchorPosition.Top; + case "bottom": + return AnchorPosition.Bottom; + case "left": + return AnchorPosition.Left; + case "right": + return AnchorPosition.Right; + case "top-left": + return AnchorPosition.TopLeft; + case "top-right": + return AnchorPosition.TopRight; + case "bottom-left": + return AnchorPosition.BottomLeft; + case "bottom-right": + return AnchorPosition.BottomRight; + default: + return AnchorPosition.Center; + } + } } } diff --git a/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs b/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs index 392fd6da7a..7bc49499a7 100644 --- a/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs +++ b/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs @@ -117,13 +117,24 @@ public int? Quality set { Set("q", value); } } - // TODO: (mc) make Enum + /// + /// max (default) | boxpad | crop | min | pad | stretch + /// public string ScaleMode { get { return Get("m"); } set { Set("m", value); } } + /// + /// center (default) | top | bottom | left | right | top-left | top-right | bottom-left | bottom-right + /// + public string AnchorPosition + { + get { return Get("pos"); } + set { Set("pos", value); } + } + public string BackgroundColor { get { return Get("bg"); } From 5241fb72c9f25ae69197415692bc75a3f128fc65 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 6 Mar 2019 12:43:55 +0100 Subject: [PATCH 003/460] Added some string resources --- .../Migrations/MigrationsConfiguration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index fa81751642..aad27d3e42 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -707,6 +707,16 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Kunden müssen eine Steuernummer angeben", "Specifies whether customers must enter a VAT identification number.", "Legt fest, ob Kunden bei der Registrierung eine Steuernummer angeben müssen."); + + builder.AddOrUpdate("Common.Top", "Top", "Oben"); + builder.AddOrUpdate("Common.Bottom", "Bottom", "Unten"); + builder.AddOrUpdate("Common.Left", "Left", "Links"); + builder.AddOrUpdate("Common.Right", "Right", "Rechts"); + builder.AddOrUpdate("Common.TopLeft", "Top left", "Links oben"); + builder.AddOrUpdate("Common.TopRight", "Top right", "Rechts oben"); + builder.AddOrUpdate("Common.BottomLeft", "Bottom left", "Links unten"); + builder.AddOrUpdate("Common.BottomRight", "Bottom right", "Rechts unten"); + builder.AddOrUpdate("Common.Center", "Center", "Mitte"); } } } From da2c39280c1e1af3f74d3e3a317c71fa18ac0996 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 6 Mar 2019 13:33:20 +0100 Subject: [PATCH 004/460] Added confirmation request to the delete products function in product list --- .../Administration/Views/Product/List.cshtml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml index 3b8924e689..69a81b30c2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml @@ -215,11 +215,13 @@ }); //"Delete (selected)" button - $('#delete-selected').click(function (e) { - e.preventDefault(); - //redirect to required URL - setLocation('@(Url.Action("DeleteSelected", "Product"))?selectedIds=' + selectedIds.join(",")); - return false; + $('#delete-selected').click(function (e) { + if (confirm(@T("Admin.Common.AskToProceed").JsText)) { + e.preventDefault(); + //redirect to required URL + setLocation('@(Url.Action("DeleteSelected", "Product"))?selectedIds=' + selectedIds.join(",")); + return false; + } }); //wire up checkboxes. From 156a4fe9320ec2a835dd1d868870e3fb4859298b Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 6 Mar 2019 13:33:54 +0100 Subject: [PATCH 005/460] Structured data: added itemprop for description --- .../SmartStore.Web/Views/Product/Partials/Product.Info.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Info.cshtml b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Info.cshtml index 54160ba19d..82fe05d7b0 100644 --- a/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Info.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Product/Partials/Product.Info.cshtml @@ -42,7 +42,7 @@ @if (Model.ShortDescription.Value.HasValue()) { -
+
@Html.Raw(Model.ShortDescription)
} From 38bdb05595757c6196644b0972c93ad0728d9102 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 6 Mar 2019 16:57:19 +0100 Subject: [PATCH 006/460] Added service method to get multiple manufacturers by ids --- .../Catalog/CategoryService.cs | 6 +++--- .../Catalog/ICategoryService.cs | 4 ++-- .../Catalog/IManufacturerService.cs | 7 +++++++ .../Catalog/ManufacturerService.cs | 17 +++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs index 8d6fe284c5..884ee2b7d1 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs @@ -440,9 +440,9 @@ public virtual IList GetCategoriesByIds(int[] categoryIds) return new List(); } - var query = from p in _categoryRepository.Table - where categoryIds.Contains(p.Id) - select p; + var query = from c in _categoryRepository.Table + where categoryIds.Contains(c.Id) + select c; var categories = query.ToList(); diff --git a/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs b/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs index be6bc5c836..b52589cb5e 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs @@ -101,10 +101,10 @@ IPagedList GetAllCategories( Category GetCategoryById(int categoryId); /// - /// Gets a categories by Ids + /// Gets categories by Ids /// /// Array of category identifiers - /// List if Categories + /// List of Categories IList GetCategoriesByIds(int[] categoryIds); /// diff --git a/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs b/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs index 8843f4eb94..f8127a3d2c 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs @@ -60,6 +60,13 @@ IPagedList GetAllManufacturers(string manufacturerName, /// Manufacturer Manufacturer GetManufacturerById(int manufacturerId); + /// + /// Gets manufacturers by Ids + /// + /// Array of manufacturer identifiers + /// List of manufacturers + IList GetManufacturersByIds(int[] manufacturerIds); + /// /// Inserts a manufacturer /// diff --git a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs index d160e1d7c6..8e0cfeb411 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs @@ -121,6 +121,23 @@ public virtual Manufacturer GetManufacturerById(int manufacturerId) return _manufacturerRepository.GetByIdCached(manufacturerId, "db.manu.id-" + manufacturerId); } + public virtual IList GetManufacturersByIds(int[] manufacturerIds) + { + if (manufacturerIds == null || !manufacturerIds.Any()) + { + return new List(); + } + + var query = from m in _manufacturerRepository.Table + where manufacturerIds.Contains(m.Id) + select m; + + var manufacturers = query.ToList(); + + // Sort by passed identifier sequence. + return manufacturers.OrderBySequence(manufacturerIds).ToList(); + } + public virtual void InsertManufacturer(Manufacturer manufacturer) { if (manufacturer == null) From 3eeb2ee95cb723b2ca63eb39097c887a8d097390 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 7 Mar 2019 17:24:56 +0100 Subject: [PATCH 007/460] AmazonPay: Updated Amazon SDK --- src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj | 4 ++-- src/Plugins/SmartStore.AmazonPay/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index 6700cd8636..6105db6395 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -78,8 +78,8 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\AmazonPay.3.3.1\lib\net20\AmazonPay.dll + + ..\..\packages\AmazonPay.3.5.0\lib\net20\AmazonPay.dll True diff --git a/src/Plugins/SmartStore.AmazonPay/packages.config b/src/Plugins/SmartStore.AmazonPay/packages.config index 0486d9903c..e78470d805 100644 --- a/src/Plugins/SmartStore.AmazonPay/packages.config +++ b/src/Plugins/SmartStore.AmazonPay/packages.config @@ -1,6 +1,6 @@  - + From 7a18d28c84fce5001b96be6c6de21f0f0a283799 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 8 Mar 2019 14:51:39 +0100 Subject: [PATCH 008/460] Updated change log --- changelog.md | 3 ++- .../Domain/Catalog/ProductSpecificationAttribute.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index c63fce1b9e..ceec208d7b 100644 --- a/changelog.md +++ b/changelog.md @@ -45,7 +45,8 @@ * #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. -* #1542 Pages: Added options to set body CSS class and Html Id +* #1542 Pages: Added options to set body CSS class and Html Id. +* Web-API: Added endpoints for ProductSpecificationAttribute. ### Improvements * (Perf) Significantly increased query performance for products with a lot of category assignments (> 10). diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs index 132e767131..eb8595b3af 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs @@ -46,6 +46,7 @@ public partial class ProductSpecificationAttribute : BaseEntity /// /// Gets or sets the specification attribute option /// + [DataMember] public virtual SpecificationAttributeOption SpecificationAttributeOption { get; set; } } } From 5dafc7706bfdc3239b07386506edac52583fc60a Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 8 Mar 2019 22:25:10 +0100 Subject: [PATCH 009/460] Enhancements & fixes for FileSystemHelper --- .../Extensions/IOExtensions.cs | 3 - .../Utilities/FileSystemHelper.cs | 205 ++++++++++-------- .../Common/TempFileCleanupTask.cs | 2 +- .../DataExchange/Export/DataExporter.cs | 6 +- .../DataExchange/Import/DataImporter.cs | 2 +- .../SmartStore.Services/Media/ImageCache.cs | 2 +- .../WebApi/WebApiExtension.cs | 2 +- .../Controllers/CommonController.cs | 2 +- .../Controllers/ImportController.cs | 8 +- .../Controllers/LanguageController.cs | 4 +- .../Controllers/RoxyFileManagerController.cs | 4 +- 11 files changed, 128 insertions(+), 112 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs index 5b6c628c10..2104c9c3ee 100644 --- a/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore { diff --git a/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs b/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs index ef9e57293e..b7324ccee2 100644 --- a/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs @@ -9,13 +9,16 @@ namespace SmartStore.Utilities { public static class FileSystemHelper { + private static readonly string _pathTemp = CommonHelper.MapPath(CommonHelper.GetAppSetting("sm:TempDirectory", "~/App_Data/_temp")); + private static readonly string _pathTempTenant = CommonHelper.MapPath(DataSettings.Current.TenantPath + "/_temp"); + /// /// Returns physical path to application temp directory /// /// Name of a sub directory to be created and returned (optional) public static string TempDir(string subDirectory = null) { - return TempDirInternal(CommonHelper.GetAppSetting("sm:TempDirectory", "~/App_Data/_temp"), subDirectory); + return TempDirInternal(_pathTemp, subDirectory); } /// @@ -24,18 +27,16 @@ public static string TempDir(string subDirectory = null) /// Name of a sub directory to be created and returned (optional) public static string TempDirTenant(string subDirectory = null) { - return TempDirInternal(DataSettings.Current.TenantPath + "/_temp", subDirectory); + return TempDirInternal(_pathTempTenant, subDirectory); } - private static string TempDirInternal(string virtualPath, string subDirectory = null) + private static string TempDirInternal(string path, string subDirectory = null) { - var path = CommonHelper.MapPath(virtualPath); - if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } - + if (subDirectory.HasValue()) { path = Path.Combine(path, subDirectory); @@ -50,41 +51,125 @@ private static string TempDirInternal(string virtualPath, string subDirectory = } /// - /// Safe way to cleanup the temp directory. Should be called via scheduled task. + /// Safe way to cleanup temporary directories. Should be called via scheduled task. /// - public static void TempCleanup() + public static void ClearTempDirectories() { - try - { - var dirs = new string[] { TempDir(), TempDirTenant() }; + var olderThan = TimeSpan.FromHours(5); + + ClearDirectory(new DirectoryInfo(TempDir()), false, olderThan); + ClearDirectory(new DirectoryInfo(TempDirTenant()), false, olderThan); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory path + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Delete only files older than this TimeSpan + /// Name of files not to be deleted + public static void ClearDirectory(string path, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) + { + if (path.IsEmpty()) + return; + + ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, olderThan, exceptFileNames); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory path + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Name of files not to be deleted + public static void ClearDirectory(string path, bool deleteIfEmpfy, params string[] exceptFileNames) + { + if (path.IsEmpty()) + return; + + ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); + } - foreach (var dir in dirs) + /// + /// Safe way to delete all directory content + /// + /// Directory info object + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Name of files not to be deleted + public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, params string[] exceptFileNames) + { + Guard.NotNull(dir, nameof(dir)); + + ClearDirectory(dir, deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory info object + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Delete only files older than this TimeSpan + /// Name of files not to be deleted + public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) + { + Guard.NotNull(dir, nameof(dir)); + + if (!dir.Exists) + return; + + var olderThanDate = DateTime.UtcNow.Subtract(olderThan); + + for (int i = 0; i < 10; i++) + { + try { - if (Directory.Exists(dir)) + foreach (var file in dir.EnumerateFiles().Where(fi => fi.LastWriteTime < olderThanDate)) { - var oldestDate = DateTime.Now.Subtract(new TimeSpan(0, 5, 0, 0)); - var files = Directory.EnumerateFiles(dir); + if (file.LastWriteTimeUtc >= olderThanDate) + continue; - foreach (string file in files) - { - var fi = new FileInfo(file); + if (exceptFileNames.Any(x => x.IsCaseInsensitiveEqual(file.Name))) + continue; - if (fi.LastWriteTime < oldestDate) - Delete(file); + if (file.IsReadOnly) + { + file.IsReadOnly = false; } + + file.Delete(); + } + + foreach (var subDir in dir.EnumerateDirectories()) + { + ClearDirectory(subDir, true, olderThan, exceptFileNames); } } + catch (Exception ex) + { + ex.Dump(); + } } - catch (Exception ex) + + if (deleteIfEmpfy) { - ex.Dump(); + try + { + if (dir.EnumerateFileSystemInfos().Any()) + { + dir.Delete(); + } + } + catch (Exception ex) + { + ex.Dump(); + } } } /// /// Safe way to delete a file. /// - public static bool Delete(string path) + public static bool DeleteFile(string path) { if (path.IsEmpty()) return true; @@ -96,7 +181,8 @@ public static bool Delete(string path) throw new UnauthorizedAccessException("Deleting folders not possible due to security reasons: {0}".FormatWith(path)); } - File.Delete(path); // no exception, if file doesn't exists + // No exception, if file doesn't exists + File.Delete(path); return true; } catch (Exception ex) @@ -109,7 +195,7 @@ public static bool Delete(string path) /// /// Safe way to copy a file. /// - public static bool Copy(string sourcePath, string destinationPath, bool overwrite = true, bool deleteSource = false) + public static bool CopyFile(string sourcePath, string destinationPath, bool overwrite = true, bool deleteSource = false) { bool result = true; try @@ -117,7 +203,7 @@ public static bool Copy(string sourcePath, string destinationPath, bool overwrit File.Copy(sourcePath, destinationPath, overwrite); if (deleteSource) - Delete(sourcePath); + DeleteFile(sourcePath); } catch (Exception exc) { @@ -174,73 +260,6 @@ public static bool CopyDirectory(DirectoryInfo source, DirectoryInfo target, boo return result; } - /// - /// Safe way to delete all directory content - /// - /// A directory path - /// Delete directoryPath too - /// Name of files not to be deleted - public static void ClearDirectory(string directoryPath, bool selfToo, List exceptFileNames = null) - { - if (directoryPath.IsEmpty()) - return; - - try - { - var dir = new DirectoryInfo(directoryPath); - - foreach (var fi in dir.GetFiles()) - { - if (exceptFileNames != null && exceptFileNames.Any(x => x.IsCaseInsensitiveEqual(fi.Name))) - continue; - - try - { - fi.IsReadOnly = false; - fi.Delete(); - } - catch (Exception) - { - try - { - Thread.Sleep(0); - fi.Delete(); - } - catch { } - } - } - - foreach (var di in dir.GetDirectories()) - { - ClearDirectory(di.FullName, false); - - try - { - di.Delete(); - } - catch (Exception) - { - try - { - Thread.Sleep(0); - di.Delete(); - } - catch (Exception) { } - } - } - } - catch (Exception) { } - - if (selfToo) - { - try - { - Directory.Delete(directoryPath, true); // just deletes the (now empty) directory - } - catch (Exception) { } - } - } - /// /// Creates a non existing directory name /// diff --git a/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs b/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs index a07dfe8cce..e02429fba3 100644 --- a/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs +++ b/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs @@ -10,7 +10,7 @@ public partial class TempFileCleanupTask : ITask { public void Execute(TaskExecutionContext ctx) { - FileSystemHelper.TempCleanup(); + FileSystemHelper.ClearTempDirectories(); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs index 811dfa2abe..6d1b16defc 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs @@ -333,7 +333,7 @@ private void StreamToFile(DataExporterContext ctx, Stream stream, string path, A if (ctx.ExecuteContext.Abort == DataExchangeAbortion.Hard && ctx.IsFileBasedExport && path.HasValue()) { - FileSystemHelper.Delete(path); + FileSystemHelper.DeleteFile(path); } } @@ -1786,8 +1786,8 @@ private void ExportCoreOuter(DataExporterContext ctx) var logPath = profile.GetExportLogPath(); var zipPath = profile.GetExportZipPath(); - FileSystemHelper.Delete(logPath); - FileSystemHelper.Delete(zipPath); + FileSystemHelper.DeleteFile(logPath); + FileSystemHelper.DeleteFile(zipPath); FileSystemHelper.ClearDirectory(ctx.FolderContent, false); using (var logger = new TraceLogger(logPath)) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs index a464efaf8a..e413acdde9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs @@ -257,7 +257,7 @@ private void ImportCoreOuter(DataImporterContext ctx) var customer = _services.WorkContext.CurrentCustomer; var profile = ctx.Request.Profile; var logPath = profile.GetImportLogPath(); - FileSystemHelper.Delete(logPath); + FileSystemHelper.DeleteFile(logPath); using (var logger = new TraceLogger(logPath)) { diff --git a/src/Libraries/SmartStore.Services/Media/ImageCache.cs b/src/Libraries/SmartStore.Services/Media/ImageCache.cs index b6f005e920..6ff7f04bcf 100644 --- a/src/Libraries/SmartStore.Services/Media/ImageCache.cs +++ b/src/Libraries/SmartStore.Services/Media/ImageCache.cs @@ -202,7 +202,7 @@ public virtual void Delete(IFile file) public virtual void Clear() { - for (int i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) { try { diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs index 0e6567ce8a..20a8480a0c 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs @@ -121,7 +121,7 @@ public static void DeleteLocalFiles(this MultipartFormDataStreamProvider provide try { foreach (var file in provider.FileData) - FileSystemHelper.Delete(file.LocalFileName); + FileSystemHelper.DeleteFile(file.LocalFileName); } catch { } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index a1f462b301..603fedac78 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -910,7 +910,7 @@ public ActionResult MaintenanceDeleteFiles(MaintenanceModel model) if ((!startDateValue.HasValue || startDateValue.Value < info.CreationTimeUtc) && (!endDateValue.HasValue || info.CreationTimeUtc < endDateValue.Value)) { - if (FileSystemHelper.Delete(fullPath)) + if (FileSystemHelper.DeleteFile(fullPath)) ++model.DeleteExportedFiles.NumberOfDeletedFiles; } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs index 77927bf619..e9ab64e7b5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs @@ -66,7 +66,7 @@ private bool IsValidImportFile(string path, out string error) catch (Exception ex) { error = ex.ToAllMessages(); - FileSystemHelper.Delete(path); + FileSystemHelper.DeleteFile(path); return false; } } @@ -397,7 +397,7 @@ public ActionResult Create(ImportProfileModel model) { var importFileDestination = Path.Combine(profile.GetImportFolder(true, true), model.TempFileName); - FileSystemHelper.Copy(importFile, importFileDestination, true, true); + FileSystemHelper.CopyFile(importFile, importFileDestination, true, true); return RedirectToAction("Edit", new { id = profile.Id }); } @@ -611,7 +611,7 @@ public JsonResult FileUpload(int id) if (id == 0) { var path = Path.Combine(FileSystemHelper.TempDirTenant(), postedFile.FileName); - FileSystemHelper.Delete(path); + FileSystemHelper.DeleteFile(path); success = postedFile.Stream.ToFile(path); if (success) @@ -787,7 +787,7 @@ public ActionResult DeleteImportFile(int id, string name) if (profile != null) { var path = Path.Combine(profile.GetImportFolder(true), name); - FileSystemHelper.Delete(path); + FileSystemHelper.DeleteFile(path); } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs index f5388be5d7..0bfdb6d317 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs @@ -804,7 +804,7 @@ public async Task ImportXml(int id, FormCollection form, ImportMod } finally { - FileSystemHelper.Delete(tempFilePath); + FileSystemHelper.DeleteFile(tempFilePath); } return RedirectToAction("Edit", new { id = language.Id }); @@ -882,7 +882,7 @@ private void DownloadCore(ILifetimeScope scope, CancellationToken ct, LanguageDo asyncState.Remove(); } - FileSystemHelper.Delete(tempFilePath); + FileSystemHelper.DeleteFile(tempFilePath); } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/RoxyFileManagerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/RoxyFileManagerController.cs index f741ba1a7c..f759a170bb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/RoxyFileManagerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/RoxyFileManagerController.cs @@ -459,7 +459,7 @@ private void DownloadDir(string path) // create zip from temp folder var tempZip = Path.Combine(FileSystemHelper.TempDirTenant(), folder.Name + ".zip"); - FileSystemHelper.Delete(tempZip); + FileSystemHelper.DeleteFile(tempZip); ZipFile.CreateFromDirectory(tempDir, tempZip, CompressionLevel.Fastest, false); @@ -469,7 +469,7 @@ private void DownloadDir(string path) Response.TransmitFile(tempZip); Response.Flush(); - FileSystemHelper.Delete(tempZip); + FileSystemHelper.DeleteFile(tempZip); FileSystemHelper.ClearDirectory(tempDir, true); Response.End(); From 855747f34bed56dd5c5314921e85495374e20120 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 12 Mar 2019 01:43:22 +0100 Subject: [PATCH 010/460] Minor bundling & css fix --- .../SmartStore.Web.Framework/UI/BundleBuilder.cs | 2 +- .../Administration/Content/theme.scss | 2 +- .../Administration/Views/Blog/List.cshtml | 2 +- .../Views/MessageTemplate/Edit.cshtml | 2 +- .../Views/Order/ShipmentDetails.cshtml | 2 +- .../Views/Order/_Edit.BillingAndShipment.cshtml | 4 ++-- .../Administration/Views/Order/_Edit.Info.cshtml | 6 +++--- .../Administration/Views/Setting/Blog.cshtml | 2 +- .../Views/Shared/Layouts/_AdminRoot.cshtml | 1 - .../Views/Shared/Partials/_Providers.cshtml | 4 ++-- .../SmartStore.Web/Content/shared/_fa.scss | 15 ++++++++------- .../Content/shared/_variables-shared.scss | 1 + .../Views/Boards/Partials/_ForumPost.cshtml | 2 +- .../SmartStore.Web/Views/Boards/Topic.cshtml | 2 +- 14 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs index 241a3f953e..7d5850f6b2 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs @@ -101,7 +101,7 @@ protected virtual string GetBundleVirtualPath(BundleType type, IEnumerable x)) + foreach (var file in files) { hashInput += file; hashInput += ","; diff --git a/src/Presentation/SmartStore.Web/Administration/Content/theme.scss b/src/Presentation/SmartStore.Web/Administration/Content/theme.scss index a9953de8c5..a45bc8dda9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/theme.scss +++ b/src/Presentation/SmartStore.Web/Administration/Content/theme.scss @@ -123,7 +123,7 @@ // -// Telerik adaptation: +// Vendor adaptations: // Must come latest because of css // specificity. // -------------------------------------------------- diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml index 1301a3b84e..9375955cd7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml @@ -8,7 +8,7 @@ }
- + @T("Admin.ContentManagement.Blog.BlogPosts")
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/Edit.cshtml index 94d40249ad..1b4d9ae4c9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/Edit.cshtml @@ -7,7 +7,7 @@ {
- + @T("Admin.ContentManagement.MessageTemplates.EditMessageTemplateDetails") - @Model.Name @Html.ActionLink("(" + T("Admin.ContentManagement.MessageTemplates.BackToList") + ")", "List")
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml index 48ee154e8d..35af7ea85a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml @@ -36,7 +36,7 @@ @Html.EditorFor(model => model.TrackingNumber) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/_Edit.BillingAndShipment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/_Edit.BillingAndShipment.cshtml index 9b53e5d2cb..2e47d7e3d5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/_Edit.BillingAndShipment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/_Edit.BillingAndShipment.cshtml @@ -10,7 +10,7 @@
@@ -25,7 +25,7 @@
+ + + +@* \ No newline at end of file +*@ \ No newline at end of file From 61b4a1ef6cfd7f712158503a6e6c7602fa3732e1 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 21 Mar 2019 21:36:07 +0100 Subject: [PATCH 036/460] Fixes in rare cases a shipping options error may not have been displayed in checkout --- .../Controllers/CheckoutController.cs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs b/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs index 002fe5fa15..759689b47e 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CheckoutController.cs @@ -610,37 +610,43 @@ public ActionResult NewShippingAddress(CheckoutShippingAddressModel model) public ActionResult ShippingMethod() { - //validation - var cart = _workContext.CurrentCustomer.GetCartItems(ShoppingCartType.ShoppingCart, _storeContext.CurrentStore.Id); + var store = _storeContext.CurrentStore; + var customer = _workContext.CurrentCustomer; + var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); - if (cart.Count == 0) + if (cart.Count == 0) + { return RedirectToRoute("ShoppingCart"); + } - if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed)) + if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) + { return new HttpUnauthorizedResult(); + } if (!cart.RequiresShipping()) { - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.SelectedShippingOption, null, _storeContext.CurrentStore.Id); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedShippingOption, null, store.Id); return RedirectToAction("PaymentMethod"); } - - var shippingOptions = _shippingService.GetShippingOptions(cart, _workContext.CurrentCustomer.ShippingAddress, "", _storeContext.CurrentStore.Id).ShippingOptions; - var checkoutState = _httpContext.GetCheckoutState(); - if (checkoutState.CustomProperties.ContainsKey("HasOnlyOneActiveShippingMethod")) - checkoutState.CustomProperties["HasOnlyOneActiveShippingMethod"] = shippingOptions.Count == 1; + var response = _shippingService.GetShippingOptions(cart, customer.ShippingAddress, "", store.Id); + var options = response.ShippingOptions; + var state = _httpContext.GetCheckoutState(); + + if (state.CustomProperties.ContainsKey("HasOnlyOneActiveShippingMethod")) + { + state.CustomProperties["HasOnlyOneActiveShippingMethod"] = options.Count == 1; + } else - checkoutState.CustomProperties.Add("HasOnlyOneActiveShippingMethod", shippingOptions.Count == 1); + { + state.CustomProperties.Add("HasOnlyOneActiveShippingMethod", options.Count == 1); + } - if (shippingOptions.Count <= 1 && _shippingSettings.SkipShippingIfSingleOption) + if (options.Count <= 1 && _shippingSettings.SkipShippingIfSingleOption && response.Success) { - _genericAttributeService.SaveAttribute( - _workContext.CurrentCustomer, - SystemCustomerAttributeNames.SelectedShippingOption, - shippingOptions.FirstOrDefault(), - _storeContext.CurrentStore.Id); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedShippingOption, options.FirstOrDefault(), store.Id); var referrer = Services.WebHelper.GetUrlReferrer(); if (referrer.EndsWith("/PaymentMethod") || referrer.EndsWith("/Confirm")) @@ -651,7 +657,6 @@ public ActionResult ShippingMethod() return RedirectToAction("PaymentMethod"); } - //model var model = PrepareShippingMethodModel(cart); return View(model); } From b3e7465d4bc0b1cc633597dd9f0c81e16446a207 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 22 Mar 2019 00:37:24 +0100 Subject: [PATCH 037/460] LinkBuilder: Minor fix --- .../SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml index 59e5086b4b..fa36ffe5bf 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml @@ -35,7 +35,7 @@ ? arr.Select(x => (LinkType)Enum.Parse(typeof(LinkType), x, true)).ToArray() : new LinkType[] { LinkType.Product, LinkType.Category, LinkType.Manufacturer, LinkType.Topic, LinkType.File, LinkType.Url }; - Html.AddScriptParts("~/Scripts/smartstore.linkbuilder.js"); + Html.AddScriptParts(true, "~/Scripts/smartstore.linkbuilder.js"); } +
+
@(Html.LocalizedEditor("menu-info-localized", - @ - - - - -
+ @
+
+
@Html.SmartLabelFor(model => model.Locales[item].Title) -
+ +
@*IMPORTANT: Do not delete, this hidden element contains the id to assign localized values to the corresponding language *@ @Html.HiddenFor(model => model.Locales[item].LanguageId, new { id = Html.FieldIdFor(model => model.Locales[item].LanguageId) + "_1" }) @Html.EditorFor(model => model.Locales[item].Title) @Html.ValidationMessageFor(model => model.Locales[item].Title) -
+
+
+
, - @ - - - - -
+ @
+
+
@Html.SmartLabelFor(model => model.Title) -
+ +
@Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) -
+ + + )) + + if (Model.Id > 0) + { + @Html.Partial("_MenuItemList", Model) + } } @helper TabStores() diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml new file mode 100644 index 0000000000..96d7da7ed7 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml @@ -0,0 +1,21 @@ +@using SmartStore.Admin.Models.Menus +@model MenuRecordModel +@{ +} + +
+
+
+
@T("Admin.ContentManagement.MenuLinks")
+
+
+
+ +
+ TODO: bootstrap list groups. +
+ + From dcd8e44357a8cbb77ccc432b331d528d4aadc8b6 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 22 Mar 2019 20:51:52 +0100 Subject: [PATCH 041/460] Menu builder --- .../Administration/Content/_admin.scss | 28 ++++++++-- .../Controllers/MenuController.cs | 5 +- .../Models/Menus/MenuRecordModel.cs | 2 +- .../Views/Menu/_MenuItemList.cshtml | 51 +++++++++++++++---- 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss b/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss index 2426cbdd0f..b96e6e9952 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss +++ b/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss @@ -1387,6 +1387,29 @@ table.payment-method-features td { } } +// +// Menus +// -------------------------------------------------------------- + +.menu-item-list .list-group { + .list-group-item { + overflow: visible; + + .item-commands { + opacity: 0; + transition: opacity 0.05s linear; + } + + &:hover .item-commands { + opacity: 1; + } + } + + .sub-item:last-child { + border-bottom: 0 none; + } +} + // // Misc @@ -1407,11 +1430,6 @@ table.payment-method-features td { } } - -// -// Misc -// -------------------------------------------------------------- - .cke_chrome.cke_focus { border-color: $input-focus-border-color; box-shadow: none; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index c3715ddd36..f985ab70de 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -1,6 +1,8 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Web.Mvc; using SmartStore.Admin.Models.Menus; +using SmartStore.Collections; using SmartStore.ComponentModel; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Domain.Common; @@ -14,6 +16,7 @@ using SmartStore.Web.Framework.Filters; using SmartStore.Web.Framework.Modelling; using SmartStore.Web.Framework.Security; +using SmartStore.Web.Framework.UI; using Telerik.Web.Mvc; namespace SmartStore.Admin.Controllers diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs index f77a28eb49..314f3b554a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs @@ -43,7 +43,7 @@ public MenuRecordModel() public IList Locales { get; set; } - public TreeNode Items { get; set; } + public TreeNode Root { get; set; } } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml index 96d7da7ed7..f77c1f03b5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml @@ -1,19 +1,48 @@ -@using SmartStore.Admin.Models.Menus +@using SmartStore.Collections +@using SmartStore.Admin.Models.Menus @model MenuRecordModel @{ } - -
-
-
-
@T("Admin.ContentManagement.MenuLinks")
-
+

@T("Admin.ContentManagement.MenuLinks")

+@if (Model.Root != null) +{ + -
+} -
- TODO: bootstrap list groups. -
+@helper ItemGroup(TreeNode node, int level) +{ + var indentStyle = level != 0 ? " style=\"padding-left: {0}px;\"".FormatInvariant(level == 1 ? 20 : level * 30) : ""; +
+
@node.Value.Text
+
+ + +
+
+ if (node.HasChildren) + { +
+ @foreach (var child in node.Children) + { + @ItemGroup(child, level + 1); + } +
+ } +} diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml deleted file mode 100644 index f77c1f03b5..0000000000 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_MenuItemList.cshtml +++ /dev/null @@ -1,50 +0,0 @@ -@using SmartStore.Collections -@using SmartStore.Admin.Models.Menus -@model MenuRecordModel -@{ -} -

@T("Admin.ContentManagement.MenuLinks")

-@if (Model.Root != null) -{ - -} - -@helper ItemGroup(TreeNode node, int level) -{ - var indentStyle = level != 0 ? " style=\"padding-left: {0}px;\"".FormatInvariant(level == 1 ? 20 : level * 30) : ""; -
-
@node.Value.Text
-
- - -
-
- if (node.HasChildren) - { -
- @foreach (var child in node.Children) - { - @ItemGroup(child, level + 1); - } -
- } -} - - From 9e6db7c0f4a004747ccc19a8c5c52a0000095e44 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 25 Mar 2019 12:18:07 +0100 Subject: [PATCH 046/460] Minor change --- .../Controllers/MenuController.cs | 27 +------------------ .../Administration/Views/Menu/ItemList.cshtml | 23 ++++++++-------- .../Views/Menu/_CreateOrUpdate.cshtml | 2 +- 3 files changed, 14 insertions(+), 38 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index 6a1493b46b..a03423b984 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -452,32 +452,7 @@ private void UpdateLocales(MenuItemRecord entity, MenuItemRecordModel model) private TreeNode GetItemList(int id) { - return new TreeNode(new MenuItem { Text = "Root" }, new List> - { - new TreeNode(new MenuItem { Text = "Item 1", EntityId = 1 }), - new TreeNode(new MenuItem { Text = "Item 2", EntityId = 3 }), - new TreeNode(new MenuItem { Text = "Item 3", EntityId = 4 }, new List> - { - new TreeNode(new MenuItem { Text = "Sub 1", EntityId = 7 }), - new TreeNode(new MenuItem { Text = "Sub 2", EntityId = 8, Visible = false }), - new TreeNode(new MenuItem { Text = "Sub 3", EntityId = 10 }, new List> - { - new TreeNode(new MenuItem { Text = "Sub 1", EntityId = 13 }), - new TreeNode(new MenuItem { Text = "Sub 2", EntityId = 15 }), - new TreeNode(new MenuItem { Text = "Sub 3", EntityId = 16 }, new List> - { - new TreeNode(new MenuItem { Text = "Sub 1", EntityId = 17 }), - new TreeNode(new MenuItem { Text = "Sub 2", EntityId = 9 }) - }), - new TreeNode(new MenuItem { Text = "Sub 4", EntityId = 11 }), - new TreeNode(new MenuItem { EntityId = 21, Attributes = new Dictionary { { "IsDivider", true } } }), - new TreeNode(new MenuItem { Text = "Sub 5", EntityId = 12 }), - new TreeNode(new MenuItem { Text = "Sub 6", EntityId = 18 }) - }) - }), - new TreeNode(new MenuItem { Text = "Item 4", EntityId = 19 }), - new TreeNode(new MenuItem { Text = "Item 5", EntityId = 20 }) - }); + return new TreeNode(new MenuItem { Text = "Root" }); } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml index 0fb508c6c3..3fef89b8f9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml @@ -22,9 +22,9 @@ else
@T("Admin.ContentManagement.Menus.NoMenuItemsAvailable")
-
- @AddRootItem() -
+
+
+ @AddRootItem()
} @@ -33,7 +33,7 @@ else - @T("Admin.ContentManagement.AddMenuItem") + @T("Admin.ContentManagement.AddMenuItem")… } @@ -55,17 +55,18 @@ else diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml index 67331d3cce..4e27e8dfa9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml @@ -67,7 +67,7 @@ )) -

@T("Admin.ContentManagement.MenuLinks")

+

@T("Admin.ContentManagement.MenuLinks")

From ddc3f91cb5d1dc42c49ce05d3a777318a3b9c993 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 25 Mar 2019 14:19:22 +0100 Subject: [PATCH 047/460] Resolves #1591, #1592 Path traversal vulnerability --- .../Controllers/ExportController.cs | 22 ++++++++++++++----- .../Controllers/ImportController.cs | 15 ++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 3ad1460048..d971885b87 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Mime; using System.Text; using System.Web.Mvc; @@ -30,6 +31,7 @@ using SmartStore.Services.Messages; using SmartStore.Services.Security; using SmartStore.Services.Tasks; +using SmartStore.Utilities; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Filters; @@ -1304,9 +1306,15 @@ public ActionResult DownloadLogFile(int id) return RedirectToAction("List"); } + [HttpGet] public ActionResult DownloadExportFile(int id, string name, bool? isDeployment) { - string message = null; + if (PathHelper.HasInvalidFileNameChars(name)) + { + return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "Invalid file name."); + } + + string message = null; string path = null; if (Services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) @@ -1317,8 +1325,10 @@ public ActionResult DownloadExportFile(int id, string name, bool? isDeployment) if (deployment != null) { var deploymentFolder = deployment.GetDeploymentFolder(); - if (deploymentFolder.HasValue()) - path = Path.Combine(deploymentFolder, name); + if (deploymentFolder.HasValue()) + { + path = Path.Combine(deploymentFolder, name); + } } } else @@ -1327,8 +1337,10 @@ public ActionResult DownloadExportFile(int id, string name, bool? isDeployment) if (profile != null) { path = Path.Combine(profile.GetExportFolder(true), name); - if (!System.IO.File.Exists(path)) - path = Path.Combine(profile.GetExportFolder(false), name); + if (!System.IO.File.Exists(path)) + { + path = Path.Combine(profile.GetExportFolder(false), name); + } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs index 13c8c34441..ef2131818c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Mime; using System.Text; using System.Web.Mvc; @@ -733,9 +734,15 @@ public ActionResult DownloadLogFile(int id) return RedirectToAction("List"); } + [HttpGet] public ActionResult DownloadImportFile(int id, string name) { - string message = null; + if (PathHelper.HasInvalidFileNameChars(name)) + { + return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "Invalid file name."); + } + + string message = null; if (Services.Permissions.Authorize(StandardPermissionProvider.ManageImports)) { @@ -744,8 +751,10 @@ public ActionResult DownloadImportFile(int id, string name) { var path = Path.Combine(profile.GetImportFolder(true), name); - if (!System.IO.File.Exists(path)) - path = Path.Combine(profile.GetImportFolder(false), name); + if (!System.IO.File.Exists(path)) + { + path = Path.Combine(profile.GetImportFolder(false), name); + } if (System.IO.File.Exists(path)) { From 22b699f97e83cda7a94e3138f7eb9d892c1c8601 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 25 Mar 2019 21:29:31 +0100 Subject: [PATCH 048/460] Please execute update-database -TargetMigration TopicHtmlIdAndBodyCss --- ...r.cs => 201903252015583_Menus.Designer.cs} | 2 +- ...4425_Menus.cs => 201903252015583_Menus.cs} | 0 ..._Menus.resx => 201903252015583_Menus.resx} | 0 .../SmartStore.Data/SmartStore.Data.csproj | 10 +-- .../SmartStore.Data/Utilities/DataMigrator.cs | 75 ++++++++++--------- 5 files changed, 44 insertions(+), 43 deletions(-) rename src/Libraries/SmartStore.Data/Migrations/{201903202044425_Menus.Designer.cs => 201903252015583_Menus.Designer.cs} (93%) rename src/Libraries/SmartStore.Data/Migrations/{201903202044425_Menus.cs => 201903252015583_Menus.cs} (100%) rename src/Libraries/SmartStore.Data/Migrations/{201903202044425_Menus.resx => 201903252015583_Menus.resx} (100%) diff --git a/src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.Designer.cs similarity index 93% rename from src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.Designer.cs rename to src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.Designer.cs index 410e94a834..0e4caeeeb8 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.Designer.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.Designer.cs @@ -13,7 +13,7 @@ public sealed partial class Menus : IMigrationMetadata string IMigrationMetadata.Id { - get { return "201903202044425_Menus"; } + get { return "201903252015583_Menus"; } } string IMigrationMetadata.Source diff --git a/src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.cs b/src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.cs similarity index 100% rename from src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.cs rename to src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.cs diff --git a/src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.resx b/src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.resx similarity index 100% rename from src/Libraries/SmartStore.Data/Migrations/201903202044425_Menus.resx rename to src/Libraries/SmartStore.Data/Migrations/201903252015583_Menus.resx diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 0567e72736..3d9c8b8d92 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -654,9 +654,9 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - - 201903202044425_Menus.cs + + + 201903252015583_Menus.cs @@ -1179,8 +1179,8 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - 201903202044425_Menus.cs + + 201903252015583_Menus.cs diff --git a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs index d6b6dff20c..5995141656 100644 --- a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs +++ b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs @@ -451,8 +451,9 @@ public static void CreateSystemMenus(IDbContext context) throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); } - const string linkBuilderProvider = "LinkBuilder"; - const string routeNameProvider = "RouteName"; + const string entityProvider = "entity"; + const string routeProvider = "route"; + const string routeTemplate = "{{\"routename\":\"{0}\"}}"; var menuSet = ctx.Set(); var menuItemSet = ctx.Set(); @@ -535,8 +536,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerInfo.Id, - SystemName = routeNameProvider, - Model = "ManufacturerList", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("ManufacturerList"), Title = GetResource("Manufacturers.List"), DisplayOrder = ++order, Published = manufacturerCount > 0 @@ -544,8 +545,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerInfo.Id, - SystemName = routeNameProvider, - Model = "RecentlyAddedProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), Title = GetResource("Products.NewProducts"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) @@ -553,8 +554,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerInfo.Id, - SystemName = routeNameProvider, - Model = "RecentlyViewedProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), Title = GetResource("Products.RecentlyViewedProducts"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) @@ -562,8 +563,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerInfo.Id, - SystemName = routeNameProvider, - Model = "CompareProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("CompareProducts"), Title = GetResource("Products.Compare.List"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) @@ -575,16 +576,16 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerService.Id, - SystemName = routeNameProvider, - Model = "contactus", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("contactus"), Title = GetResource("ContactUs"), DisplayOrder = ++order }); menuItemSet.Add(new MenuItemRecord { MenuId = footerService.Id, - SystemName = routeNameProvider, - Model = "Blog", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("Blog"), Title = GetResource("Blog"), DisplayOrder = ++order, Published = GetSetting("BlogSettings.Enabled", false) @@ -592,8 +593,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerService.Id, - SystemName = routeNameProvider, - Model = "Boards", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("Boards"), Title = GetResource("Forum.Forums"), DisplayOrder = ++order, Published = GetSetting("ForumSettings.ForumsEnabled", false) @@ -601,7 +602,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerService.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:shippinginfo", Title = GetResource("ShippingReturns"), DisplayOrder = ++order @@ -609,7 +610,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerService.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:paymentinfo", Title = GetResource("Paymentinfo"), DisplayOrder = ++order @@ -621,7 +622,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:aboutus", Title = GetResource("AboutUs"), DisplayOrder = ++order @@ -629,7 +630,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:imprint", Title = GetResource("Imprint"), DisplayOrder = ++order @@ -637,7 +638,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:disclaimer", Title = GetResource("Disclaimer"), DisplayOrder = ++order @@ -645,7 +646,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:privacyinfo", Title = GetResource("PrivacyNotice"), DisplayOrder = ++order @@ -653,7 +654,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:conditionsofuse", Title = GetResource("ConditionsOfUse"), DisplayOrder = ++order @@ -664,8 +665,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - SystemName = routeNameProvider, - Model = "Login", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("Login"), Title = GetResource("Account.Login"), DisplayOrder = ++order }); @@ -691,8 +692,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = routeNameProvider, - Model = "RecentlyAddedProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), Title = GetResource("Products.NewProducts"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) @@ -700,8 +701,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = routeNameProvider, - Model = "ManufacturerList", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("ManufacturerList"), Title = GetResource("Manufacturers.List"), DisplayOrder = ++order, Published = manufacturerCount > 0 @@ -709,8 +710,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = routeNameProvider, - Model = "RecentlyViewedProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), Title = GetResource("Products.RecentlyViewedProducts"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) @@ -718,8 +719,8 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = routeNameProvider, - Model = "CompareProducts", + SystemName = routeProvider, + Model = routeTemplate.FormatInvariant("CompareProducts"), Title = GetResource("Products.Compare.List"), DisplayOrder = ++order, Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) @@ -734,7 +735,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:aboutus", Title = GetResource("AboutUs"), DisplayOrder = ++order @@ -742,7 +743,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:disclaimer", Title = GetResource("Disclaimer"), DisplayOrder = ++order @@ -750,7 +751,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:shippinginfo", Title = GetResource("ShippingReturns"), DisplayOrder = ++order @@ -758,7 +759,7 @@ public static void CreateSystemMenus(IDbContext context) menuItemSet.Add(new MenuItemRecord { MenuId = serviceMenu.Id, - SystemName = linkBuilderProvider, + SystemName = entityProvider, Model = "topic:conditionsofuse", Title = GetResource("ConditionsOfUse"), DisplayOrder = ++order From 0cea7c7bd49b9456ee03a4e96e0d4bb65afa8c8c Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 26 Mar 2019 16:40:49 +0100 Subject: [PATCH 049/460] Menu builder --- .../SmartStore.Services/Cms/LinkResolver.cs | 3 +- .../Cms/LinkResolverResult.cs | 2 +- .../Cms/Menus/IMenuStorage.cs | 7 ++ .../Cms/Menus/MenuStorage.cs | 35 +++++++++- .../DependencyRegistrar.cs | 27 +++++++- .../SmartStore.Web.Framework.csproj | 2 + .../UI/Menus/DatabaseMenuResolver.cs | 27 ++++---- .../UI/Menus/IMenuService.cs | 5 +- .../UI/Menus/MenuService.cs | 65 +++++++++++++++++++ .../Menus/Providers/EntityMenuItemProvider.cs | 39 ++++++----- .../UI/Menus/Providers/MenuItemMetadata.cs | 7 ++ .../Menus/Providers/MenuItemProviderBase.cs | 40 +++++++++--- .../Menus/Providers/RouteMenuItemProvider.cs | 47 +++++++++----- .../Controllers/MenuController.cs | 20 ++++-- 14 files changed, 252 insertions(+), 74 deletions(-) create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index c9498ea3d0..96173eafd0 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -132,7 +132,8 @@ public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable GetAllMenus( /// Menu entity. MenuRecord GetMenuById(int id, bool withItems = false); + /// + /// Checks whether the menu exists. + /// + /// Menu system name. + /// true the menu exists, false the menu doesn't exist. + bool MenuExists(string systemName); + #region Menu items /// diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index fc69cffc16..f5c162b4ca 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -11,6 +11,8 @@ namespace SmartStore.Services.Cms { public partial class MenuStorage : IMenuStorage { + private const string MENU_SYSTEMNAME_CACHE_KEY = "SmartStore.Menu.Storage.SystemNames"; + private readonly IRepository _menuRepository; private readonly IRepository _menuItemRepository; private readonly IRepository _storeMappingRepository; @@ -40,21 +42,34 @@ public virtual void InsertMenu(MenuRecord menu) Guard.NotNull(menu, nameof(MenuRecord)); _menuRepository.Insert(menu); + + _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); } public virtual void UpdateMenu(MenuRecord menu) { Guard.NotNull(menu, nameof(MenuRecord)); + var oldSystemName = menu.SystemName; + _menuRepository.Update(menu); + + if (oldSystemName != menu.SystemName) + { + _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); + } } public virtual void DeleteMenu(MenuRecord menu) { - if (menu != null) + if (menu == null) { - _menuRepository.Delete(menu); + return; } + + _menuRepository.Delete(menu); + + _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); } public virtual IPagedList GetAllMenus( @@ -86,6 +101,22 @@ public virtual MenuRecord GetMenuById(int id, bool withItems = false) return query.FirstOrDefault(x => x.Id == id); } + public virtual bool MenuExists(string systemName) + { + if (systemName.IsEmpty()) + { + return false; + } + + var data = _services.Cache.Get(MENU_SYSTEMNAME_CACHE_KEY, () => + { + var systemNames = _menuRepository.TableUntracked.Select(x => x.SystemName).ToList(); + return new HashSet(systemNames); + }); + + return data.Contains(systemName); + } + #region Menu items public virtual void InsertMenuItem(MenuItemRecord item) diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 218fdf32cf..9b87959f25 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -89,6 +89,7 @@ using SmartStore.Web.Framework.Theming; using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Web.Framework.UI; +using SmartStore.Web.Framework.UI.Menus; using SmartStore.Web.Framework.WebApi; using SmartStore.Web.Framework.WebApi.Configuration; using Module = Autofac.Module; @@ -809,10 +810,10 @@ public class UiModule : Module { private readonly ITypeFinder _typeFinder; - public UiModule(ITypeFinder typeFinder) + public UiModule(ITypeFinder typeFinder) { _typeFinder = typeFinder; - } + } protected override void Load(ContainerBuilder builder) { @@ -841,7 +842,27 @@ protected override void Load(ContainerBuilder builder) { builder.RegisterType(type).As().PropertiesAutowired(PropertyWiringOptions.None).InstancePerRequest(); } - } + + // Menus. + builder.RegisterType().As().InstancePerRequest(); + + var menuResolverTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + foreach (var type in menuResolverTypes) + { + builder.RegisterType(type).As().PropertiesAutowired(PropertyWiringOptions.None).InstancePerRequest(); + } + + var menuItemProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + foreach (var type in menuItemProviderTypes) + { + var systemName = type.GetAttribute(false)?.Name.EmptyNull(); + var registration = builder.RegisterType(type).As().PropertiesAutowired(PropertyWiringOptions.None).InstancePerRequest(); + registration.WithMetadata(m => + { + m.For(em => em.SystemName, systemName); + }); + } + } } public class IOModule : Module diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 1f71c90b83..8272af9f89 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -372,10 +372,12 @@ + + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs index 359a1362a8..fb0a12c682 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs @@ -1,26 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI { - public class DatabaseMenuResolver : IMenuResolver + public class DatabaseMenuResolver : IMenuResolver { - public bool Exists(string menuName) - { - // TODO: Check whether a menu with passed name exists in the database. - // Make it FAST, e.g. by caching the existing menu names in a HashSet (don't forget invalidation). + protected readonly IMenuStorage _menuStorage; + + public DatabaseMenuResolver(IMenuStorage menuStorage) + { + _menuStorage = menuStorage; + } - throw new NotImplementedException(); + public bool Exists(string menuName) + { + return _menuStorage.MenuExists(menuName); } public IMenu Resolve(string name) { - // TODO: Resolve menus from database > MenuRecord - - throw new NotImplementedException(); + + return null; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs index fbb0288300..5fed9fd0bd 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs @@ -1,10 +1,9 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - public interface IMenuService // INFO: replaces ISiteMapService + public partial interface IMenuService // INFO: replaces ISiteMapService { IMenu GetMenu(string name); TreeNode GetRootNode(string menuName); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs new file mode 100644 index 0000000000..d67f4d11ca --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Web.Mvc; +using SmartStore.Collections; + +namespace SmartStore.Web.Framework.UI.Menus +{ + public partial class MenuService : IMenuService + { + protected readonly IEnumerable _menuResolvers; + + private TreeNode _currentNode; + private bool _currentNodeResolved; + + public MenuService(IEnumerable menuResolvers) + { + _menuResolvers = menuResolvers; + } + + public virtual IMenu GetMenu(string name) + { + if (name.HasValue()) + { + foreach (var resolver in _menuResolvers) + { + if (resolver.Exists(name)) + { + return resolver.Resolve(name); + } + } + } + + return null; + } + + public virtual TreeNode GetRootNode(string menuName) + { + return GetMenu(menuName)?.Root; + } + + public virtual TreeNode GetCurrentNode(string menuName, ControllerContext controllerContext) + { + if (!_currentNodeResolved) + { + var menu = GetMenu(menuName); + if (menu != null) + { + _currentNode = menu.Root.SelectNode(x => x.Value.IsCurrent(controllerContext), true); + } + _currentNodeResolved = true; + } + + return _currentNode; + } + + public virtual void ResolveElementCounts(string menuName, TreeNode curNode, bool deep = false) + { + GetMenu(menuName)?.ResolveElementCounts(curNode, deep); + } + + public virtual void ClearCache(string menuName) + { + GetMenu(menuName)?.ClearCache(); + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index b65b4bd953..adafd90e03 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -1,28 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Collections; +using SmartStore.Collections; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Plugins; +using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI { - // INFO: The provider's SystemName is also the edit template name > Views/Shared/EditorTemplates/MenuItem.{SystemName}.cshtml. - // Model is: string - [SystemName("entity")] + // INFO: The provider's SystemName is also the edit template name > Views/Shared/EditorTemplates/MenuItem.{SystemName}.cshtml. + // Model is: string + [SystemName("entity")] public class EntityMenuItemProvider : MenuItemProviderBase { - protected override void ApplyLink(TreeNode node, MenuItemRecord entity) + private readonly ILinkResolver _linkResolver; + + public EntityMenuItemProvider(ILinkResolver linkResolver) + { + _linkResolver = linkResolver; + } + + protected override void ApplyLink(TreeNode node, MenuItemRecord entity) { - var url = string.Empty; // TODO: create url with LinkResolver - var entityName = string.Empty; // TODO: determine entity name/type, if applicable - var id = 0; // TODO: determine entity id, if applicable + if (entity.Model.IsEmpty()) + { + return; + } + + // TODO: to resolve for what roles, what language, what store? + var result = _linkResolver.Resolve(entity.Model); - node.Id = entity + "." + id; - node.Value.EntityId = id; - node.Value.Url = url; + node.Id = string.Concat(result.Type.ToString().ToLower(), ".", result.Id); + node.Value.EntityId = result.Id; + node.Value.Url = result.Link; // TBD: What about cache invalidation? We would also have two levels of caching: // One in the LinkResolver and one in the menu system. diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs new file mode 100644 index 0000000000..caa05bb722 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs @@ -0,0 +1,7 @@ +namespace SmartStore.Web.Framework.UI +{ + public class MenuItemMetadata + { + public string SystemName { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 074cde2590..12879368b9 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Collections; +using SmartStore.Collections; using SmartStore.Core.Domain.Cms; namespace SmartStore.Web.Framework.UI { - public abstract class MenuItemProviderBase : IMenuItemProvider + public abstract class MenuItemProviderBase : IMenuItemProvider { public virtual void Append(TreeNode parent, MenuItemRecord entity) { @@ -21,17 +16,42 @@ public virtual void Append(TreeNode parent, MenuItemRecord entity) } /// - /// Converts the passed menu item entity to a object + /// Converts the passed menu item entity to a object. /// /// The entity to convert. - /// + /// Menu item. protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) { var menuItem = new MenuItem { - // TODO: convert + Text = entity.Title, + Visible = entity.Published }; + // TODO: support divider elements. + //menuItem.Attributes.Add("IsDivider", entity.IsDivider); + + if (entity.NoFollow) + { + menuItem.LinkHtmlAttributes.Add("rel", "nofollow"); + } + if (entity.ShortDescription.HasValue()) + { + menuItem.LinkHtmlAttributes.Add("title", entity.ShortDescription); + } + if (entity.NewWindow) + { + menuItem.LinkHtmlAttributes.Add("target", "_blank"); + } + if (entity.HtmlId.HasValue()) + { + menuItem.LinkHtmlAttributes.Add("id", entity.HtmlId); + } + if (entity.CssClass.HasValue()) + { + menuItem.LinkHtmlAttributes.Add("class", entity.CssClass); + } + return menuItem; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs index 4a0c2913ea..019e89c340 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs @@ -1,31 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web.Routing; +using System.Web.Routing; +using Newtonsoft.Json; using SmartStore.Collections; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Plugins; namespace SmartStore.Web.Framework.UI { - [SystemName("route")] + [SystemName("route")] public class RouteMenuItemProvider : MenuItemProviderBase { protected override void ApplyLink(TreeNode node, MenuItemRecord entity) { - var routeName = string.Empty; // TODO: get from entity - var routeValues = new RouteValueDictionary(); // TODO: deserialize dict from entity - - if (routeName.HasValue()) - { - node.Value.Route(routeName, routeValues); - } - else - { - node.Value.Action(routeValues); - } + if (entity.Model.IsEmpty()) + { + return; + } + + try + { + var routeValues = JsonConvert.DeserializeObject(entity.Model); + var routeName = string.Empty; + + if (routeValues.TryGetValue("routename", out var val)) + { + routeName = val as string; + routeValues.Remove("routename"); + } + + if (routeName.HasValue()) + { + node.Value.Route(routeName, routeValues); + } + else + { + node.Value.Action(routeValues); + } + } + catch { } } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index a03423b984..9ef8edecaa 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -30,6 +30,7 @@ public class MenuController : AdminControllerBase private readonly IStoreMappingService _storeMappingService; private readonly IAclService _aclService; private readonly ICustomerService _customerService; + private readonly IMenuService _menuService; private readonly AdminAreaSettings _adminAreaSettings; public MenuController( @@ -39,6 +40,7 @@ public MenuController( IStoreMappingService storeMappingService, IAclService aclService, ICustomerService customerService, + IMenuService menuService, AdminAreaSettings adminAreaSettings) { _menuStorage = menuStorage; @@ -47,6 +49,7 @@ public MenuController( _storeMappingService = storeMappingService; _aclService = aclService; _customerService = customerService; + _menuService = menuService; _adminAreaSettings = adminAreaSettings; } @@ -237,12 +240,15 @@ public ActionResult DeleteConfirmed(int id) // Ajax. public ActionResult ItemList(int id) { + var menu = _menuStorage.GetMenuById(id); + var model = new MenuRecordModel { - Id = id, - ItemTree = GetItemList(id) + Id = id }; + model.ItemTree = GetItemTree(menu); + return PartialView(model); } @@ -401,7 +407,7 @@ public ActionResult DeleteItem(int id) if (Request.IsAjaxRequest()) { - var model = GetItemList(menuId); + var model = GetItemTree(_menuStorage.GetMenuById(menuId)); return PartialView("ItemList", model); } @@ -425,7 +431,7 @@ private void PrepareModel(MenuRecordModel model, MenuRecord entity, bool exclude if (entity != null) { - model.ItemTree = GetItemList(entity.Id); + model.ItemTree = GetItemTree(entity); } } @@ -450,9 +456,11 @@ private void UpdateLocales(MenuItemRecord entity, MenuItemRecordModel model) } } - private TreeNode GetItemList(int id) + private TreeNode GetItemTree(MenuRecord menu) { - return new TreeNode(new MenuItem { Text = "Root" }); + var root = _menuService.GetRootNode(menu?.SystemName); + + return root ?? new TreeNode(new MenuItem { Text = "Root" }); } #endregion From b58af6e9cba05e5bcb42bc2ba1b3a6fa73ed5b33 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 26 Mar 2019 18:48:28 +0100 Subject: [PATCH 050/460] Code typo --- .../Caching/MemoryCacheManager.cs | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index 89a2c707e6..f523714d7d 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -22,6 +22,8 @@ public partial class MemoryCacheManager : DisposableObject, ICacheManager private readonly MemoryCache _cache; private readonly ConcurrentDictionary _keyLocks = new ConcurrentDictionary(); + private string _currentScope; + public MemoryCacheManager() { _cache = new MemoryCache("SmartStore"); @@ -54,27 +56,40 @@ private bool TryGet(string key, out T value) public T Get(string key) { - T value; - TryGet(key, out value); + TryGet(key, out T value); return value; } public T Get(string key, Func acquirer, TimeSpan? duration = null) { - T value; - if (TryGet(key, out value)) + if (TryGet(key, out T value)) { return value; } lock (String.Intern(key)) { - // atomic operation must be outer locked + // Atomic operation must be outer locked if (!TryGet(key, out value)) { - value = acquirer(); + string parentScope = null; + + try + { + // Push scope + parentScope = _currentScope; + _currentScope = key; + + value = acquirer(); + } + finally + { + // Pop scope + _currentScope = parentScope; + } + Put(key, value, duration); return value; } @@ -85,9 +100,7 @@ public T Get(string key, Func acquirer, TimeSpan? duration = null) public async Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null) { - T value; - - if (TryGet(key, out value)) + if (TryGet(key, out T value)) { return value; } From 63c50662d73ec142f229f5c8e38364a62f10a19f Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 26 Mar 2019 19:16:45 +0100 Subject: [PATCH 051/460] Added acquirer delegate to ICacheManager.GetHashSet() to ensure thread-safe initialization of sets. --- .../SmartStore.Core/Caching/ICacheManager.cs | 3 ++- src/Libraries/SmartStore.Core/Caching/ISet.cs | 1 + .../Caching/MemoryCacheManager.cs | 17 ++++++++++++++--- .../SmartStore.Core/Caching/MemorySet.cs | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs index c28fc3d0ff..cfb1823429 100644 --- a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs @@ -44,8 +44,9 @@ public interface ICacheManager /// a new set is created and put to cache automatically /// /// The set cache item key + /// Optional acquirer callback that is invoked when requested set does not exist yet. /// Cached item value - ISet GetHashSet(string key); + ISet GetHashSet(string key, Func> acquirer = null); /// /// Adds a cache item with the specified key diff --git a/src/Libraries/SmartStore.Core/Caching/ISet.cs b/src/Libraries/SmartStore.Core/Caching/ISet.cs index 59decb5d52..d0bff72bd7 100644 --- a/src/Libraries/SmartStore.Core/Caching/ISet.cs +++ b/src/Libraries/SmartStore.Core/Caching/ISet.cs @@ -6,6 +6,7 @@ namespace SmartStore.Core.Caching public interface ISet : IEnumerable { bool Add(string item); + void AddRange(IEnumerable items); void Clear(); bool Contains(string item); bool Remove(string item); diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index f523714d7d..d4d72b7877 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -174,10 +174,21 @@ public void Clear() RemoveByPattern("*"); } - public virtual ISet GetHashSet(string key) + public virtual ISet GetHashSet(string key, Func> acquirer = null) { - var set = Get(key, () => new MemorySet(this)); - return set; + var result = Get(key, () => + { + var set = new MemorySet(this); + var items = acquirer?.Invoke(); + if (items != null) + { + set.AddRange(items); + } + + return set; + }); + + return result; } private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration) diff --git a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs index 51254daa26..48cc1c0fb6 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs @@ -16,6 +16,11 @@ public MemorySet(ICacheManager cache) _cache = cache; } + public void AddRange(IEnumerable items) + { + this.AddRange(items); + } + public bool Move(string destinationKey, string item) { if (this.Contains(item)) From 8b74b67d75a29f525058f54c8440342d4f6ec1b3 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 26 Mar 2019 19:19:44 +0100 Subject: [PATCH 052/460] IMenuStorage.MenuExists() should work against ICacheManager > ISet. --- .../SmartStore.Services/Cms/Menus/MenuStorage.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index f5c162b4ca..25a6b73e65 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -108,13 +108,12 @@ public virtual bool MenuExists(string systemName) return false; } - var data = _services.Cache.Get(MENU_SYSTEMNAME_CACHE_KEY, () => - { - var systemNames = _menuRepository.TableUntracked.Select(x => x.SystemName).ToList(); - return new HashSet(systemNames); - }); + var data = _services.Cache.GetHashSet(MENU_SYSTEMNAME_CACHE_KEY, () => + { + return _menuRepository.TableUntracked.Select(x => x.SystemName).ToArray(); + }); - return data.Contains(systemName); + return data.Contains(systemName); } #region Menu items From b083cbac9d281cac41332adcab6232f37db671b7 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 26 Mar 2019 20:31:24 +0100 Subject: [PATCH 053/460] Refactored cache eviction code for menu system names --- .../Cms/Menus/MenuStorage.cs | 67 +++++++++++++------ 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 25a6b73e65..72286808d5 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -2,6 +2,7 @@ using System.Data.Entity; using System.Linq; using SmartStore.Core; +using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Domain.Security; @@ -43,7 +44,11 @@ public virtual void InsertMenu(MenuRecord menu) _menuRepository.Insert(menu); - _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); + var systemNames = GetMenuSystemNames(false); + if (systemNames != null && menu.Published) + { + systemNames.Add(menu.SystemName); + } } public virtual void UpdateMenu(MenuRecord menu) @@ -52,24 +57,37 @@ public virtual void UpdateMenu(MenuRecord menu) var oldSystemName = menu.SystemName; + var modProps = _services.DbContext.GetModifiedProperties(menu); + _menuRepository.Update(menu); - if (oldSystemName != menu.SystemName) - { - _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); - } + var systemNames = GetMenuSystemNames(false); + if (systemNames != null) + { + if (modProps.TryGetValue(nameof(menu.Published), out var original) && original.Convert() == true) + { + systemNames.Remove(menu.SystemName); + } + else if (modProps.TryGetValue(nameof(menu.SystemName), out original)) + { + systemNames.Remove((string)original); + systemNames.Add(menu.SystemName); + } + } } public virtual void DeleteMenu(MenuRecord menu) { if (menu == null) - { return; - } _menuRepository.Delete(menu); - _services.Cache.Remove(MENU_SYSTEMNAME_CACHE_KEY); + var systemNames = GetMenuSystemNames(false); + if (systemNames != null) + { + systemNames.Remove(menu.SystemName); + } } public virtual IPagedList GetAllMenus( @@ -108,12 +126,7 @@ public virtual bool MenuExists(string systemName) return false; } - var data = _services.Cache.GetHashSet(MENU_SYSTEMNAME_CACHE_KEY, () => - { - return _menuRepository.TableUntracked.Select(x => x.SystemName).ToArray(); - }); - - return data.Contains(systemName); + return GetMenuSystemNames(true).Contains(systemName); } #region Menu items @@ -181,11 +194,27 @@ public virtual MenuItemRecord GetMenuItemById(int id) return _menuItemRepository.GetById(id); } - #endregion + #endregion - #region Utilities + #region Utilities - protected virtual IQueryable BuildMenuQuery(string systemName, int storeId, bool showHidden, bool withItems) + private ISet GetMenuSystemNames(bool create) + { + if (create || _services.Cache.Contains(MENU_SYSTEMNAME_CACHE_KEY)) + { + return _services.Cache.GetHashSet(MENU_SYSTEMNAME_CACHE_KEY, () => + { + return _menuRepository.TableUntracked + .Where(x => x.Published) + .Select(x => x.SystemName) + .ToArray(); + }); + } + + return null; + } + + protected virtual IQueryable BuildMenuQuery(string systemName, int storeId, bool showHidden, bool withItems) { var applied = false; var entityName = nameof(MenuRecord); @@ -245,6 +274,6 @@ orderby grp.Key return query; } - #endregion - } + #endregion + } } From 7afdd4eeeb6ff684023fdce8a0a0275120fd9855 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 27 Mar 2019 03:38:28 +0100 Subject: [PATCH 054/460] Cache item dependencies (parent entries get auto-evicted when any child entry changes) --- .../Caching/CacheAcquireContext.cs | 41 +++++++++ .../SmartStore.Core/Caching/ICacheManager.cs | 6 +- .../Caching/MemoryCacheManager.cs | 87 +++++++++---------- .../SmartStore.Core/Caching/NullCache.cs | 4 +- .../SmartStore.Core/SmartStore.Core.csproj | 1 + .../Catalog/CategoryService.cs | 10 +-- .../Catalog/ManufacturerService.cs | 8 +- .../Cms/Menus/MenuStorage.cs | 2 +- .../Menus/Providers/EntityMenuItemProvider.cs | 2 +- src/Presentation/SmartStore.Web/Global.asax | 2 +- 10 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 src/Libraries/SmartStore.Core/Caching/CacheAcquireContext.cs diff --git a/src/Libraries/SmartStore.Core/Caching/CacheAcquireContext.cs b/src/Libraries/SmartStore.Core/Caching/CacheAcquireContext.cs new file mode 100644 index 0000000000..f9422cf390 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Caching/CacheAcquireContext.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using SmartStore.Utilities; + +namespace SmartStore.Core.Caching +{ + public class CacheAcquireContext + { + private readonly CacheAcquireContext _parent; + private readonly HashSet _dependentKeys = new HashSet(); + + private CacheAcquireContext(string key, CacheAcquireContext parent) + { + Key = key; + _parent = parent; + + if (parent != null) + parent._dependentKeys.Add(key); + } + + public static CacheAcquireContext Current { get; set; } + + public static IDisposable Begin(string key) + { + Current = new CacheAcquireContext(key, Current); + var action = new ActionDisposable(() => + { + Current = Current?._parent; + }); + + return action; + } + + public string Key { get; set; } + + public IEnumerable DependentKeys + { + get { return _dependentKeys; } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs index cfb1823429..97cdeab2f4 100644 --- a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs @@ -54,7 +54,11 @@ public interface ICacheManager /// Key /// Value /// Absolute expiration time - void Put(string key, object value, TimeSpan? duration = null); + /// + /// Optional sequence of cache entry keys on which this entry is to depend. + /// If one of the dependent entries change, this entry will be removed from cache automatically. + /// + void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null); /// /// Gets a value indicating whether the value associated with the specified key is cached diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index d4d72b7877..e651472a26 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -56,74 +56,66 @@ private bool TryGet(string key, out T value) public T Get(string key) { - TryGet(key, out T value); - - return value; - } - - public T Get(string key, Func acquirer, TimeSpan? duration = null) - { - - if (TryGet(key, out T value)) + using (CacheAcquireContext.Begin(key)) { + TryGet(key, out T value); return value; } + } - lock (String.Intern(key)) + public T Get(string key, Func acquirer, TimeSpan? duration = null) + { + using (CacheAcquireContext.Begin(key)) { - // Atomic operation must be outer locked - if (!TryGet(key, out value)) + if (TryGet(key, out T value)) { - string parentScope = null; + return value; + } - try + lock (String.Intern(key)) + { + // Atomic operation must be outer locked + if (!TryGet(key, out value)) { - // Push scope - parentScope = _currentScope; - _currentScope = key; - value = acquirer(); + Put(key, value, duration, CacheAcquireContext.Current.DependentKeys); + return value; } - finally - { - // Pop scope - _currentScope = parentScope; - } - - Put(key, value, duration); - return value; } - } - return value; + return value; + } } public async Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null) { - if (TryGet(key, out T value)) + using (CacheAcquireContext.Begin(key)) { - return value; - } + if (TryGet(key, out T value)) + { + return value; + } - // get the async (semaphore) locker specific to this key - var keyLock = AsyncLock.Acquire(key); + // get the async (semaphore) locker specific to this key + var keyLock = AsyncLock.Acquire(key); - using (await keyLock.LockAsync()) - { - if (!TryGet(key, out value)) + using (await keyLock.LockAsync()) { - value = await acquirer().ConfigureAwait(false); - Put(key, value, duration); - return value; + if (!TryGet(key, out value)) + { + value = await acquirer().ConfigureAwait(false); + Put(key, value, duration, CacheAcquireContext.Current.DependentKeys); + return value; + } } - } - return value; + return value; + } } - public void Put(string key, object value, TimeSpan? duration = null) + public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) { - _cache.Set(key, value ?? FakeNull, GetCacheItemPolicy(duration)); + _cache.Set(key, value ?? FakeNull, GetCacheItemPolicy(duration, dependencies)); } public bool Contains(string key) @@ -191,7 +183,7 @@ public virtual ISet GetHashSet(string key, Func> acquirer = return result; } - private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration) + private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable dependencies) { var absoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration; @@ -199,13 +191,18 @@ private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration) { absoluteExpiration = DateTime.UtcNow + duration.Value; } - + var cacheItemPolicy = new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration, SlidingExpiration = ObjectCache.NoSlidingExpiration }; + if (dependencies != null && dependencies.Any()) + { + cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(dependencies)); + } + return cacheItemPolicy; } diff --git a/src/Libraries/SmartStore.Core/Caching/NullCache.cs b/src/Libraries/SmartStore.Core/Caching/NullCache.cs index ae8d19b285..12f9ead42f 100644 --- a/src/Libraries/SmartStore.Core/Caching/NullCache.cs +++ b/src/Libraries/SmartStore.Core/Caching/NullCache.cs @@ -47,12 +47,12 @@ public Task GetAsync(string key, Func> acquirer, TimeSpan? duratio } - public ISet GetHashSet(string key) + public ISet GetHashSet(string key, Func> acquirer = null) { return new MemorySet(this); } - public void Put(string key, object value, TimeSpan? duration = null) + public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) { } diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 0735e454d3..faefded4f1 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -158,6 +158,7 @@ + diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs index 884ee2b7d1..4a5d50042f 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs @@ -28,11 +28,11 @@ public partial class CategoryService : ICategoryService internal const string CATEGORY_TREE_KEY = "category:tree-{0}-{1}-{2}"; internal const string CATEGORY_TREE_PATTERN_KEY = "category:tree-*"; - private const string CATEGORIES_BY_PARENT_CATEGORY_ID_KEY = "category.byparent-{0}-{1}-{2}-{3}"; - private const string PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY = "productcategory.allbycategoryid-{0}-{1}-{2}-{3}-{4}-{5}"; - private const string PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY = "productcategory.allbyproductid-{0}-{1}-{2}-{3}"; - private const string CATEGORIES_PATTERN_KEY = "category.*"; - private const string PRODUCTCATEGORIES_PATTERN_KEY = "productcategory.*"; + private const string CATEGORIES_BY_PARENT_CATEGORY_ID_KEY = "category:byparent-{0}-{1}-{2}-{3}"; + private const string PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY = "productcategory:allbycategoryid-{0}-{1}-{2}-{3}-{4}-{5}"; + private const string PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY = "productcategory:allbyproductid-{0}-{1}-{2}-{3}"; + private const string CATEGORIES_PATTERN_KEY = "category:*"; + private const string PRODUCTCATEGORIES_PATTERN_KEY = "productcategory:*"; private readonly IRepository _categoryRepository; private readonly IRepository _productCategoryRepository; diff --git a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs index 8e0cfeb411..16c096b7c5 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs @@ -15,10 +15,10 @@ namespace SmartStore.Services.Catalog { public partial class ManufacturerService : IManufacturerService { - private const string PRODUCTMANUFACTURERS_ALLBYMANUFACTURERID_KEY = "SmartStore.productmanufacturer.allbymanufacturerid-{0}-{1}-{2}-{3}-{4}"; - private const string PRODUCTMANUFACTURERS_ALLBYPRODUCTID_KEY = "SmartStore.productmanufacturer.allbyproductid-{0}-{1}-{2}"; - private const string MANUFACTURERS_PATTERN_KEY = "SmartStore.manufacturer.*"; - private const string PRODUCTMANUFACTURERS_PATTERN_KEY = "SmartStore.productmanufacturer.*"; + private const string PRODUCTMANUFACTURERS_ALLBYMANUFACTURERID_KEY = "productmanufacturer:allbymanufacturerid-{0}-{1}-{2}-{3}-{4}"; + private const string PRODUCTMANUFACTURERS_ALLBYPRODUCTID_KEY = "productmanufacturer:allbyproductid-{0}-{1}-{2}"; + private const string MANUFACTURERS_PATTERN_KEY = "manufacturer:*"; + private const string PRODUCTMANUFACTURERS_PATTERN_KEY = "productmanufacturer:*"; private readonly IRepository _manufacturerRepository; private readonly IRepository _productManufacturerRepository; diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 72286808d5..e17984b3e7 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -12,7 +12,7 @@ namespace SmartStore.Services.Cms { public partial class MenuStorage : IMenuStorage { - private const string MENU_SYSTEMNAME_CACHE_KEY = "SmartStore.Menu.Storage.SystemNames"; + private const string MENU_SYSTEMNAME_CACHE_KEY = "MenuStorage:SystemNames"; private readonly IRepository _menuRepository; private readonly IRepository _menuItemRepository; diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index adafd90e03..d1744cbf3f 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -24,7 +24,7 @@ protected override void ApplyLink(TreeNode node, MenuItemRecord entity return; } - // TODO: to resolve for what roles, what language, what store? + // TBD: to resolve for what roles, what language, what store? var result = _linkResolver.Resolve(entity.Model); node.Id = string.Concat(result.Type.ToString().ToLower(), ".", result.Id); 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 6341ce995a37746236122d2d636d1570f846df3c Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 27 Mar 2019 16:24:06 +0100 Subject: [PATCH 055/460] Menu builder --- .../Cms/Menus/IMenuStorage.cs | 7 +- .../Cms/Menus/MenuStorage.cs | 33 +++- .../UI/Menus/DatabaseMenu.cs | 138 ++++++++++++---- .../UI/Menus/DatabaseMenuResolver.cs | 26 ++- .../Menus/Providers/EntityMenuItemProvider.cs | 5 +- .../Menus/Providers/MenuItemProviderBase.cs | 17 +- .../Controllers/MenuController.cs | 152 ++++++++++++++---- .../Models/Menus/MenuItemRecordModel.cs | 5 +- .../Administration/Views/Menu/EditItem.cshtml | 2 +- .../Administration/Views/Menu/ItemList.cshtml | 10 +- .../Views/Menu/_CreateOrUpdate.Item.cshtml | 3 +- 11 files changed, 300 insertions(+), 98 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs index aeee17ca4b..156e9e10ed 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs @@ -28,7 +28,7 @@ public partial interface IMenuStorage /// /// Menu system name. /// Store identifier. 0 to get menus for all stores. - /// Whether to get hidden menus. + /// Whether to include hidden menus. /// Whether to include menu items. /// Page index. /// Page size. @@ -36,7 +36,7 @@ public partial interface IMenuStorage IPagedList GetAllMenus( string systemName = null, int storeId = 0, - bool showHidden = false, + bool includeHidden = false, bool withItems = false, int pageIndex = 0, int pageSize = int.MaxValue); @@ -81,8 +81,9 @@ IPagedList GetAllMenus( /// Gets a menu item by identifier. /// /// Menu item identifier. + /// Whether to include menu and menu item entities. /// Menu item entity. - MenuItemRecord GetMenuItemById(int id); + MenuItemRecord GetMenuItemById(int id, bool withMenu = false); #endregion } diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index e17984b3e7..4f44c3ef4e 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -93,12 +93,12 @@ public virtual void DeleteMenu(MenuRecord menu) public virtual IPagedList GetAllMenus( string systemName = null, int storeId = 0, - bool showHidden = false, + bool includeHidden = false, bool withItems = false, int pageIndex = 0, int pageSize = int.MaxValue) { - var query = BuildMenuQuery(systemName, storeId, showHidden, withItems); + var query = BuildMenuQuery(systemName, storeId, includeHidden, withItems); return new PagedList(query, pageIndex, pageSize); } @@ -135,6 +135,12 @@ public virtual void InsertMenuItem(MenuItemRecord item) { Guard.NotNull(item, nameof(MenuRecord)); + // Prevent inconsistent tree structure. + if (item.ParentItemId != 0 && item.ParentItemId == item.Id) + { + item.ParentItemId = 0; + } + _menuItemRepository.Insert(item); } @@ -142,6 +148,12 @@ public virtual void UpdateMenuItem(MenuItemRecord item) { Guard.NotNull(item, nameof(MenuRecord)); + // Prevent inconsistent tree structure. + if (item.ParentItemId != 0 && item.ParentItemId == item.Id) + { + item.ParentItemId = 0; + } + _menuItemRepository.Update(item); } @@ -184,14 +196,21 @@ void GetChildIds(int parentId) } } - public virtual MenuItemRecord GetMenuItemById(int id) + public virtual MenuItemRecord GetMenuItemById(int id, bool withMenu = false) { if (id == 0) { return null; } - return _menuItemRepository.GetById(id); + var query = _menuItemRepository.Table; + + if (withMenu) + { + query = query.Include(x => x.Menu.Items); + } + + return query.FirstOrDefault(x => x.Id == id); } #endregion @@ -214,7 +233,7 @@ private ISet GetMenuSystemNames(bool create) return null; } - protected virtual IQueryable BuildMenuQuery(string systemName, int storeId, bool showHidden, bool withItems) + protected virtual IQueryable BuildMenuQuery(string systemName, int storeId, bool includeHidden, bool withItems) { var applied = false; var entityName = nameof(MenuRecord); @@ -225,7 +244,7 @@ protected virtual IQueryable BuildMenuQuery(string systemName, int s query = query.Include(x => x.Items); } - query = query.Where(x => showHidden || x.Published); + query = query.Where(x => includeHidden || x.Published); if (systemName.HasValue()) { @@ -245,7 +264,7 @@ from m in sm.DefaultIfEmpty() applied = true; } - if (!showHidden && !QuerySettings.IgnoreAcl) + if (!includeHidden && !QuerySettings.IgnoreAcl) { var allowedRoleIds = _services.WorkContext.CurrentCustomer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs index d50fbb43f2..60404079b6 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs @@ -1,39 +1,119 @@ using System; using System.Collections.Generic; using System.Linq; -using SmartStore.Core.Logging; -using SmartStore.Core.Domain.Cms; using SmartStore.Collections; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Cms; using SmartStore.Services; using SmartStore.Services.Cms; +using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.UI { - /// - /// A generic implementation of which represents a - /// entity retrieved by . - /// - internal class DatabaseMenu : IMenu - { - public string Name => throw new NotImplementedException(); - - public TreeNode Root => throw new NotImplementedException(); - - public bool ApplyPermissions => throw new NotImplementedException(); - - public void ClearCache() - { - throw new NotImplementedException(); - } - - public IDictionary> GetAllCachedMenus() - { - throw new NotImplementedException(); - } - - public void ResolveElementCounts(TreeNode curNode, bool deep = false) - { - throw new NotImplementedException(); - } - } + /// + /// A generic implementation of which represents a + /// entity retrieved by . + /// + internal class DatabaseMenu : MenuBase + { + private readonly IMenuStorage _menuStorage; + private readonly DbQuerySettings _querySettings; + private readonly IDictionary> _menuItemProviders; + + public DatabaseMenu( + string menuName, + ICommonServices services, + IMenuStorage menuStorage, + IMenuPublisher menuPublisher, + DbQuerySettings querySettings, + IEnumerable> menuItemProviders) + { + Guard.NotEmpty(menuName, nameof(menuName)); + + Name = menuName; + Services = services; + MenuPublisher = menuPublisher; + + _menuStorage = menuStorage; + _querySettings = querySettings; + _menuItemProviders = menuItemProviders.ToDictionarySafe(x => x.Metadata.SystemName, x => x); + } + + public override string Name { get; } + + public override bool ApplyPermissions => false; + + protected override TreeNode Build() + { + var storeId = _querySettings.IgnoreMultiStore ? 0 : Services.StoreContext.CurrentStore.Id; + var menu = _menuStorage.GetAllMenus(Name, storeId, false, true, 0, 1).FirstOrDefault(); + + if (menu == null) + { + return new TreeNode(new MenuItem { Text = Name }); + } + + var entities = menu.Items + .OrderBy(x => x.ParentItemId) + .ThenBy(x => x.DisplayOrder) + .ThenBy(x => x.Title); + + var parent = new TreeNode(new MenuItem { Text = menu.GetLocalized(x => x.Title) }); + MenuItemRecord prevItem = null; + + foreach (var entity in entities) + { + // Get parent. + if (prevItem != null) + { + if (entity.ParentItemId != parent.Value.EntityId) + { + if (entity.ParentItemId == prevItem.Id) + { + // Level +1. + parent = parent.LastChild; + } + else + { + // Level -x. + while (!parent.IsRoot) + { + if (parent.Value.EntityId == entity.ParentItemId) + { + break; + } + parent = parent.Parent; + } + } + } + } + + // Add to parent. + if (entity.SystemName.HasValue() && _menuItemProviders.TryGetValue(entity.SystemName, out var provider)) + { + var providerBase = provider.Value as MenuItemProviderBase; + providerBase.Append(parent, entity); + } + else + { + // TODO: Convert and append native items too (divider etc.)... + //menuItem.Attributes.Add("IsDivider", entity.IsDivider); + } + + prevItem = entity; + } + + return parent; + } + + protected override string GetCacheKey() + { + var cacheKey = "{0}-{1}-{2}".FormatInvariant( + Services.WorkContext.WorkingLanguage.Id, + _querySettings.IgnoreMultiStore ? 0 : Services.StoreContext.CurrentStore.Id, + _querySettings.IgnoreAcl ? "0" : Services.WorkContext.CurrentCustomer.GetRolesIdent()); + + return cacheKey; + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs index fb0a12c682..56c60c63f0 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs @@ -1,16 +1,34 @@ -using SmartStore.Services.Cms; +using System; +using System.Collections.Generic; +using SmartStore.Core.Data; +using SmartStore.Services; +using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI { public class DatabaseMenuResolver : IMenuResolver { + protected readonly ICommonServices _services; protected readonly IMenuStorage _menuStorage; + protected readonly IMenuPublisher _menuPublisher; + protected readonly IEnumerable> _menuItemProviders; - public DatabaseMenuResolver(IMenuStorage menuStorage) + public DatabaseMenuResolver( + ICommonServices services, + IMenuStorage menuStorage, + IMenuPublisher menuPublisher, + IEnumerable> menuItemProviders) { + _services = services; _menuStorage = menuStorage; + _menuPublisher = menuPublisher; + _menuItemProviders = menuItemProviders; + + QuerySettings = DbQuerySettings.Default; } + public DbQuerySettings QuerySettings { get; set; } + public bool Exists(string menuName) { return _menuStorage.MenuExists(menuName); @@ -18,8 +36,8 @@ public bool Exists(string menuName) public IMenu Resolve(string name) { - - return null; + var menu = new DatabaseMenu(name, _services, _menuStorage, _menuPublisher, QuerySettings, _menuItemProviders); + return menu; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index d1744cbf3f..9922231feb 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -24,11 +24,8 @@ protected override void ApplyLink(TreeNode node, MenuItemRecord entity return; } - // TBD: to resolve for what roles, what language, what store? + // Always resolve against current store, current customer and working language. var result = _linkResolver.Resolve(entity.Model); - - node.Id = string.Concat(result.Type.ToString().ToLower(), ".", result.Id); - node.Value.EntityId = result.Id; node.Value.Url = result.Link; // TBD: What about cache invalidation? We would also have two levels of caching: diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 12879368b9..106f8de616 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -1,5 +1,6 @@ using SmartStore.Collections; using SmartStore.Core.Domain.Cms; +using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.UI { @@ -22,22 +23,22 @@ public virtual void Append(TreeNode parent, MenuItemRecord entity) /// Menu item. protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) { + var shortDescription = entity.GetLocalized(x => x.ShortDescription); + var menuItem = new MenuItem { - Text = entity.Title, + EntityId = entity.Id, + Text = entity.GetLocalized(x => x.Title), Visible = entity.Published }; - // TODO: support divider elements. - //menuItem.Attributes.Add("IsDivider", entity.IsDivider); - if (entity.NoFollow) { menuItem.LinkHtmlAttributes.Add("rel", "nofollow"); } - if (entity.ShortDescription.HasValue()) + if (shortDescription.HasValue()) { - menuItem.LinkHtmlAttributes.Add("title", entity.ShortDescription); + menuItem.LinkHtmlAttributes.Add("title", shortDescription); } if (entity.NewWindow) { @@ -52,7 +53,9 @@ protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) menuItem.LinkHtmlAttributes.Add("class", entity.CssClass); } - return menuItem; + // TODO: entity.ShowExpanded + + return menuItem; } /// diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index 9ef8edecaa..7668dc56e5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -30,7 +30,6 @@ public class MenuController : AdminControllerBase private readonly IStoreMappingService _storeMappingService; private readonly IAclService _aclService; private readonly ICustomerService _customerService; - private readonly IMenuService _menuService; private readonly AdminAreaSettings _adminAreaSettings; public MenuController( @@ -40,7 +39,6 @@ public MenuController( IStoreMappingService storeMappingService, IAclService aclService, ICustomerService customerService, - IMenuService menuService, AdminAreaSettings adminAreaSettings) { _menuStorage = menuStorage; @@ -49,7 +47,6 @@ public MenuController( _storeMappingService = storeMappingService; _aclService = aclService; _customerService = customerService; - _menuService = menuService; _adminAreaSettings = adminAreaSettings; } @@ -114,7 +111,7 @@ public ActionResult Create() } var model = new MenuRecordModel(); - PrepareModel(model, null, false); + PrepareModel(model, null); AddLocales(_languageService, model.Locales); return View(model); @@ -144,7 +141,7 @@ public ActionResult Create(MenuRecordModel model, bool continueEditing, FormColl return continueEditing ? RedirectToAction("Edit", new { id = menu.Id }) : RedirectToAction("List"); } - PrepareModel(model, null, false); + PrepareModel(model, null); return View(model); } @@ -156,7 +153,7 @@ public ActionResult Edit(int id) return AccessDeniedView(); } - var menu = _menuStorage.GetMenuById(id); + var menu = _menuStorage.GetMenuById(id, true); if (menu == null) { return HttpNotFound(); @@ -164,7 +161,7 @@ public ActionResult Edit(int id) var model = MiniMapper.Map(menu); - PrepareModel(model, menu, false); + PrepareModel(model, menu); AddLocales(_languageService, model.Locales, (locale, languageId) => { @@ -182,7 +179,7 @@ public ActionResult Edit(MenuRecordModel model, bool continueEditing, FormCollec return AccessDeniedView(); } - var menu = _menuStorage.GetMenuById(model.Id); + var menu = _menuStorage.GetMenuById(model.Id, true); if (menu == null) { return HttpNotFound(); @@ -204,7 +201,7 @@ public ActionResult Edit(MenuRecordModel model, bool continueEditing, FormCollec return continueEditing ? RedirectToAction("Edit", menu.Id) : RedirectToAction("List"); } - PrepareModel(model, menu, true); + PrepareModel(model, menu); return View(model); } @@ -240,7 +237,12 @@ public ActionResult DeleteConfirmed(int id) // Ajax. public ActionResult ItemList(int id) { - var menu = _menuStorage.GetMenuById(id); + var menu = _menuStorage.GetMenuById(id, true); + if (menu == null) + { + NotifyError(T("Admin.Common.ResourceNotFound")); + return new EmptyResult(); + } var model = new MenuRecordModel { @@ -275,15 +277,6 @@ public ActionResult CreateItem(int menuId, int parentItemId, string btnId) PrepareModel(model, null); AddLocales(_languageService, model.Locales); - // Preset max display order to always insert item at the end. - var item = menu.Items.Where(x => x.ParentItemId == parentItemId) - .OrderByDescending(x => x.DisplayOrder) - .FirstOrDefault(); - if (item != null) - { - model.DisplayOrder = item.DisplayOrder + 1; - } - ViewBag.BtnId = btnId; return View(model); @@ -301,6 +294,7 @@ public ActionResult CreateItem(MenuItemRecordModel itemModel, string btnId, bool if (ModelState.IsValid) { var item = MiniMapper.Map(itemModel); + item.ParentItemId = itemModel.ParentItemId ?? 0; _menuStorage.InsertMenuItem(item); itemModel.Id = item.Id; @@ -313,10 +307,14 @@ public ActionResult CreateItem(MenuItemRecordModel itemModel, string btnId, bool if (continueEditing) { - PrepareModel(itemModel, item); + PrepareModel(itemModel, null); NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); } } + else + { + PrepareModel(itemModel, null); + } return View(itemModel); } @@ -328,13 +326,14 @@ public ActionResult EditItem(int id, string btnId) return AccessDeniedView(); } - var item = _menuStorage.GetMenuItemById(id); + var item = _menuStorage.GetMenuItemById(id, true); if (item == null) { return HttpNotFound(); } var model = MiniMapper.Map(item); + model.ParentItemId = item.ParentItemId == 0 ? (int?)null : item.ParentItemId; PrepareModel(model, item); @@ -358,7 +357,7 @@ public ActionResult EditItem(MenuItemRecordModel itemModel, string btnId, bool c return AccessDeniedView(); } - var item = _menuStorage.GetMenuItemById(itemModel.Id); + var item = _menuStorage.GetMenuItemById(itemModel.Id, continueEditing); if (item == null) { return HttpNotFound(); @@ -367,6 +366,7 @@ public ActionResult EditItem(MenuItemRecordModel itemModel, string btnId, bool c if (ModelState.IsValid) { MiniMapper.Map(itemModel, item); + item.ParentItemId = itemModel.ParentItemId ?? 0; _menuStorage.UpdateMenuItem(item); @@ -382,6 +382,10 @@ public ActionResult EditItem(MenuItemRecordModel itemModel, string btnId, bool c NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); } } + else + { + PrepareModel(itemModel, item); + } return View(itemModel); } @@ -407,8 +411,7 @@ public ActionResult DeleteItem(int id) if (Request.IsAjaxRequest()) { - var model = GetItemTree(_menuStorage.GetMenuById(menuId)); - return PartialView("ItemList", model); + return ItemList(menuId); } return RedirectToAction("Edit", new { id = menuId }); @@ -418,25 +421,60 @@ public ActionResult DeleteItem(int id) #region Utilities - private void PrepareModel(MenuRecordModel model, MenuRecord entity, bool excludeProperties) + private void PrepareModel(MenuRecordModel model, MenuRecord entity) { - if (!excludeProperties) + if (entity != null) { - model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(entity); - model.SelectedCustomerRoleIds = _aclService.GetCustomerRoleIdsWithAccessTo(entity); + if (ModelState.IsValid) + { + model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(entity); + model.SelectedCustomerRoleIds = _aclService.GetCustomerRoleIdsWithAccessTo(entity); + } + + model.ItemTree = GetItemTree(entity); } model.AvailableStores = Services.StoreService.GetAllStores().ToSelectListItems(model.SelectedStoreIds); model.AvailableCustomerRoles = _customerService.GetAllCustomerRoles(true).ToSelectListItems(model.SelectedCustomerRoleIds); - - if (entity != null) - { - model.ItemTree = GetItemTree(entity); - } } private void PrepareModel(MenuItemRecordModel model, MenuItemRecord entity) { + var menu = entity != null + ? entity.Menu + : _menuStorage.GetMenuById(model.MenuId, true); + + // Preset max display order to always insert item at the end. + if (entity == null) + { + var item = menu.Items.Where(x => x.ParentItemId == model.ParentItemId) + .OrderByDescending(x => x.DisplayOrder) + .FirstOrDefault(); + if (item != null) + { + model.DisplayOrder = item.DisplayOrder + 1; + } + } + + // List to select parent item. + var itemTree = GetItemTree(menu); + itemTree.Traverse(x => + { + if (entity != null && entity.Id == x.Value.EntityId) + { + // Element cannot be parent itself. + } + else + { + var path = string.Join(" » ", x.Trail.Skip(1).Select(y => y.Value.Text)); + model.AllItems.Add(new SelectListItem + { + Text = path, + Value = x.Value.EntityId.ToString(), + Selected = entity != null && entity.ParentItemId == x.Value.EntityId + }); + } + }); } private void UpdateLocales(MenuRecord entity, MenuRecordModel model) @@ -458,9 +496,53 @@ private void UpdateLocales(MenuItemRecord entity, MenuItemRecordModel model) private TreeNode GetItemTree(MenuRecord menu) { - var root = _menuService.GetRootNode(menu?.SystemName); + // We cannot use IMenuService because it always loads the tree for current store. + var entities = menu.Items + .OrderBy(x => x.ParentItemId) + .ThenBy(x => x.DisplayOrder) + .ThenBy(x => x.Title); + + var parent = new TreeNode(new MenuItem { Text = menu.GetLocalized(x => x.Title) }); + MenuItemRecord prevItem = null; + + foreach (var entity in entities) + { + // Get parent. + if (prevItem != null) + { + if (entity.ParentItemId != parent.Value.EntityId) + { + if (entity.ParentItemId == prevItem.Id) + { + // Level +1. + parent = parent.LastChild; + } + else + { + // Level -x. + while (!parent.IsRoot) + { + if (parent.Value.EntityId == entity.ParentItemId) + { + break; + } + parent = parent.Parent; + } + } + } + } + + parent.Append(new MenuItem + { + EntityId = entity.Id, + Text = entity.GetLocalized(x => x.Title), + Visible = entity.Published + }); + + prevItem = entity; + } - return root ?? new TreeNode(new MenuItem { Text = "Root" }); + return parent; } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs index c0eac8f205..c2792779df 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Web.Mvc; using FluentValidation; using FluentValidation.Attributes; using SmartStore.Core.Localization; @@ -14,13 +15,15 @@ public class MenuItemRecordModel : EntityModelBase, ILocalizedModel(); + AllItems = new List(); } public int MenuId { get; set; } public string Model { get; set; } [SmartResourceDisplayName("Admin.ContentManagement.Menus.Item.ParentItem")] - public int ParentItemId { get; set; } + public int? ParentItemId { get; set; } + public IList AllItems { get; set; } [SmartResourceDisplayName("Admin.ContentManagement.Menus.Item.Provider")] public string SystemName { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditItem.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditItem.cshtml index eaea103d87..81b6d30713 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditItem.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditItem.cshtml @@ -9,7 +9,7 @@ {
- @T("Admin.ContentManagement.EditMenuItem") + @T("Admin.ContentManagement.EditMenuItem") - @Model.Title
@@ -34,7 +34,7 @@ else
@foreach (var provider in Model.AllProviders) { - + @provider.Text } @@ -49,12 +49,14 @@ else
+ @if (item.Icon.HasValue()) + { +
+ +
+ } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.Item.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.Item.cshtml index 5e06e7802d..49edab2443 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.Item.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.Item.cshtml @@ -4,17 +4,16 @@ @Html.ValidationSummary(false) @Html.HiddenFor(model => model.Id) @Html.HiddenFor(model => model.MenuId) +@Html.HiddenFor(model => model.ProviderName) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditorTemplates/MenuItem.Route.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditorTemplates/MenuItem.Route.cshtml index 994637ca85..821035c25c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditorTemplates/MenuItem.Route.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/EditorTemplates/MenuItem.Route.cshtml @@ -6,8 +6,10 @@ var rnd = CommonHelper.GenerateRandomInteger(); var id = ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty); var routeNameFieldId = string.Concat("RouteName", rnd); - var rawValue = ViewData.Model != null ? Convert.ToString(ViewData.Model) : null; + var routeValuesFieldId = string.Concat("RouteValues", rnd); var routeName = string.Empty; + var routeValuesStr = string.Empty; + var rawValue = ViewData.Model != null ? Convert.ToString(ViewData.Model) : null; try { @@ -18,26 +20,34 @@ if (routeValues.TryGetValue("routename", out val)) { routeName = val as string; + routeValues.Remove("routename"); } + + routeValuesStr = JsonConvert.SerializeObject(routeValues); } } catch { } } From 9d00f2746acb309a886e626bfa26480b6b18d0f8 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 1 Apr 2019 19:30:55 +0200 Subject: [PATCH 078/460] Closes #1586 added TabbableModel to MessageTemplateModel --- .../Administration/Models/Messages/MessageTemplateModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Messages/MessageTemplateModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Messages/MessageTemplateModel.cs index aef4792494..90cffa4419 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Messages/MessageTemplateModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Messages/MessageTemplateModel.cs @@ -12,7 +12,7 @@ namespace SmartStore.Admin.Models.Messages { [Validator(typeof(MessageTemplateValidator))] - public class MessageTemplateModel : EntityModelBase, ILocalizedModel, IStoreSelector + public class MessageTemplateModel : TabbableModel, ILocalizedModel, IStoreSelector { public MessageTemplateModel() { From 1f1a776497ea25632380479b2130cc4b58992bf3 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 1 Apr 2019 19:37:15 +0200 Subject: [PATCH 079/460] Closes #1461 Admin Grid: find a way to overflow the filter dialog when grid has no data to display --- changelog.md | 1 + .../Scripts/smartstore.hacks.js | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index f3e24a4558..b07cd5e107 100644 --- a/changelog.md +++ b/changelog.md @@ -77,6 +77,7 @@ * #1571 Compare products only shows one specification attribute option * #1539 Allow signing in with both e-mail and username * Trusted Shops: Trustbadge won't be displayed in Popups & Iframes anymore +* #1461 Admin Grid: filter dialog will be displayed entirely even when grid has no data to display ### Bugfixes * In a multi-store environment, multiple topics with the same system name cannot be resolved reliably. diff --git a/src/Presentation/SmartStore.Web/Scripts/smartstore.hacks.js b/src/Presentation/SmartStore.Web/Scripts/smartstore.hacks.js index 3518c4e486..a69568d345 100644 --- a/src/Presentation/SmartStore.Web/Scripts/smartstore.hacks.js +++ b/src/Presentation/SmartStore.Web/Scripts/smartstore.hacks.js @@ -18,17 +18,24 @@ // correctly anymore. We have to reposition it. if ($.telerik && $.telerik.filtering) { var showFilter = $.telerik.filtering.implementation.showFilter; - $.telerik.filtering.implementation.showFilter = function (e) { - // The filter button - var btn = $(e.currentTarget); - // Call the original func - showFilter.apply(this, [e]); - // The filter dropdown - var filter = btn.data('filter'); - var grid = filter.parent(); - filter.css({ - left: (btn.offset().left - grid.offset().left) + "px" - }); + $.telerik.filtering.implementation.showFilter = function (e) { + // The filter button + var btn = $(e.currentTarget); + // Call the original func + showFilter.apply(this, [e]); + // The filter dropdown + var filter = btn.data('filter'); + var grid = filter.parent(); + filter.css({ + left: (btn.offset().left - grid.offset().left) + "px" + }); + + // wrap with new relative element to circumvent cropping of filter dialog + if (!grid.hasClass("wrapped")) { + grid.addClass("wrapped") + .css("position", "initial") + .wrap("
"); + } }; } } From 9b8900f7fc73a3bc715dede05eafa419e433cb52 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 1 Apr 2019 19:37:15 +0200 Subject: [PATCH 080/460] Closes #1461 Admin Grid: find a way to overflow the filter dialog when grid has no data to display --- .../Administration/Controllers/MessageTemplateController.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs index db5273490c..509b57af78 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs @@ -21,6 +21,7 @@ using SmartStore.Core.Email; using System.Threading.Tasks; using System.Web.Caching; +using SmartStore.Web.Framework.Modelling; namespace SmartStore.Admin.Controllers { @@ -197,7 +198,8 @@ private void PrepareLastModelTree(MessageTemplate template) [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] [FormValueRequired("save", "save-continue")] - public ActionResult Edit(MessageTemplateModel model, bool continueEditing) + [ValidateInput(false)] + public ActionResult Edit(MessageTemplateModel model, bool continueEditing, FormCollection form) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageMessageTemplates)) return AccessDeniedView(); @@ -222,6 +224,8 @@ public ActionResult Edit(MessageTemplateModel model, bool continueEditing) // locales UpdateLocales(messageTemplate, model); + Services.EventPublisher.Publish(new ModelBoundEvent(model, messageTemplate, form)); + NotifySuccess(_localizationService.GetResource("Admin.ContentManagement.MessageTemplates.Updated")); return continueEditing ? RedirectToAction("Edit", messageTemplate.Id) : RedirectToAction("List"); } From 4d70917537fc9ca4716876a327f0a5c9a7cda82b Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Mon, 1 Apr 2019 20:35:26 +0200 Subject: [PATCH 081/460] Reduce exception dump in console --- .../SmartStore.Core/Logging/log4net/Log4netLogger.cs | 12 +++++++----- src/Libraries/SmartStore.Core/WebHelper.cs | 7 ++----- .../SmartStore.Services/Cms/LinkResolver.cs | 12 ++++++++++-- .../SmartStore.Web.Framework/DependencyRegistrar.cs | 4 ++-- .../Extensions/HttpExtensions.cs | 4 ++-- .../SmartStore.Web.Framework/Theming/ThemeContext.cs | 2 +- .../SmartStore.Web.Framework/WebStoreContext.cs | 6 +++++- 7 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs index 1b043a2108..a08674ae43 100644 --- a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs +++ b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs @@ -75,10 +75,14 @@ public void Log(LogLevel level, Exception exception, string message, object[] ar protected internal void TryAddExtendedThreadInfo(LoggingEvent loggingEvent) { HttpRequest httpRequest = null; + bool httpRequestReady = HttpContext.Current?.Handler != null; try { - httpRequest = HttpContext.Current.Request; + if (httpRequestReady) + { + httpRequest = HttpContext.Current.Request; + } } catch { @@ -104,10 +108,9 @@ protected internal void TryAddExtendedThreadInfo(LoggingEvent loggingEvent) { var container = EngineContext.Current.ContainerManager; - IWorkContext workContext; // CustomerId - if (container.TryResolve(null, out workContext)) + if (container.TryResolve(null, out IWorkContext workContext)) { try { @@ -119,10 +122,9 @@ protected internal void TryAddExtendedThreadInfo(LoggingEvent loggingEvent) } } - IWebHelper webHelper; // Url & stuff - if (container.TryResolve(null, out webHelper)) + if (container.TryResolve(null, out IWebHelper webHelper)) { try { diff --git a/src/Libraries/SmartStore.Core/WebHelper.cs b/src/Libraries/SmartStore.Core/WebHelper.cs index 79ae6353fa..e5a0bf1ca0 100644 --- a/src/Libraries/SmartStore.Core/WebHelper.cs +++ b/src/Libraries/SmartStore.Core/WebHelper.cs @@ -182,12 +182,9 @@ public virtual string ServerVariables(string name) try { - if (_httpContext != null && _httpContext.Request != null) + if (_httpContext.Handler != null && _httpContext?.Request?.ServerVariables[name] != null) { - if (_httpContext.Request.ServerVariables[name] != null) - { - result = _httpContext.Request.ServerVariables[name]; - } + result = _httpContext.Request.ServerVariables[name]; } } catch diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index 2462363f2a..b1810ebd9d 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -228,8 +228,16 @@ protected virtual LinkResolverData Parse(string linkExpression) case LinkType.Category: case LinkType.Manufacturer: case LinkType.Topic: - var id = path.ToInt(); - result.Value = id != 0 ? (object)id : path; + if (int.TryParse(path, out var id)) + { + // Reduce thrown exceptions in console + result.Value = id; + } + else + { + result.Value = path; + } + result.QueryString = query; break; case LinkType.Url: diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 88871f5616..6a09ce8553 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -724,7 +724,7 @@ static HttpContextBase HttpContextBaseFactory(IComponentContext ctx) static bool IsRequestValid() { - if (HttpContext.Current == null) + if (HttpContext.Current?.Handler == null) return false; try @@ -732,7 +732,7 @@ static bool IsRequestValid() // The "Request" property throws at application startup on IIS integrated pipeline mode var req = HttpContext.Current.Request; } - catch (Exception) + catch { return false; } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs index f3bdab1bfd..63eecdab18 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs @@ -215,10 +215,10 @@ internal static void SetUserThemeChoiceInCookie(this HttpContextBase context, st internal static HttpCookie GetPreviewModeCookie(this HttpContextBase context, bool createIfMissing) { - if (context == null) + if (context?.Handler == null) return null; - var cookie = context.Request.Cookies.Get("sm.PreviewModeOverrides"); + var cookie = context.Request?.Cookies?.Get("sm.PreviewModeOverrides"); if (cookie == null && createIfMissing) { diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs index 5145c9ea89..ce5b268dbf 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs @@ -147,7 +147,7 @@ public string GetRequestTheme() { try { - return (string)_httpContext.Request.RequestContext.RouteData.DataTokens[OverriddenThemeNameKey]; + return (string)_httpContext.Request?.RequestContext?.RouteData?.DataTokens[OverriddenThemeNameKey]; } catch { diff --git a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs index b6e97e2b1d..00b28af2d8 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs @@ -52,7 +52,11 @@ public void SetRequestStore(int? storeId) { try { - var value = _httpContext.Request.RequestContext.RouteData?.DataTokens?.Get(OverriddenStoreIdKey); + // Reduce thrown exceptions in console + if (_httpContext.Handler == null) + return null; + + var value = _httpContext.Request?.RequestContext?.RouteData?.DataTokens?.Get(OverriddenStoreIdKey); if (value != null) { return (int)value; From b953119c952a7315eea7d332917225a5ea10979d Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 2 Apr 2019 01:12:10 +0200 Subject: [PATCH 082/460] New KeyedLock class for (a)sync locking of singleton code by any given key (instead of app-wide locking) --- .../SmartStore.Core/Async/AsyncLock.cs | 110 ------------------ .../SmartStore.Core/Caching/CacheScope.cs | 5 + .../Caching/MemoryCacheManager.cs | 16 ++- .../SmartStore.Core/SmartStore.Core.csproj | 2 +- .../Utilities/Threading/KeyedLock.cs | 109 +++++++++++++++++ .../Controllers/CommonController.cs | 3 +- .../Controllers/MediaController.cs | 25 ++-- 7 files changed, 134 insertions(+), 136 deletions(-) delete mode 100644 src/Libraries/SmartStore.Core/Async/AsyncLock.cs create mode 100644 src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs diff --git a/src/Libraries/SmartStore.Core/Async/AsyncLock.cs b/src/Libraries/SmartStore.Core/Async/AsyncLock.cs deleted file mode 100644 index b93da92afc..0000000000 --- a/src/Libraries/SmartStore.Core/Async/AsyncLock.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using SmartStore.Utilities; - -namespace SmartStore.Core.Async -{ - public sealed class AsyncLock : DisposableObject - { - private static readonly TimeSpan _noTimeout = TimeSpan.FromMilliseconds(-1); - private static readonly ConcurrentDictionary _keyLocks = new ConcurrentDictionary(); - - private readonly SemaphoreSlim _semaphore; - private readonly int _initialCount; - - public AsyncLock() - : this(1, int.MaxValue) - { - } - - public AsyncLock(int initialCount) - : this(initialCount, int.MaxValue) - { - } - - public AsyncLock(int initialCount, int maxCount) - { - _semaphore = new SemaphoreSlim(initialCount, maxCount); - _initialCount = initialCount; - } - - public static AsyncLock Acquire(object key) - { - Guard.NotNull(key, nameof(key)); - - var asyncLock = _keyLocks.GetOrAdd(key, x => new AsyncLock()); - - // Perf: once we have more than 100 lock objects in the storage, remove the idle ones. - if (_keyLocks.Count > 100) - { - // never remove the current one and the locked ones - var idles = _keyLocks.Where(kvp => kvp.Key != key && !kvp.Value.IsLockHeld).ToArray(); - foreach (var kvp in idles) - { - var value = kvp.Value; - // double check for held lock - if (!value.IsLockHeld) - { - _keyLocks.TryRemove(kvp.Key, out value); - } - } - } - - return asyncLock; - } - - public bool IsLockHeld - { - get { return _semaphore.CurrentCount < _initialCount; } - } - - public int Release() - { - return _semaphore.Release(); - } - - public int Release(int releaseCount) - { - return _semaphore.Release(releaseCount); - } - - public Task LockAsync() - { - return LockAsync(_noTimeout, CancellationToken.None); - } - - public Task LockAsync(TimeSpan timeout) - { - return LockAsync(timeout, CancellationToken.None); - } - - public Task LockAsync(CancellationToken cancellationToken) - { - return LockAsync(_noTimeout, cancellationToken); - } - - public Task LockAsync(TimeSpan timeout, CancellationToken cancellationToken) - { - var wait = _semaphore.WaitAsync(timeout, cancellationToken); - - return wait.IsCompleted - ? Task.FromResult(new ActionDisposable(() => _semaphore.Release()) as IDisposable) - : wait.ContinueWith((t, s) => new ActionDisposable(() => ((SemaphoreSlim)s).Release()) as IDisposable, - _semaphore, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - - protected override void OnDispose(bool disposing) - { - if (disposing) - _semaphore.Dispose(); - } - } -} diff --git a/src/Libraries/SmartStore.Core/Caching/CacheScope.cs b/src/Libraries/SmartStore.Core/Caching/CacheScope.cs index 6ba28fb2ce..b705ab10b6 100644 --- a/src/Libraries/SmartStore.Core/Caching/CacheScope.cs +++ b/src/Libraries/SmartStore.Core/Caching/CacheScope.cs @@ -17,6 +17,11 @@ public CacheScope(string key) public void AddDependency(string key) { + if (key == null) + { + return; + } + if (_dependencies == null) { _dependencies = new HashSet(); diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index d7231ecdf1..626cbf5d2f 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -4,12 +4,11 @@ using System.Threading.Tasks; using System.Collections.Concurrent; using System.Threading; -using SmartStore.Core.Async; -using System.Collections; using System.Collections.Generic; using SmartStore.Utilities; using System.Text.RegularExpressions; using System.Diagnostics; +using SmartStore.Utilities.Threading; namespace SmartStore.Core.Caching { @@ -44,7 +43,7 @@ private bool TryGet(string key, bool independent, out T value) if (obj != null) { // Make the parent scope's entry depend on this - if (!independent) + if (true /*!independent*/) { _scopeAccessor.PropagateKey(key); } @@ -73,8 +72,9 @@ public T Get(string key, Func acquirer, TimeSpan? duration = null, bool in { return value; } - - lock (String.Intern(key)) + + // Get the (semaphore) locker specific to this key + using (KeyedLock.Lock(key)) { // Atomic operation must be outer locked if (!TryGet(key, independent, out value)) @@ -99,9 +99,7 @@ public async Task GetAsync(string key, Func> acquirer, TimeSpan? d } // Get the async (semaphore) locker specific to this key - var keyLock = AsyncLock.Acquire(key); - - using (await keyLock.LockAsync()) + using (await KeyedLock.LockAsync(key)) { if (!TryGet(key, independent, out value)) { @@ -204,7 +202,7 @@ private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable true /* x != null*/))); } cacheItemPolicy.RemovedCallback = OnRemoveEntry; diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 2519398a3b..1cc3539562 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -154,7 +154,6 @@ Properties\AssemblyVersionInfo.cs - @@ -693,6 +692,7 @@ + diff --git a/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs new file mode 100644 index 0000000000..234f721413 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace SmartStore.Utilities.Threading +{ + public sealed class KeyedLock + { + private static readonly TimeSpan _noTimeout = TimeSpan.FromMilliseconds(-1); + private static readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); + + private int _waiterCount; + + private KeyedLock(object key) + { + Key = key; + Semaphore = new SemaphoreSlim(1, 1); + } + + private object Key { get; set; } + private SemaphoreSlim Semaphore { get; set; } + + public bool IsLockHeld + { + get { return _waiterCount > 1; } + } + + public int WaiterCount + { + get { return _waiterCount; } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void IncrementCount() => Interlocked.Increment(ref _waiterCount); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DecrementCount() => Interlocked.Decrement(ref _waiterCount); + + public static IDisposable Lock(object key) + { + var keyedLock = GetOrCreateLock(key); + keyedLock.Semaphore.Wait(); + return new Releaser(keyedLock); + } + + public static Task LockAsync(object key) + { + return LockAsync(key, _noTimeout, CancellationToken.None); + } + + public static Task LockAsync(object key, TimeSpan timeout) + { + return LockAsync(key, timeout, CancellationToken.None); + } + + public static Task LockAsync(object key, CancellationToken cancellationToken) + { + return LockAsync(key, _noTimeout, cancellationToken); + } + + public static Task LockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken) + { + var keyedLock = GetOrCreateLock(key); + var wait = keyedLock.Semaphore.WaitAsync(timeout, cancellationToken); + + return wait.IsCompleted + ? Task.FromResult(new Releaser(keyedLock) as IDisposable) + : wait.ContinueWith((t, s) => new Releaser((KeyedLock)s) as IDisposable, + keyedLock, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + private static KeyedLock GetOrCreateLock(object key) + { + Guard.NotNull(key, nameof(key)); + + var item = _locks.GetOrAdd(key, k => new KeyedLock(key)); + item.IncrementCount(); + return item; + } + + sealed class Releaser : IDisposable + { + private KeyedLock _item; + + public Releaser(KeyedLock item) + { + _item = item; + } + + public void Dispose() + { + _item.DecrementCount(); + if (_item.WaiterCount == 0) + { + // Remove from dict + _locks.TryRemove(_item.Key, out _); + } + + _item.Semaphore.Release(); + _item = null; + } + } + } +} diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index 603fedac78..c859c14f95 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -51,7 +50,7 @@ namespace SmartStore.Admin.Controllers { - [AdminAuthorize] + [AdminAuthorize] public class CommonController : AdminControllerBase { const string CHECKUPDATE_CACHEKEY_PREFIX = "admin:common:checkupdateresult"; diff --git a/src/Presentation/SmartStore.Web/Controllers/MediaController.cs b/src/Presentation/SmartStore.Web/Controllers/MediaController.cs index dec8ab499d..d8c11c2ff6 100644 --- a/src/Presentation/SmartStore.Web/Controllers/MediaController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/MediaController.cs @@ -1,24 +1,24 @@ using System; +using System.IO; +using System.Net; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using System.Web.SessionState; +using SmartStore.Core.Data; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Events; using SmartStore.Core.IO; -using SmartStore.Services.Media; -using SmartStore.Services.Common; using SmartStore.Core.Logging; +using SmartStore.Services.Common; +using SmartStore.Services.Media; +using SmartStore.Services.Seo; using SmartStore.Utilities; -using System.IO; -using SmartStore.Core.Async; -using SmartStore.Core.Events; -using SmartStore.Web.Framework.Security; -using SmartStore.Core.Data; +using SmartStore.Utilities.Threading; using SmartStore.Web.Framework.Filters; -using System.Net; -using SmartStore.Core.Domain.Seo; -using SmartStore.Services.Seo; +using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Controllers { @@ -315,11 +315,8 @@ private async Task HandleImage( { if (!cachedImage.Exists) { - // get the async (semaphore) locker specific to this key - var keyLock = AsyncLock.Acquire("lock" + cachedImage.Path); - // Lock concurrent requests to same resource - using (await keyLock.LockAsync()) + using (await KeyedLock.LockAsync("MediaController.HandleImage." + cachedImage.Path)) { _imageCache.RefreshInfo(cachedImage); From 371cf8ea120d7b5f6c9488220aa0edf23c4874fa Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 2 Apr 2019 12:11:06 +0200 Subject: [PATCH 083/460] Menu builder --- .../Menus/Providers/MenuItemProviderBase.cs | 9 +++++- .../Controllers/MenuController.cs | 3 ++ .../Controllers/SecurityController.cs | 22 +++++++++++-- .../Models/Menus/MenuItemRecordModel.cs | 4 ++- .../Administration/SmartStore.Admin.csproj | 1 + .../Administration/Views/Menu/ItemList.cshtml | 12 +++---- .../Views/Menu/_CreateOrUpdate.Item.cshtml | 18 +++++------ .../EditorTemplates/AccessPermissions.cshtml | 32 +++++++++++++++++++ 8 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/Shared/EditorTemplates/AccessPermissions.cshtml diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 2c4f196bb2..32e32f09d5 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -6,7 +6,9 @@ namespace SmartStore.Web.Framework.UI { public abstract class MenuItemProviderBase : IMenuItemProvider { - public virtual void Append(MenuItemProviderRequest request) + public IIconExplorer IconExplorer { get; set; } + + public virtual void Append(MenuItemProviderRequest request) { Guard.NotNull(request, nameof(request)); Guard.NotNull(request.Parent, nameof(request.Parent)); @@ -66,6 +68,11 @@ protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) menuItem.LinkHtmlAttributes.Add("class", entity.CssClass); } + if (entity.Icon.HasValue()) + { + menuItem.Icon = IconExplorer.GetIconByName(entity.Icon).GetCssClass(entity.Style); + } + // For future use: entity.ShowExpanded return menuItem; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index 596081e4a2..b8e58adf5f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -286,6 +286,7 @@ public ActionResult CreateItem(MenuItemRecordModel itemModel, bool continueEditi { var item = MiniMapper.Map(itemModel); item.ParentItemId = itemModel.ParentItemId ?? 0; + item.PermissionNames = string.Join(",", itemModel.PermissionNames ?? new string[0]).NullEmpty(); _menuStorage.InsertMenuItem(item); @@ -320,6 +321,7 @@ public ActionResult EditItem(int id) var model = MiniMapper.Map(item); model.ParentItemId = item.ParentItemId == 0 ? (int?)null : item.ParentItemId; + model.PermissionNames = item.PermissionNames.SplitSafe(","); PrepareModel(model, item); AddLocales(_languageService, model.Locales, (locale, languageId) => @@ -350,6 +352,7 @@ public ActionResult EditItem(MenuItemRecordModel itemModel, bool continueEditing { MiniMapper.Map(itemModel, item); item.ParentItemId = itemModel.ParentItemId ?? 0; + item.PermissionNames = string.Join(",", itemModel.PermissionNames ?? new string[0]).NullEmpty(); _menuStorage.UpdateMenuItem(item); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs index 097cfd497b..8beda6fa82 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs @@ -12,7 +12,7 @@ namespace SmartStore.Admin.Controllers { - [AdminAuthorize] + [AdminAuthorize] public class SecurityController : AdminControllerBase { #region Fields @@ -34,10 +34,28 @@ public SecurityController(IWorkContext workContext, this._customerService = customerService; } - #endregion  + #endregion #region Methods + // Ajax. + public ActionResult AllAccessPermissions(string selected) + { + var permissions = _permissionService.GetAllPermissionRecords(); + var selectedArr = selected.SplitSafe(","); + + var data = permissions + .Select(x => new + { + id = x.SystemName, + text = x.Name, // TODO: localization. + selected = selectedArr.Contains(x.SystemName) + }) + .ToList(); + + return new JsonResult { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; + } + public ActionResult AccessDenied(string pageUrl) { var currentCustomer = _workContext.CurrentCustomer; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs index f808684528..9be7b719a2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using FluentValidation; using FluentValidation.Attributes; @@ -30,7 +31,8 @@ public class MenuItemRecordModel : EntityModelBase, IIcon, ILocalizedModel + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml index bab00b82cd..9dbe2596a5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml @@ -49,13 +49,11 @@ else - @if (item.Icon.HasValue()) - { -
- -
- }
+
+
+ @Html.SmartLabelFor(model => model.PermissionNames) +
+
+ @Html.EditorFor(model => model.PermissionNames) + @Html.ValidationMessageFor(model => model.PermissionNames) +
+
@Html.SmartLabelFor(model => model.DisplayOrder) @@ -104,15 +113,6 @@ @Html.ValidationMessageFor(model => model.BeginGroup)
-
-
- @Html.SmartLabelFor(model => model.PermissionNames) -
-
- @Html.EditorFor(model => model.PermissionNames) - @Html.ValidationMessageFor(model => model.PermissionNames) -
-
@Html.SmartLabelFor(model => model.Icon) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shared/EditorTemplates/AccessPermissions.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shared/EditorTemplates/AccessPermissions.cshtml new file mode 100644 index 0000000000..2aa5b1eb8b --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shared/EditorTemplates/AccessPermissions.cshtml @@ -0,0 +1,32 @@ +@{ + var id = ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty); + var name = ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty); + var value = ViewData.Model != null ? string.Join(",", ViewData.Model) : null; + var url = Url.Action("AllAccessPermissions", "Security", new { selected = value }); +} + + + \ No newline at end of file From 6373bb4dc126d999ef2ad9f7cb794f14ae44231b Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 2 Apr 2019 21:27:13 +0200 Subject: [PATCH 084/460] Link builder: better query string handling --- .../SmartStore.Services/Cms/LinkResolver.cs | 3 +- .../Administration/Views/Menu/ItemList.cshtml | 2 +- .../Views/Menu/_CreateOrUpdate.cshtml | 4 +- .../Scripts/smartstore.linkbuilder.js | 62 +++++++++---------- .../Views/Shared/EditorTemplates/Link.cshtml | 29 ++++----- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index b1810ebd9d..a2ea254799 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -79,6 +79,7 @@ public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable +
@T("Admin.ContentManagement.Menus.NoMenuItemsAvailable")
} diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml index 7cc38395c1..a794f749c9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/_CreateOrUpdate.cshtml @@ -45,7 +45,7 @@
@*IMPORTANT: Do not delete, this hidden element contains the id to assign localized values to the corresponding language *@ - @Html.HiddenFor(model => model.Locales[item].LanguageId, new { id = Html.FieldIdFor(model => model.Locales[item].LanguageId) + "_1" }) + @Html.HiddenFor(model => model.Locales[item].LanguageId) @Html.EditorFor(model => model.Locales[item].Title) @Html.ValidationMessageFor(model => model.Locales[item].Title) @@ -71,7 +71,7 @@ } - - @* 3. append additional controls. *@ @if (allowedTypes.Contains(LinkType.Product)) { @@ -145,11 +130,23 @@
} - @* 4. append reset button. *@ + @* 4. append additional buttons. *@
+ @if (!hideQueryString) + { + + + }
From 1da5778478229407b902cc874bfedaa0599df6f7 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 3 Apr 2019 01:04:45 +0200 Subject: [PATCH 085/460] Locking & other concurrency stuff --- .../Caching/CacheScopeAccessor.cs | 9 +-- .../Caching/MemoryCacheManager.cs | 68 +++++++++++-------- .../Extensions/EnumerableExtensions.cs | 34 +++++++++- .../Utilities/Threading/KeyedLock.cs | 7 +- .../SmartStore.Data/Caching/EfDbCache.cs | 7 +- .../Catalog/CategoryTreeChangeHandler.cs | 2 +- .../DependencyRegistrar.cs | 2 +- .../Theming/Assets/AssetTranslator.cs | 3 +- .../Controllers/HomeController.cs | 9 +-- .../Themes/Flex/Content/_menu.scss | 10 +-- 10 files changed, 99 insertions(+), 52 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs b/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs index a49eabf666..ca91788e81 100644 --- a/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs +++ b/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs @@ -1,12 +1,12 @@ using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using SmartStore.Utilities; namespace SmartStore.Core.Caching { public class CacheScopeAccessor : ICacheScopeAccessor { - private readonly ConcurrentStack _stack = new ConcurrentStack(); + private readonly Stack _stack = new Stack(); public CacheScopeAccessor() { @@ -16,8 +16,8 @@ public CacheScope Current { get { - _stack.TryPeek(out var current); - return current; + _stack.TryPeek(out var result); + return result; } } @@ -43,6 +43,7 @@ public IDisposable BeginScope(string key, bool independent = false) var action = new ActionDisposable(() => { _stack.TryPop(out _); + if (!independent) { PropagateKey(key); diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index 626cbf5d2f..9d36624788 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -9,6 +9,7 @@ using System.Text.RegularExpressions; using System.Diagnostics; using SmartStore.Utilities.Threading; +using SmartStore.Core.Infrastructure.DependencyManagement; namespace SmartStore.Core.Caching { @@ -19,14 +20,19 @@ public partial class MemoryCacheManager : DisposableObject, ICacheManager // which is bad if we intentionally wanted to save NULL values. public const string FakeNull = "__[NULL]__"; - private readonly ICacheScopeAccessor _scopeAccessor; - private readonly MemoryCache _cache; + private readonly Work _scopeAccessor; + private MemoryCache _cache; private readonly ConcurrentDictionary _keyLocks = new ConcurrentDictionary(); - public MemoryCacheManager(ICacheScopeAccessor scopeAccessor) + public MemoryCacheManager(Work scopeAccessor) { _scopeAccessor = scopeAccessor; - _cache = new MemoryCache("SmartStore"); + _cache = CreateCache(); + } + + private MemoryCache CreateCache() + { + return new MemoryCache("SmartStore"); } public bool IsDistributedCache @@ -43,9 +49,9 @@ private bool TryGet(string key, bool independent, out T value) if (obj != null) { // Make the parent scope's entry depend on this - if (true /*!independent*/) + if (!independent) { - _scopeAccessor.PropagateKey(key); + _scopeAccessor.Value.PropagateKey(key); } if (obj.Equals(FakeNull)) @@ -79,10 +85,10 @@ public T Get(string key, Func acquirer, TimeSpan? duration = null, bool in // Atomic operation must be outer locked if (!TryGet(key, independent, out value)) { - using (_scopeAccessor.BeginScope(key)) + using (_scopeAccessor.Value.BeginScope(key)) { value = acquirer(); - Put(key, value, duration, _scopeAccessor.Current.Dependencies); + Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); return value; } } @@ -103,10 +109,10 @@ public async Task GetAsync(string key, Func> acquirer, TimeSpan? d { if (!TryGet(key, independent, out value)) { - using (_scopeAccessor.BeginScope(key)) + using (_scopeAccessor.Value.BeginScope(key)) { value = await acquirer().ConfigureAwait(false); - Put(key, value, duration, _scopeAccessor.Current.Dependencies); + Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); return value; } } @@ -142,31 +148,34 @@ public IEnumerable Keys(string pattern) } var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase); - return keys.Where(x => wildcard.IsMatch(x)); + return keys.Where(x => wildcard.IsMatch(x)).ToArray(); } public int RemoveByPattern(string pattern) { - var keysToRemove = Keys(pattern); - int count = 0; - lock (_cache) { + var keysToRemove = Keys(pattern); + int count = 0; + // lock atomic operation foreach (string key in keysToRemove) { _cache.Remove(key); count++; } - } - return count; + return count; + } } public void Clear() { - RemoveByPattern("*"); - } + // Faster way of clearing cache: https://stackoverflow.com/questions/8043381/how-do-i-clear-a-system-runtime-caching-memorycache + var oldCache = Interlocked.Exchange(ref _cache, CreateCache()); + oldCache.Dispose(); + GC.Collect(); + } public virtual ISet GetHashSet(string key, Func> acquirer = null) { @@ -202,21 +211,26 @@ private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable true /* x != null*/))); + // INFO: we can only depend on existing items, otherwise this entry will be removed immediately. + dependencies = dependencies.Where(x => x != null && _cache.Contains(x)); + if (dependencies.Any()) + { + cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(dependencies)); + } } - cacheItemPolicy.RemovedCallback = OnRemoveEntry; + //cacheItemPolicy.RemovedCallback = OnRemoveEntry; return cacheItemPolicy; } - private void OnRemoveEntry(CacheEntryRemovedArguments args) - { - if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged) - { - Debug.WriteLine("MEMCACHE: remove depending entry '{0}'.".FormatInvariant(args.CacheItem.Key)); - } - } + //private void OnRemoveEntry(CacheEntryRemovedArguments args) + //{ + // if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged) + // { + // Debug.WriteLine("MEMCACHE: remove depending entry '{0}'.".FormatInvariant(args.CacheItem.Key)); + // } + //} protected override void OnDispose(bool disposing) { diff --git a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs index a711c68aa7..cb529dcace 100644 --- a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs @@ -423,6 +423,36 @@ public static int Remove(this IList list, Func selector) return count; } - #endregion - } + #endregion + + #region Stack + + public static bool TryPeek(this Stack stack, out T value) + { + value = default(T); + + if (stack.Count > 0) + { + value = stack.Peek(); + return true; + } + + return false; + } + + public static bool TryPop(this Stack stack, out T value) + { + value = default(T); + + if (stack.Count > 0) + { + value = stack.Pop(); + return true; + } + + return false; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs index 234f721413..4110165632 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs @@ -39,9 +39,14 @@ public int WaiterCount public void DecrementCount() => Interlocked.Decrement(ref _waiterCount); public static IDisposable Lock(object key) + { + return Lock(key, _noTimeout); + } + + public static IDisposable Lock(object key, TimeSpan timeout) { var keyedLock = GetOrCreateLock(key); - keyedLock.Semaphore.Wait(); + keyedLock.Semaphore.Wait(timeout); return new Releaser(keyedLock); } diff --git a/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs b/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs index b59caee693..67d4561746 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs @@ -8,6 +8,7 @@ using SmartStore.Core.Domain.Messages; using SmartStore.Core.Domain.Tasks; using SmartStore.Core.Infrastructure.DependencyManagement; +using SmartStore.Utilities.Threading; namespace SmartStore.Data.Caching { @@ -198,7 +199,7 @@ public virtual bool TryGet(string key, out object value) { if (entry.HasExpired(now)) { - lock (String.Intern(key)) + using (KeyedLock.Lock(key)) { InvalidateItemUnlocked(entry); } @@ -222,7 +223,7 @@ public virtual DbCacheEntry Put(string key, object value, IEnumerable de key = HashKey(key); - lock (String.Intern(key)) + using (KeyedLock.Lock(key)) { var entitySets = dependentEntitySets.Distinct().ToArray(); var entry = new DbCacheEntry @@ -290,7 +291,7 @@ public virtual void InvalidateItem(string key) Guard.NotEmpty(key, nameof(key)); - lock (String.Intern(key)) + using (KeyedLock.Lock(key)) { InvalidateItemUnlocked(key); } diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs index ebbe3e7632..ec5a83a21b 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs @@ -104,7 +104,7 @@ public void OnBeforeSave(IHookedEntity entry) if (modProps.Keys.Any(x => _h.Contains(x))) { - // Hierarchy affecting properties has changed. Nuke every tree. + // Hierarchy affecting properties has changed. Nuke each tree. cache.RemoveByPattern(CategoryService.CATEGORY_TREE_PATTERN_KEY); PublishEvent(CategoryTreeChangeReason.Hierarchy); _invalidated = true; diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 6a09ce8553..12c5d7cf55 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -554,7 +554,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerRequest(); // Model/Business cache (application scoped) - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().Named("null").SingleInstance(); diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs index 1e3e3fbd6c..62bf5fd53e 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs @@ -7,6 +7,7 @@ using BundleTransformer.SassAndScss.Translators; using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; +using SmartStore.Utilities.Threading; namespace SmartStore.Web.Framework.Theming.Assets { @@ -71,7 +72,7 @@ private IAsset TranslateInternal(IAsset asset) if (validationMode || !TryGetCachedAsset(asset, out result)) { - lock (String.Intern("CachedAsset:" + asset.VirtualPath)) + using (KeyedLock.Lock("CachedAsset:" + asset.VirtualPath)) { if (validationMode || !TryGetCachedAsset(asset, out result)) { diff --git a/src/Presentation/SmartStore.Web/Controllers/HomeController.cs b/src/Presentation/SmartStore.Web/Controllers/HomeController.cs index e1f639975e..585aaf0da2 100644 --- a/src/Presentation/SmartStore.Web/Controllers/HomeController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/HomeController.cs @@ -1,23 +1,18 @@ using System; -using System.Collections.Generic; -using System.Net; -using System.Text; using System.Web.Mvc; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Seo; using SmartStore.Core.Email; using SmartStore.Services.Catalog; using SmartStore.Services.Customers; using SmartStore.Services.Localization; using SmartStore.Services.Messages; using SmartStore.Services.Search; -using SmartStore.Services.Seo; using SmartStore.Services.Topics; using SmartStore.Web.Framework.Controllers; +using SmartStore.Web.Framework.Filters; using SmartStore.Web.Framework.Security; using SmartStore.Web.Models.Common; -using SmartStore.Web.Framework.Filters; namespace SmartStore.Web.Controllers { @@ -73,7 +68,7 @@ public ActionResult StoreClosed() [GdprConsent] public ActionResult ContactUs() { - var topic = _topicService.Value.GetTopicBySystemName("ContactUs", 0, false); + var topic = _topicService.Value.GetTopicBySystemName("ContactUs", 0, false); var model = new ContactUsModel { diff --git a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss index 0e759b9f13..a26b0e2eef 100644 --- a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss +++ b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss @@ -114,12 +114,12 @@ $ocm-selected-color: $warning; right: 1rem; top: 50%; transform: translateY(-50%); - @include fontawesome('\f105'); + @include fontawesome('\f054'); color: #646464; - font-size: 20px; - width: 20px; - height: 20px; - min-height: 20px; + font-size: 16px; + width: 16px; + height: 16px; + min-height: 16px; text-align: center; } From afd77a833ab388ad49e4930a7fc682d20e297d42 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 3 Apr 2019 14:15:38 +0200 Subject: [PATCH 086/460] Menu builder Fixes System.NullReferenceException in AsyncHelpers.Run --- .../Caching/MemoryCacheManager.cs | 2 +- .../DependencyRegistrar.cs | 7 +-- .../SmartStore.Web.Framework.csproj | 2 +- .../UI/Components/Menu/MenuExtensions.cs | 8 ++-- .../UI/Menus/DatabaseMenu.cs | 4 +- .../UI/Menus/DatabaseMenuResolver.cs | 4 +- .../Providers/CatalogMenuItemProvider.cs | 44 +++++++++++-------- .../Menus/Providers/EntityMenuItemProvider.cs | 3 +- .../UI/Menus/Providers/IMenuItemProvider.cs | 9 ++++ .../UI/Menus/Providers/MenuItemMetadata.cs | 7 --- .../Menus/Providers/MenuItemProviderBase.cs | 27 +++++++----- .../Providers/MenuItemProviderMetadata.cs | 40 +++++++++++++++++ .../Menus/Providers/RouteMenuItemProvider.cs | 3 +- .../Controllers/MenuController.cs | 21 +++++++-- .../Models/Menus/MenuItemRecordModel.cs | 1 + .../Views/Menu/CreateItem.cshtml | 2 +- .../Administration/Views/Menu/EditItem.cshtml | 2 +- .../EditorTemplates/MenuItem.Route.cshtml | 8 +++- .../Views/Menu/_CreateOrUpdate.Item.cshtml | 10 ++--- 19 files changed, 138 insertions(+), 66 deletions(-) delete mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderMetadata.cs diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index 9d36624788..37f9bc939d 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -112,7 +112,7 @@ public async Task GetAsync(string key, Func> acquirer, TimeSpan? d using (_scopeAccessor.Value.BeginScope(key)) { value = await acquirer().ConfigureAwait(false); - Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); + Put(key, value, duration, _scopeAccessor.Value.Current?.Dependencies); return value; } } diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 12c5d7cf55..9eed17a52a 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -862,11 +862,12 @@ protected override void Load(ContainerBuilder builder) var menuItemProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); foreach (var type in menuItemProviderTypes) { - var providerName = type.GetAttribute(false)?.Name.EmptyNull(); + var attribute = type.GetAttribute(false); var registration = builder.RegisterType(type).As().PropertiesAutowired(PropertyWiringOptions.None).InstancePerRequest(); - registration.WithMetadata(m => + registration.WithMetadata(m => { - m.For(em => em.ProviderName, providerName); + m.For(em => em.ProviderName, attribute.ProviderName); + m.For(em => em.AppendsMultipleItems, attribute.AppendsMultipleItems); }); } } diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 4141044b5f..c9cf056f8b 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -377,7 +377,7 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs index 4f1cc01525..e69643f095 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs @@ -80,9 +80,9 @@ public static NodePathState GetNodePathState(this TreeNode node, IList /// Whether to include menu items without existing parent menu item. /// Tree of menu items. public static TreeNode GetTree( - this IList items, + this IEnumerable items, string origin, - IDictionary> itemProviders, + IDictionary> itemProviders, bool includeItemsWithoutExistingParent = false) { Guard.NotNull(items, nameof(items)); @@ -143,13 +143,13 @@ public static TreeNode GetTree( return root; } - private static IList SortForTree(this IList items, bool includeItemsWithoutExistingParent) + private static IList SortForTree(this IEnumerable items, bool includeItemsWithoutExistingParent) { var result = new List(); SortChildItems(0); - if (includeItemsWithoutExistingParent && result.Count != items.Count) + if (includeItemsWithoutExistingParent && result.Count != items.Count()) { foreach (var item in items) { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs index 8113012499..e3643e2252 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs @@ -16,7 +16,7 @@ internal class DatabaseMenu : MenuBase { private readonly IMenuStorage _menuStorage; private readonly DbQuerySettings _querySettings; - private readonly IDictionary> _menuItemProviders; + private readonly IDictionary> _menuItemProviders; public DatabaseMenu( string menuName, @@ -24,7 +24,7 @@ public DatabaseMenu( IMenuStorage menuStorage, IMenuPublisher menuPublisher, DbQuerySettings querySettings, - IEnumerable> menuItemProviders) + IEnumerable> menuItemProviders) { Guard.NotEmpty(menuName, nameof(menuName)); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs index 425baf59b6..8cfe1c0cf7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs @@ -11,13 +11,13 @@ public class DatabaseMenuResolver : IMenuResolver protected readonly ICommonServices _services; protected readonly IMenuStorage _menuStorage; protected readonly IMenuPublisher _menuPublisher; - protected readonly IEnumerable> _menuItemProviders; + protected readonly IEnumerable> _menuItemProviders; public DatabaseMenuResolver( ICommonServices services, IMenuStorage menuStorage, IMenuPublisher menuPublisher, - IEnumerable> menuItemProviders) + IEnumerable> menuItemProviders) { _services = services; _menuStorage = menuStorage; diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs index d08c4f1245..b53f9cd7ac 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs @@ -2,7 +2,7 @@ using System.Linq; using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; -using SmartStore.Core.Plugins; +using SmartStore.Core.Domain.Cms; using SmartStore.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Localization; @@ -11,7 +11,7 @@ namespace SmartStore.Web.Framework.UI { - [SystemName("catalog")] + [MenuItemProvider("catalog", AppendsMultipleItems = true)] public class CatalogMenuItemProvider : MenuItemProviderBase { private readonly ICommonServices _services; @@ -36,6 +36,9 @@ public override void Append(MenuItemProviderRequest request) } else { + // INFO: Replaces CatalogSiteMap to a large extent and appends + // all catalog nodes (without root) to the passed parent node. + var tree = _categoryService.GetCategoryTree(0, false, _services.StoreContext.CurrentStore.Id); var allPictureIds = tree.Flatten().Select(x => x.PictureId.GetValueOrDefault()); var allPictureInfos = _pictureService.GetPictureInfos(allPictureIds); @@ -52,25 +55,14 @@ public override void Append(MenuItemProviderRequest request) // Do not append the root itself. foreach (var child in tree.Children) { - var node = ConvertNode(child, allPictureInfos); + var node = ConvertNode(child, request.Entity, allPictureInfos); request.Parent.Append(node); } } - // INFO: Replaces CatalogSiteMap to a large extent and appends - // all catalog nodes (without root) to the passed parent node. - // TBD: Cache invalidation workflow changes, because the catalog tree // is now contained within other menus. Invalidating the tree now means: // invalidate all containing menus also. - - // TBD: A MenuItemRecord with this provider assigned to it cannot have child nodes. - // We must prevent this in the UI somehow. - - // TBD (if this provider is assigned to MenuItemRecord): - // Some props are void: Title, ShortDescription, CssClass, HtmlId etc. These need to be hidden in the backend. - // Some props are inheritable: NoFollow, NewWindow etc. - // We need a mechanism to control those restrictions. } protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) @@ -78,7 +70,7 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode ConvertNode(TreeNode node, IDictionary allPictureInfos) + private TreeNode ConvertNode(TreeNode node, MenuItemRecord entity, IDictionary allPictureInfos) { var cat = node.Value; var name = cat.Id > 0 ? cat.GetLocalized(x => x.Name) : null; @@ -103,14 +95,30 @@ private TreeNode ConvertNode(TreeNode node, IDictionary } } - var convertedNode = new TreeNode(menuItem); - convertedNode.Id = node.Id; + // Apply inheritable properties. + menuItem.Visible = entity.Published; + menuItem.PermissionNames = entity.PermissionNames; + + if (entity.NoFollow) + { + menuItem.LinkHtmlAttributes.Add("rel", "nofollow"); + } + + if (entity.NewWindow) + { + menuItem.LinkHtmlAttributes.Add("target", "_blank"); + } + + var convertedNode = new TreeNode(menuItem) + { + Id = node.Id + }; if (node.HasChildren) { foreach (var childNode in node.Children) { - convertedNode.Append(ConvertNode(childNode, allPictureInfos)); + convertedNode.Append(ConvertNode(childNode, entity, allPictureInfos)); } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index a24c5076b8..ce552d6302 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -1,12 +1,11 @@ using SmartStore.Collections; -using SmartStore.Core.Plugins; using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI { // INFO: The provider's SystemName is also the edit template name > Views/Shared/EditorTemplates/MenuItem.{SystemName}.cshtml. // Model is: string - [SystemName("entity")] + [MenuItemProvider("entity")] public class EntityMenuItemProvider : MenuItemProviderBase { private readonly ILinkResolver _linkResolver; diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/IMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/IMenuItemProvider.cs index 5d0b2f7223..46713f1000 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/IMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/IMenuItemProvider.cs @@ -15,10 +15,19 @@ public interface IMenuItemProvider public class MenuItemProviderRequest { + /// + /// Represents the origin for the creation of the tree. + /// public string Origin { get; set; } + /// + /// Node to which items are to be appended. + /// public TreeNode Parent { get; set; } + /// + /// Entity that is converted to a menu item. + /// public MenuItemRecord Entity { get; set; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs deleted file mode 100644 index 0f669c0e97..0000000000 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemMetadata.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SmartStore.Web.Framework.UI -{ - public class MenuItemMetadata - { - public string ProviderName { get; set; } - } -} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 32e32f09d5..90d37a4d99 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -36,38 +36,43 @@ public virtual void Append(MenuItemProviderRequest request) /// Menu item. protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) { + var title = entity.GetLocalized(x => x.Title); var shortDescription = entity.GetLocalized(x => x.ShortDescription); var menuItem = new MenuItem { EntityId = entity.Id, - Text = entity.GetLocalized(x => x.Title), + Text = title, Visible = entity.Published, - Icon = entity.Icon, + Rtl = title?.CurrentLanguage?.Rtl ?? false, PermissionNames = entity.PermissionNames - }; + }; - if (entity.NoFollow) - { - menuItem.LinkHtmlAttributes.Add("rel", "nofollow"); - } if (shortDescription.HasValue()) { menuItem.LinkHtmlAttributes.Add("title", shortDescription); } - if (entity.NewWindow) + + if (entity.NoFollow) { - menuItem.LinkHtmlAttributes.Add("target", "_blank"); + menuItem.LinkHtmlAttributes.Add("rel", "nofollow"); } - if (entity.HtmlId.HasValue()) + + if (entity.NewWindow) { - menuItem.LinkHtmlAttributes.Add("id", entity.HtmlId); + menuItem.LinkHtmlAttributes.Add("target", "_blank"); } + if (entity.CssClass.HasValue()) { menuItem.LinkHtmlAttributes.Add("class", entity.CssClass); } + if (entity.HtmlId.HasValue()) + { + menuItem.LinkHtmlAttributes.Add("id", entity.HtmlId); + } + if (entity.Icon.HasValue()) { menuItem.Icon = IconExplorer.GetIconByName(entity.Icon).GetCssClass(entity.Style); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderMetadata.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderMetadata.cs new file mode 100644 index 0000000000..3377d7be3e --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderMetadata.cs @@ -0,0 +1,40 @@ +using System; + +namespace SmartStore.Web.Framework.UI +{ + /// + /// Applies metadata to menu item provider types which implement . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class MenuItemProviderAttribute : Attribute + { + public MenuItemProviderAttribute(string providerName) + { + Guard.NotEmpty(providerName, nameof(providerName)); + + ProviderName = providerName; + } + + /// + /// Unique name of the provider. + /// + public string ProviderName { get; set; } + + /// + /// Indicates that the provider appends multiple items to the tree. + /// The corresponding MenuItemRecord cannot have child elements and certain properties such as title, short description etc. are ignored. + /// + public bool AppendsMultipleItems { get; set; } + } + + + /// + /// Represents menu item provider registration metadata. + /// + public class MenuItemProviderMetadata + { + public string ProviderName { get; set; } + + public bool AppendsMultipleItems { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs index 2bee1270ad..782e30f246 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs @@ -1,11 +1,10 @@ using System.Web.Routing; using Newtonsoft.Json; using SmartStore.Collections; -using SmartStore.Core.Plugins; namespace SmartStore.Web.Framework.UI { - [SystemName("route")] + [MenuItemProvider("route")] public class RouteMenuItemProvider : MenuItemProviderBase { protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index b8e58adf5f..5a7731d0e4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -30,7 +30,7 @@ public class MenuController : AdminControllerBase private readonly IStoreMappingService _storeMappingService; private readonly IAclService _aclService; private readonly ICustomerService _customerService; - private readonly IDictionary> _menuItemProviders; + private readonly IDictionary> _menuItemProviders; private readonly AdminAreaSettings _adminAreaSettings; public MenuController( @@ -40,7 +40,7 @@ public MenuController( IStoreMappingService storeMappingService, IAclService aclService, ICustomerService customerService, - IEnumerable> menuItemProviders, + IEnumerable> menuItemProviders, AdminAreaSettings adminAreaSettings) { _menuStorage = menuStorage; @@ -429,15 +429,22 @@ private void PrepareModel(MenuRecordModel model, MenuRecord entity) private void PrepareModel(MenuItemRecordModel model, MenuItemRecord entity) { - var entities = _menuStorage.GetMenuItems(model.MenuId, 0, true); + Lazy provider = null; + var entities = _menuStorage.GetMenuItems(model.MenuId, 0, true).ToDictionary(x => x.Id, x => x); model.Locales = new List(); model.AllItems = new List(); + if (_menuItemProviders.TryGetValue(model.ProviderName, out provider)) + { + model.ProviderAppendsMultipleItems = provider.Metadata.AppendsMultipleItems; + } + // Preset max display order to always insert item at the end. if (entity == null && entities.Any()) { var item = entities + .Select(x => x.Value) .Where(x => x.ParentItemId == model.ParentItemId) .OrderByDescending(x => x.DisplayOrder) .FirstOrDefault(); @@ -446,7 +453,7 @@ private void PrepareModel(MenuItemRecordModel model, MenuItemRecord entity) } // Create list for selecting parent item. - var tree = entities.GetTree("EditMenu", _menuItemProviders, true); + var tree = entities.Values.GetTree("EditMenu", _menuItemProviders, true); tree.Traverse(x => { @@ -454,6 +461,12 @@ private void PrepareModel(MenuItemRecordModel model, MenuItemRecord entity) { // Ignore. Element cannot be parent itself. } + else if (entities.TryGetValue(x.Value.EntityId, out var record) && + _menuItemProviders.TryGetValue(record.ProviderName, out provider) && + provider.Metadata.AppendsMultipleItems) + { + // Ignore. Element cannot have child nodes. + } else if (!x.Value.IsGroupHeader) { var path = string.Join(" » ", x.Trail.Skip(1).Select(y => y.Value.Text)); diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs index 9be7b719a2..32f674ba92 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuItemRecordModel.cs @@ -16,6 +16,7 @@ public class MenuItemRecordModel : EntityModelBase, IIcon, ILocalizedModel
- @T("Admin.ContentManagement.AddMenuItem") @Html.ActionLink("(" + T("Admin.Common.BackToList") + ")", "Edit", new { id = Model.MenuId }) + @T("Admin.ContentManagement.AddMenuItem") @Html.ActionLink("(" + T("Common.Back") + ")", "Edit", new { id = Model.MenuId })
-
+
@(Html.LocalizedEditor("menu-item-localized", @
@@ -45,7 +45,7 @@
@*IMPORTANT: Do not delete, this hidden element contains the id to assign localized values to the corresponding language *@ - @Html.HiddenFor(model => model.Locales[item].LanguageId, new { id = Html.FieldIdFor(model => model.Locales[item].LanguageId) + "_1" }) + @Html.HiddenFor(model => model.Locales[item].LanguageId) @Html.EditorFor(model => model.Locales[item].Title) @Html.ValidationMessageFor(model => model.Locales[item].Title) @@ -113,7 +113,7 @@ @Html.ValidationMessageFor(model => model.BeginGroup)
-
+
@Html.SmartLabelFor(model => model.Icon)
@@ -150,7 +150,7 @@ @Html.ValidationMessageFor(model => model.NewWindow)
-
+
@Html.SmartLabelFor(model => model.HtmlId)
@@ -159,7 +159,7 @@ @Html.ValidationMessageFor(model => model.HtmlId)
-
+
@Html.SmartLabelFor(model => model.CssClass)
From 98161f1cb5f91c77ae922cd63dc420ada016c689 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 3 Apr 2019 14:42:49 +0200 Subject: [PATCH 087/460] Minor renaming --- .../Providers/CatalogMenuItemProvider.cs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs index b53f9cd7ac..eb25aa7cb7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs @@ -70,28 +70,31 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode ConvertNode(TreeNode node, MenuItemRecord entity, IDictionary allPictureInfos) + private TreeNode ConvertNode( + TreeNode categoryNode, + MenuItemRecord entity, + IDictionary allPictureInfos) { - var cat = node.Value; - var name = cat.Id > 0 ? cat.GetLocalized(x => x.Name) : null; + var node = categoryNode.Value; + var name = node.Id > 0 ? node.GetLocalized(x => x.Name) : null; var menuItem = new MenuItem { - EntityId = cat.Id, - Text = name?.Value ?? cat.Name, + EntityId = node.Id, + Text = name?.Value ?? node.Name, Rtl = name?.CurrentLanguage?.Rtl ?? false, - BadgeText = cat.Id > 0 ? cat.GetLocalized(x => x.BadgeText) : null, - BadgeStyle = (BadgeStyle)cat.BadgeStyle, - RouteName = cat.Id > 0 ? "Category" : "HomePage" + BadgeText = node.Id > 0 ? node.GetLocalized(x => x.BadgeText) : null, + BadgeStyle = (BadgeStyle)node.BadgeStyle, + RouteName = node.Id > 0 ? "Category" : "HomePage" }; - if (cat.Id > 0) + if (node.Id > 0) { - menuItem.RouteValues.Add("SeName", cat.GetSeName()); + menuItem.RouteValues.Add("SeName", node.GetSeName()); - if (cat.ParentCategoryId == 0 && cat.Published && cat.PictureId != null) + if (node.ParentCategoryId == 0 && node.Published && node.PictureId != null) { - menuItem.ImageId = cat.PictureId; + menuItem.ImageId = node.PictureId; } } @@ -111,12 +114,12 @@ private TreeNode ConvertNode(TreeNode node, MenuItemRec var convertedNode = new TreeNode(menuItem) { - Id = node.Id + Id = categoryNode.Id }; - if (node.HasChildren) + if (categoryNode.HasChildren) { - foreach (var childNode in node.Children) + foreach (var childNode in categoryNode.Children) { convertedNode.Append(ConvertNode(childNode, entity, allPictureInfos)); } From 8893637572238c92440dbfc6b339d33de87c4ca4 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 3 Apr 2019 22:01:52 +0200 Subject: [PATCH 088/460] Menu builder --- .../Infrastructure/AppDomainTypeFinder.cs | 2 +- .../DependencyRegistrar.cs | 2 + .../UI/Components/Menu/MenuItem.cs | 9 +- .../UI/Menus/DatabaseMenu.cs | 85 ++++++++++++++++++- .../UI/Menus/DatabaseMenuResolver.cs | 30 ++----- .../Providers/CatalogMenuItemProvider.cs | 1 + .../Menus/Providers/EntityMenuItemProvider.cs | 11 +++ .../Providers/MenuItemProviderMetadata.cs | 3 +- 8 files changed, 112 insertions(+), 31 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs index b7ffec8f69..4ed72318a1 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs @@ -71,7 +71,7 @@ public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable().PropertiesAutowired(PropertyWiringOptions.None).InstancePerRequest(); } + builder.RegisterType().Named("database").InstancePerDependency(); + var menuTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); foreach (var type in menuTypes) { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs index 09786612db..f51dcfbc45 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs @@ -14,11 +14,12 @@ public MenuItem() /// If this menu item refers to an entity, the id of the backed entity (like category, products e.g.) ///
public int EntityId { get; set; } + public string EntityName { get; set; } - /// - /// The total count of contained elements (like the count of products within a category) - /// - public int? ElementsCount { get; set; } + /// + /// The total count of contained elements (like the count of products within a category) + /// + public int? ElementsCount { get; set; } public string Id { get; set; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs index e3643e2252..194c1b1c51 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; using SmartStore.Collections; using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Cms; +using SmartStore.Core.Logging; using SmartStore.Services; using SmartStore.Services.Cms; +using SmartStore.Services.Search; namespace SmartStore.Web.Framework.UI { @@ -14,26 +18,37 @@ namespace SmartStore.Web.Framework.UI /// internal class DatabaseMenu : MenuBase { + private static object s_lock = new object(); + + private readonly Lazy _catalogSearchService; private readonly IMenuStorage _menuStorage; + private readonly ILogger _logger; private readonly DbQuerySettings _querySettings; + private readonly Lazy _catalogSettings; private readonly IDictionary> _menuItemProviders; public DatabaseMenu( string menuName, ICommonServices services, + Lazy catalogSearchService, IMenuStorage menuStorage, + ILogger logger, IMenuPublisher menuPublisher, DbQuerySettings querySettings, + Lazy catalogSettings, IEnumerable> menuItemProviders) { Guard.NotEmpty(menuName, nameof(menuName)); Name = menuName; - Services = services; - MenuPublisher = menuPublisher; + Services = services; + _catalogSearchService = catalogSearchService; _menuStorage = menuStorage; + _logger = logger; + MenuPublisher = menuPublisher; _querySettings = querySettings; + _catalogSettings = catalogSettings; _menuItemProviders = menuItemProviders.ToDictionarySafe(x => x.Metadata.ProviderName, x => x); } @@ -41,6 +56,72 @@ public DatabaseMenu( public override bool ApplyPermissions => false; + public override void ResolveElementCounts(TreeNode curNode, bool deep = false) + { + if (curNode == null || !curNode.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category))) + { + return; + } + + try + { + Func, bool> predicate = x => !x.Value.ElementsCount.HasValue && x.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category)); + + using (Services.Chronometer.Step($"DatabaseMenu.ResolveElementsCount() for {curNode.Value.Text.EmptyNull()}")) + { + // Perf: only resolve counts for categories in the current path. + while (curNode != null) + { + if (curNode.Children.Any(predicate)) + { + lock (s_lock) + { + if (curNode.Children.Any(predicate)) + { + var nodes = deep ? curNode.SelectNodes(x => true, false) : curNode.Children.AsEnumerable(); + nodes = nodes.Where(x => x.Value.EntityId != 0 && x.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category))); + + foreach (var node in nodes) + { + var categoryIds = new HashSet(); + + if (_catalogSettings.Value.ShowCategoryProductNumberIncludingSubcategories) + { + // Include subcategories. + node.Traverse(x => + { + categoryIds.Add(x.Value.EntityId); + }, true); + } + else + { + categoryIds.Add(node.Value.EntityId); + } + + var context = new CatalogSearchQuery() + .VisibleOnly() + .VisibleIndividuallyOnly(true) + .WithCategoryIds(null, categoryIds.ToArray()) + .HasStoreId(Services.StoreContext.CurrentStoreIdIfMultiStoreMode) + .BuildFacetMap(false) + .BuildHits(false); + + node.Value.ElementsCount = _catalogSearchService.Value.Search(context).TotalHitsCount; + } + } + } + } + + curNode = curNode.Parent; + } + } + } + catch (Exception ex) + { + _logger.Error(ex); + } + } + protected override TreeNode Build() { var entities = _menuStorage.GetMenuItems(Name, Services.StoreContext.CurrentStore.Id); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs index 8cfe1c0cf7..d28d3ed30f 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenuResolver.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using SmartStore.Core.Data; +using Autofac; using SmartStore.Services; using SmartStore.Services.Cms; @@ -8,27 +6,17 @@ namespace SmartStore.Web.Framework.UI { public class DatabaseMenuResolver : IMenuResolver { - protected readonly ICommonServices _services; + protected readonly IComponentContext _ctx; protected readonly IMenuStorage _menuStorage; - protected readonly IMenuPublisher _menuPublisher; - protected readonly IEnumerable> _menuItemProviders; public DatabaseMenuResolver( - ICommonServices services, - IMenuStorage menuStorage, - IMenuPublisher menuPublisher, - IEnumerable> menuItemProviders) + IComponentContext ctx, + IMenuStorage menuStorage) { - _services = services; + _ctx = ctx; _menuStorage = menuStorage; - _menuPublisher = menuPublisher; - _menuItemProviders = menuItemProviders; - - QuerySettings = DbQuerySettings.Default; } - public DbQuerySettings QuerySettings { get; set; } - public int Order => 1; public bool Exists(string menuName) @@ -38,12 +26,8 @@ public bool Exists(string menuName) public IMenu Resolve(string name) { - return new DatabaseMenu(name, - _services, - _menuStorage, - _menuPublisher, - QuerySettings, - _menuItemProviders); + var menu = _ctx.ResolveNamed("database", new NamedParameter("menuName", name)); + return menu; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs index eb25aa7cb7..14d1806e98 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs @@ -81,6 +81,7 @@ private TreeNode ConvertNode( var menuItem = new MenuItem { EntityId = node.Id, + EntityName = nameof(Category), Text = name?.Value ?? node.Name, Rtl = name?.CurrentLanguage?.Rtl ?? false, BadgeText = node.Id > 0 ? node.GetLocalized(x => x.BadgeText) : null, diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index ce552d6302..435b692ca3 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -26,6 +26,17 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode /// Indicates that the provider appends multiple items to the tree. - /// The corresponding MenuItemRecord cannot have child elements and certain properties such as title, short description etc. are ignored. + /// The corresponding cannot have child elements and certain properties such as title, short description etc. are ignored. /// public bool AppendsMultipleItems { get; set; } } From 2308f4ac3e6fa059d7fc06624e26ff019feb36e6 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 3 Apr 2019 20:40:40 +0200 Subject: [PATCH 089/460] Prevent thread context switch after an async operation in some places --- .../SmartStore.Core/Caching/MemoryCacheManager.cs | 4 ++-- src/Libraries/SmartStore.Core/Utilities/Retry.cs | 8 ++++---- .../Administration/Controllers/LanguageController.cs | 10 ++++++---- src/Presentation/SmartStore.Web/Global.asax | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index 37f9bc939d..c7e0ba0436 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -111,8 +111,8 @@ public async Task GetAsync(string key, Func> acquirer, TimeSpan? d { using (_scopeAccessor.Value.BeginScope(key)) { - value = await acquirer().ConfigureAwait(false); - Put(key, value, duration, _scopeAccessor.Value.Current?.Dependencies); + value = await acquirer(); + Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); return value; } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Retry.cs b/src/Libraries/SmartStore.Core/Utilities/Retry.cs index 7c42ace8d6..916b64e3b2 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Retry.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Retry.cs @@ -86,11 +86,11 @@ public static async Task RunAsync(Func operation, int attempts, TimeSpan? { Guard.NotNull(operation, nameof(operation)); - Func> wrapper = async () => + async Task wrapper() { - await operation().ConfigureAwait(false); + await operation(); return true; - }; + } await RunAsync(wrapper, attempts, wait, onFailed); } @@ -123,7 +123,7 @@ public static async Task RunAsync(Func> operation, int attempts, T try { // Call the function passed in by the caller. - return await operation().ConfigureAwait(false); + return await operation(); } catch (Exception ex) { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs index 0bfdb6d317..108adde8fc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs @@ -890,17 +890,19 @@ public async Task Download(int setId) { if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { - var ctx = new LanguageDownloadContext(setId); - ctx.AvailableResources = await CheckAvailableResources(); + var ctx = new LanguageDownloadContext(setId) + { + AvailableResources = await CheckAvailableResources() + }; - if (ctx.AvailableResources.Resources.Any()) + if (ctx.AvailableResources.Resources.Any()) { var task = AsyncRunner.Run( (container, ct, obj) => DownloadCore(container, ct, obj as LanguageDownloadContext), ctx, CancellationToken.None, TaskCreationOptions.None, - TaskScheduler.Default).ConfigureAwait(false); + TaskScheduler.Default); } } diff --git a/src/Presentation/SmartStore.Web/Global.asax b/src/Presentation/SmartStore.Web/Global.asax index 612debf2a2..ebaeaa3f40 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 ef860b3956b7cfe1620969e0e236ebf117827c73 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 3 Apr 2019 22:57:30 +0200 Subject: [PATCH 090/460] Fixed StackOverflow in MemorySet --- src/Libraries/SmartStore.Core/Caching/MemorySet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs index 48cc1c0fb6..c88cacd703 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs @@ -18,7 +18,7 @@ public MemorySet(ICacheManager cache) public void AddRange(IEnumerable items) { - this.AddRange(items); + base.UnionWith(items); } public bool Move(string destinationKey, string item) From 8efb3820d59c71cd3b3a28b163f20559ed8da082 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 4 Apr 2019 00:20:50 +0200 Subject: [PATCH 091/460] Locking & other concurrency stuff --- .../SmartStore.Core/Collections/MultiMap.cs | 57 +++++++++---------- .../SmartStore.Core/Plugins/PluginFinder.cs | 7 ++- .../Theming/Assets/DefaultAssetCache.cs | 36 ++++++------ .../UI/BundleBuilder.cs | 13 ++--- .../Caching/WebApiCachingControllingData.cs | 4 +- .../WebApi/Caching/WebApiCachingUserData.cs | 4 +- .../Infrastructure/CatalogSiteMap.cs | 5 +- 7 files changed, 61 insertions(+), 65 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs index 6c8f85fc78..5c8eb9573d 100644 --- a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs +++ b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Concurrent; using System.Linq; using System.Linq.Expressions; using Newtonsoft.Json; @@ -17,11 +16,11 @@ namespace SmartStore.Collections [Serializable] public class Multimap : IEnumerable>> { - private readonly IDictionary> _items; + private readonly IDictionary> _dict; private readonly Func, ICollection> _collectionCreator; private readonly bool _isReadonly = false; - private readonly static Func, ICollection> _defaultCollectionCreator = x => new List(x ?? Enumerable.Empty()); + internal readonly static Func, ICollection> DefaultCollectionCreator = x => new List(x ?? Enumerable.Empty()); public Multimap() : this(EqualityComparer.Default) @@ -30,8 +29,8 @@ public Multimap() public Multimap(IEqualityComparer comparer) { - _items = new Dictionary>(comparer ?? EqualityComparer.Default); - _collectionCreator = _defaultCollectionCreator; + _dict = new Dictionary>(comparer ?? EqualityComparer.Default); + _collectionCreator = DefaultCollectionCreator; } public Multimap(Func, ICollection> collectionCreator) @@ -49,7 +48,7 @@ internal Multimap(IDictionary> dictionary, Func> dictionary, bool isRea { Guard.NotNull(dictionary, nameof(dictionary)); - _items = dictionary; + _dict = dictionary; if (isReadonly && dictionary != null) { @@ -81,21 +80,21 @@ public Multimap(IEnumerable>> items, IEqu // for serialization Guard.NotNull(items, nameof(items)); - _items = new Dictionary>(comparer ?? EqualityComparer.Default); - _collectionCreator = _defaultCollectionCreator; + _dict = new Dictionary>(comparer ?? EqualityComparer.Default); + _collectionCreator = DefaultCollectionCreator; if (items != null) { foreach (var kvp in items) { - _items[kvp.Key] = CreateCollection(kvp.Value); + _dict[kvp.Key] = CreateCollection(kvp.Value); } } } protected virtual ICollection CreateCollection(IEnumerable values) { - return (_collectionCreator ?? _defaultCollectionCreator)(values ?? Enumerable.Empty()); + return (_collectionCreator ?? DefaultCollectionCreator)(values ?? Enumerable.Empty()); } /// @@ -105,7 +104,7 @@ public int Count { get { - return this._items.Keys.Count; + return this._dict.Keys.Count; } } @@ -116,7 +115,7 @@ public int TotalValueCount { get { - return this._items.Values.Sum(x => x.Count); + return this._dict.Values.Sum(x => x.Count); } } @@ -128,15 +127,15 @@ public virtual ICollection this[TKey key] { get { - if (!_items.ContainsKey(key)) + if (!_dict.ContainsKey(key)) { if (!_isReadonly) - _items[key] = CreateCollection(null); + _dict[key] = CreateCollection(null); else return null; } - return _items[key]; + return _dict[key]; } } @@ -145,7 +144,7 @@ public virtual ICollection this[TKey key] /// public virtual ICollection Keys { - get { return _items.Keys; } + get { return _dict.Keys; } } /// @@ -153,7 +152,7 @@ public virtual ICollection Keys /// public virtual ICollection> Values { - get { return _items.Values; } + get { return _dict.Values; } } public IEnumerable Find(TKey key, Expression> predicate) @@ -161,9 +160,9 @@ public IEnumerable Find(TKey key, Expression> predica Guard.NotNull(key, nameof(key)); Guard.NotNull(predicate, nameof(predicate)); - if (_items.ContainsKey(key)) + if (_dict.ContainsKey(key)) { - return _items[key].Where(predicate.Compile()); + return _dict[key].Where(predicate.Compile()); } return Enumerable.Empty(); @@ -203,12 +202,12 @@ public virtual bool Remove(TKey key, TValue value) { CheckNotReadonly(); - if (!_items.ContainsKey(key)) + if (!_dict.ContainsKey(key)) return false; - bool result = _items[key].Remove(value); - if (_items[key].Count == 0) - _items.Remove(key); + bool result = _dict[key].Remove(value); + if (_dict[key].Count == 0) + _dict.Remove(key); return result; } @@ -221,7 +220,7 @@ public virtual bool Remove(TKey key, TValue value) public virtual bool RemoveAll(TKey key) { CheckNotReadonly(); - return _items.Remove(key); + return _dict.Remove(key); } /// @@ -230,7 +229,7 @@ public virtual bool RemoveAll(TKey key) public virtual void Clear() { CheckNotReadonly(); - _items.Clear(); + _dict.Clear(); } /// @@ -240,7 +239,7 @@ public virtual void Clear() /// True if the multimap has one or more values for the specified key, otherwise false. public virtual bool ContainsKey(TKey key) { - return _items.ContainsKey(key); + return _dict.ContainsKey(key); } /// @@ -251,7 +250,7 @@ public virtual bool ContainsKey(TKey key) /// True if the multimap contains such a value; otherwise, false. public virtual bool ContainsValue(TKey key, TValue value) { - return _items.ContainsKey(key) && _items[key].Contains(value); + return _dict.ContainsKey(key) && _dict[key].Contains(value); } /// @@ -269,7 +268,7 @@ IEnumerator IEnumerable.GetEnumerator() /// An object that can be used to iterate through the multimap. public virtual IEnumerator>> GetEnumerator() { - foreach (KeyValuePair> pair in _items) + foreach (KeyValuePair> pair in _dict) yield return pair; } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs index 9bad247521..095fe2f580 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs @@ -11,11 +11,9 @@ public class PluginFinder : IPluginFinder private readonly IDictionary _nameMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); private readonly IDictionary _assemblyMap = new Dictionary(); - private static readonly object _lock = new object(); - private PluginFinder() { - lock (_lock) + lock (_plugins) { LoadPlugins(); } @@ -25,6 +23,9 @@ private PluginFinder() private void LoadPlugins() { + if (_plugins != null) + return; + var plugins = PluginManager.ReferencedPlugins.ToList(); plugins.Sort(); //sort _plugins = plugins; diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs index a834f3cfd6..ede36d2153 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs @@ -13,6 +13,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Themes; using SmartStore.Services; +using SmartStore.Utilities.Threading; namespace SmartStore.Web.Framework.Theming.Assets { @@ -23,7 +24,6 @@ public class DefaultAssetCache : IAssetCache public const string UrlRewriteCode = "urlrewrite"; public static readonly IAssetCache Null = new NullCache(); - private static readonly object _lock = new object(); const string CacheKeyPrefix = "sm:AssetCacheEntry:"; private readonly bool _isEnabled; @@ -106,7 +106,7 @@ public CachedAssetEntry GetAsset(string virtualPath) var content = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.css")); if (content == null) { - lock (_lock) + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) { InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); return null; @@ -150,12 +150,10 @@ public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virt Guard.NotEmpty(virtualPath, nameof(virtualPath)); Guard.NotEmpty(content, nameof(content)); - lock (_lock) - { - string themeName; - int storeId; - var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out themeName, out storeId); + var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) + { CacheFolder.CreateDirectory(cacheDirectoryName); try @@ -205,14 +203,11 @@ public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virt public void Clear() { - lock (_lock) + if (_env.TenantFolder.DirectoryExists("AssetCache")) { - if (_env.TenantFolder.DirectoryExists("AssetCache")) - { - _env.TenantFolder.TryDeleteDirectory("AssetCache"); - // Remove the eviction observer also - HttpRuntime.Cache.RemoveByPattern(CacheKeyPrefix); - } + _env.TenantFolder.TryDeleteDirectory("AssetCache"); + // Remove the eviction observer also + HttpRuntime.Cache.RemoveByPattern(CacheKeyPrefix); } } @@ -220,12 +215,10 @@ public bool InvalidateAsset(string virtualPath) { Guard.NotEmpty(virtualPath, nameof(virtualPath)); - lock (_lock) - { - string themeName; - int storeId; - var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out themeName, out storeId); + var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) + { if (CacheFolder.DirectoryExists(cacheDirectoryName)) { return InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); @@ -235,6 +228,11 @@ public bool InvalidateAsset(string virtualPath) } } + private string BuildLockKey(string dirName) + { + return "AssetCache.Dir." + dirName; + } + /// /// Invalidates a cached asset when any themevar was changed or the theme was touched on file system /// diff --git a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs index 7d5850f6b2..744553f48d 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs @@ -11,6 +11,7 @@ using SmartStore.Services.Seo; using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Core.Themes; +using SmartStore.Utilities.Threading; namespace SmartStore.Web.Framework.UI { @@ -31,13 +32,11 @@ public class BundleBuilder : IBundleBuilder private readonly IWorkContext _workContext; private readonly IThemeContext _themeContext; - private static readonly object s_lock = new object(); - public BundleBuilder(IStoreContext storeContext, IWorkContext workContext, IThemeContext themeContext) { - this._storeContext = storeContext; - this._workContext = workContext; - this._themeContext = themeContext; + _storeContext = storeContext; + _workContext = workContext; + _themeContext = themeContext; } public string Build(BundleType type, IEnumerable files) @@ -45,11 +44,11 @@ public string Build(BundleType type, IEnumerable files) if (files == null || !files.Any()) return string.Empty; - string bundleVirtualPath = this.GetBundleVirtualPath(type, files); + var bundleVirtualPath = this.GetBundleVirtualPath(type, files); var bundleFor = BundleTable.Bundles.GetBundleFor(bundleVirtualPath); if (bundleFor == null) { - lock (s_lock) + using (KeyedLock.Lock("BundleBuilder.Build." + bundleVirtualPath)) { bundleFor = BundleTable.Bundles.GetBundleFor(bundleVirtualPath); if (bundleFor == null) diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingControllingData.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingControllingData.cs index c1e0f4415e..b76b8b635b 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingControllingData.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingControllingData.cs @@ -7,9 +7,9 @@ namespace SmartStore.Web.Framework.WebApi.Caching { public static class WebApiCachingControllingData { - private static object _lock = new object(); + private static readonly object _lock = new object(); - public static string Key { get { return "WebApiControllingData"; } } + public static string Key { get; } = "WebApiControllingData"; public static void Remove() { diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingUserData.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingUserData.cs index 6f0a2a5c31..e6f8c62cf1 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingUserData.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/Caching/WebApiCachingUserData.cs @@ -14,7 +14,7 @@ namespace SmartStore.Web.Framework.WebApi.Caching { public static class WebApiCachingUserData { - private static object _lock = new object(); + private static readonly object _lock = new object(); /// /// Lazy storing... fired on app shut down. Note that items with CacheItemPriority.NotRemovable are not removed when the cache is emptied. @@ -60,7 +60,7 @@ private static void OnDataRemoved(string key, object value, CacheItemRemovedReas } } - public static string Key { get { return "WebApiUserData"; } } + public static string Key { get; } = "WebApiUserData"; public static void Remove() { diff --git a/src/Presentation/SmartStore.Web/Infrastructure/CatalogSiteMap.cs b/src/Presentation/SmartStore.Web/Infrastructure/CatalogSiteMap.cs index cee84aeed7..125144ccf9 100644 --- a/src/Presentation/SmartStore.Web/Infrastructure/CatalogSiteMap.cs +++ b/src/Presentation/SmartStore.Web/Infrastructure/CatalogSiteMap.cs @@ -11,14 +11,13 @@ using SmartStore.Services.Media; using SmartStore.Services.Search; using SmartStore.Services.Seo; +using SmartStore.Utilities.Threading; using SmartStore.Web.Framework.UI; namespace SmartStore.Web.Infrastructure { public class CatalogSiteMap : SiteMapBase { - private static object s_lock = new object(); - const string SiteMapName = "catalog"; private readonly ICategoryService _categoryService; @@ -73,7 +72,7 @@ public override void ResolveElementCounts(TreeNode curNode, bool deep { if (curNode.Children.Any(x => !x.Value.ElementsCount.HasValue)) { - lock (s_lock) + using (KeyedLock.Lock("Menu.ResolveElementCounts." + SiteMapName)) { if (curNode.Children.Any(x => !x.Value.ElementsCount.HasValue)) { From 99f86351fab6609559f9d3d6aa04a546f598eec6 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 4 Apr 2019 03:11:18 +0200 Subject: [PATCH 092/460] More concurrency stuff --- .../Collections/ConcurrentMultimap.cs | 210 ++++++++++++++---- .../SmartStore.Core/Collections/MultiMap.cs | 10 +- .../Extensions/CollectionExtensions.cs | 4 - .../SmartStore.Core/Plugins/PluginFinder.cs | 4 +- 4 files changed, 175 insertions(+), 53 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs index 3929c80b79..c279c2b2f2 100644 --- a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs +++ b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs @@ -4,6 +4,8 @@ using System.Collections.Concurrent; using System.Linq; using Newtonsoft.Json; +using SmartStore.Utilities; +using System.Threading; namespace SmartStore.Collections { @@ -14,40 +16,62 @@ namespace SmartStore.Collections /// The type of value. [JsonConverter(typeof(ConcurrentMultiMapConverter))] [Serializable] - public class ConcurrentMultimap : IEnumerable>> + public class ConcurrentMultimap : IEnumerable>> { - private readonly ConcurrentDictionary> _items; - private static readonly Func, IProducerConsumerCollection> _bagCreator = (col) => new ConcurrentBag(col); + private readonly ConcurrentDictionary> _dict; + private readonly Func, ICollection> _collectionCreator; public ConcurrentMultimap() - : this(null, null) + : this(null, null, null) { } public ConcurrentMultimap(IEqualityComparer comparer) - : this(null, comparer) + : this(null, comparer, null) { } - public ConcurrentMultimap(IEnumerable>> items) - : this(items, null) + public ConcurrentMultimap(IEnumerable>> items) + : this(items, null, null) { // for serialization } - public ConcurrentMultimap(IEnumerable>> items, IEqualityComparer comparer) + public ConcurrentMultimap( + IEnumerable>> items, + IEqualityComparer comparer) + : this(items, comparer, null) { - _items = new ConcurrentDictionary>(comparer ?? EqualityComparer.Default); + // for serialization + } + + public ConcurrentMultimap( + IEnumerable>> items, + IEqualityComparer comparer, + Func, ICollection> collectionCreator) + { + _collectionCreator = collectionCreator; + _dict = new ConcurrentDictionary>( + ConvertItems(items), + comparer ?? EqualityComparer.Default); + } - if (items != null) + private IEnumerable>> ConvertItems(IEnumerable>> items) + { + if (items == null) + yield break; + + foreach (var item in items) { - foreach (var kvp in items) - { - _items.TryAdd(kvp.Key, _bagCreator(kvp.Value ?? Enumerable.Empty())); - } + yield return new KeyValuePair>(item.Key, CreateCollection(item.Value)); } } + protected virtual ICollection CreateCollection(IEnumerable values) + { + return (_collectionCreator ?? Multimap.DefaultCollectionCreator)(values ?? Enumerable.Empty()); + } + /// /// Gets the count of groups/keys. /// @@ -55,7 +79,7 @@ public int Count { get { - return _items.Keys.Count; + return _dict.Keys.Count; } } @@ -66,29 +90,33 @@ public int TotalValueCount { get { - return this._items.Values.Sum(x => x.Count); + return this._dict.Values.Sum(x => x.Count); + } + } + + /// + /// Gets the count of items in the requested group. + /// + public int ValueCount(TKey key) + { + if (_dict.TryGetValue(key, out var col)) + { + return col.Count; } + + return 0; } /// /// Gets the collection of values stored under the specified key. /// /// The key. - public virtual IProducerConsumerCollection this[TKey key] + public virtual ICollection this[TKey key] { get { - IProducerConsumerCollection value; - if (!_items.TryGetValue(key, out value)) - { - value = _bagCreator(Enumerable.Empty()); - if (!_items.TryAdd(key, value)) - { - value = null; - } - } - - return value; + GetOrCreateValues(key, null, out var col); + return col; } } @@ -97,15 +125,15 @@ public virtual IProducerConsumerCollection this[TKey key] /// public virtual ICollection Keys { - get { return _items.Keys; } + get { return _dict.Keys; } } /// - /// Gets the collection of collections of values. + /// Gets all value collections as readonly to prevent non-blocking list operations. /// - public virtual ICollection> Values + public virtual IEnumerable> Values { - get { return _items.Values; } + get { return _dict.Values.Select(x => x.AsReadOnly()); } } /// @@ -115,7 +143,10 @@ public virtual ICollection> Values /// The value. public virtual void TryAdd(TKey key, TValue value) { - this[key].TryAdd(value); + if (!GetOrCreateValues(key, new[] { value }, out var col)) + { + ExecuteCollectionAction(col, x => x.Add(value)); + } } /// @@ -127,18 +158,73 @@ public virtual void TryAddRange(TKey key, IEnumerable values) { Guard.NotNull(values, nameof(values)); - values.Each(x => this[key].TryAdd(x)); + if (!GetOrCreateValues(key, values, out var col)) + { + ExecuteCollectionAction(col, x => x.AddRange(values)); + } + } + + /// + /// Attempts to remove the specified value for the specified key. + /// + /// The key. + /// The value. + /// True if such a value existed and was removed; otherwise false. + public virtual bool TryRemove(TKey key, TValue value) + { + if (!_dict.ContainsKey(key)) + return false; + + var col = _dict[key]; + var removed = false; + + ExecuteCollectionAction(col, x => + { + removed = col.Remove(value); + if (col.Count == 0) + { + _dict.TryRemove(key, out _); + } + }); + + return removed; + } + + /// + /// Attempts to remove a range of values for the specified key. + /// + /// The key. + /// The values to remove from the group. + /// True if at least one item in group has been removed; otherwise false. + public virtual bool TryRemoveRange(TKey key, IEnumerable values) + { + Guard.NotNull(values, nameof(values)); + + int numRemoved = 0; + + if (_dict.TryGetValue(key, out var col)) + { + ExecuteCollectionAction(col, x => + { + foreach (var value in values) + { + if (col.Remove(value)) + numRemoved++; + } + }); + } + + return numRemoved > 0; } /// - /// Attempts to remove all values for the specified key. + /// Attempts to remove and return all values for the specified key. /// /// The key. /// True if any such values existed; otherwise false. - public virtual bool TryRemoveAll(TKey key) + public virtual bool TryRemoveAll(TKey key, out ICollection collection) { - IProducerConsumerCollection collection; - return _items.TryRemove(key, out collection); + return _dict.TryRemove(key, out collection); } /// @@ -146,7 +232,7 @@ public virtual bool TryRemoveAll(TKey key) /// public virtual void Clear() { - _items.Clear(); + _dict.Clear(); } /// @@ -156,7 +242,7 @@ public virtual void Clear() /// True if the multimap has one or more values for the specified key, otherwise false. public virtual bool ContainsKey(TKey key) { - return _items.ContainsKey(key); + return _dict.ContainsKey(key); } /// @@ -167,7 +253,7 @@ public virtual bool ContainsKey(TKey key) /// True if the multimap contains such a value; otherwise, false. public virtual bool ContainsValue(TKey key, TValue value) { - return _items.ContainsKey(key) && _items[key].Contains(value); + return _dict.ContainsKey(key) && _dict[key].Contains(value); } /// @@ -183,12 +269,50 @@ IEnumerator IEnumerable.GetEnumerator() /// Returns an enumerator that iterates through the multimap. /// /// An object that can be used to iterate through the multimap. - public virtual IEnumerator>> GetEnumerator() + public virtual IEnumerator>> GetEnumerator() { - foreach (var pair in _items) + foreach (var pair in _dict) yield return pair; } + private bool GetOrCreateValues(TKey key, IEnumerable initial, out ICollection col) + { + // Return true when created + var created = false; + + col = _dict.GetOrAdd(key, k => + { + created = true; + return CreateCollection(initial); + }); + + return created; + } + + private void ExecuteCollectionAction(ICollection col, Action> action) + { + bool lockTaken = false; + try + { + var isBlocking = col is SynchronizedCollection; + + if (!isBlocking) + { + // Don't take lock on an already blocking collection. + Monitor.Enter(col, ref lockTaken); + } + + action(col); + } + finally + { + if (lockTaken) + { + Monitor.Exit(col); + } + } + } + #region Static members public static ConcurrentMultimap CreateFromLookup(ILookup source) diff --git a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs index 5c8eb9573d..28051a3b21 100644 --- a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs +++ b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs @@ -20,7 +20,8 @@ public class Multimap : IEnumerable, ICollection> _collectionCreator; private readonly bool _isReadonly = false; - internal readonly static Func, ICollection> DefaultCollectionCreator = x => new List(x ?? Enumerable.Empty()); + internal readonly static Func, ICollection> DefaultCollectionCreator = + x => new List(x ?? Enumerable.Empty()); public Multimap() : this(EqualityComparer.Default) @@ -81,7 +82,6 @@ public Multimap(IEnumerable>> items, IEqu Guard.NotNull(items, nameof(items)); _dict = new Dictionary>(comparer ?? EqualityComparer.Default); - _collectionCreator = DefaultCollectionCreator; if (items != null) { @@ -147,9 +147,9 @@ public virtual ICollection Keys get { return _dict.Keys; } } - /// - /// Gets the collection of collections of values. - /// + /// + /// Gets all value collections. + /// public virtual ICollection> Values { get { return _dict.Values; } diff --git a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs index 8fec538fbe..3085a78161 100644 --- a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs @@ -1,9 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Web.Routing; namespace SmartStore { diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs index 095fe2f580..4677d009f6 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs @@ -11,9 +11,11 @@ public class PluginFinder : IPluginFinder private readonly IDictionary _nameMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); private readonly IDictionary _assemblyMap = new Dictionary(); + private static readonly object _lock = new object(); + private PluginFinder() { - lock (_plugins) + lock (_lock) { LoadPlugins(); } From c8cbec0a56c7286f1565ab1c4b041bdba5a838a3 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 4 Apr 2019 11:57:52 +0200 Subject: [PATCH 093/460] Menu builder: show type icon instead of user icon in backend item list --- .../Cms/LinkResolverResult.cs | 27 ++++++++++- .../UI/Menus/DatabaseMenu.cs | 3 +- .../Providers/CatalogMenuItemProvider.cs | 11 ++++- .../Menus/Providers/EntityMenuItemProvider.cs | 26 ++++++++--- .../Menus/Providers/RouteMenuItemProvider.cs | 46 ++++++++++++------- .../Administration/Views/Menu/ItemList.cshtml | 2 +- .../Views/Shared/EditorTemplates/Link.cshtml | 26 ++--------- 7 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs index 796ff6b493..b33ca7294c 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs @@ -69,7 +69,7 @@ public string Link } } - public override string ToString() + public override string ToString() { return this.Link; } @@ -91,4 +91,29 @@ object ICloneable.Clone() return this.MemberwiseClone(); } } + + + public static class LinkResolverExtensions + { + public static (string Icon, string ResKey) GetLinkTypeInfo(this LinkType type) + { + switch (type) + { + case LinkType.Product: + return ("fa fa-cube", "Common.Entity.Product"); + case LinkType.Category: + return ("fa fa-sitemap", "Common.Entity.Category"); + case LinkType.Manufacturer: + return ("far fa-building", "Common.Entity.Manufacturer"); + case LinkType.Topic: + return ("far fa-file", "Common.Entity.Topic"); + case LinkType.Url: + return ("fa fa-link", "Common.Url"); + case LinkType.File: + return ("far fa-folder-open", "Common.File"); + default: + throw new SmartException("Unknown link builder type."); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs index 194c1b1c51..c1146e3d2a 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs @@ -58,13 +58,14 @@ public DatabaseMenu( public override void ResolveElementCounts(TreeNode curNode, bool deep = false) { - if (curNode == null || !curNode.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category))) + if (curNode == null) { return; } try { + // Only consider child elements of type category. Func, bool> predicate = x => !x.Value.ElementsCount.HasValue && x.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category)); using (Services.Chronometer.Step($"DatabaseMenu.ResolveElementsCount() for {curNode.Value.Text.EmptyNull()}")) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs index 14d1806e98..adccaaef22 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs @@ -3,6 +3,7 @@ using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Cms; +using SmartStore.Core.Localization; using SmartStore.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Localization; @@ -26,13 +27,21 @@ public CatalogMenuItemProvider( _services = services; _categoryService = categoryService; _pictureService = pictureService; + + T = NullLocalizer.Instance; } + public Localizer T { get; set; } + public override void Append(MenuItemProviderRequest request) { if (request.Origin.IsCaseInsensitiveEqual("EditMenu")) { - base.Append(request); + var item = ConvertToMenuItem(request.Entity); + item.BadgeText = T("Providers.MenuItems.FriendlyName.Catalog"); + item.Icon = "fa fa-sitemap"; + + request.Parent.Append(item); } else { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index 435b692ca3..28945d78d9 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -1,4 +1,5 @@ using SmartStore.Collections; +using SmartStore.Core.Localization; using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI @@ -13,8 +14,12 @@ public class EntityMenuItemProvider : MenuItemProviderBase public EntityMenuItemProvider(ILinkResolver linkResolver) { _linkResolver = linkResolver; + + T = NullLocalizer.Instance; } + public Localizer T { get; set; } + protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) { if (request.Entity.Model.IsEmpty()) @@ -26,25 +31,34 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) + public RouteMenuItemProvider() + { + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) { - if (request.Entity.Model.IsEmpty()) + if (request.Origin.IsCaseInsensitiveEqual("EditMenu")) { - return; + node.Value.BadgeText = T("Providers.MenuItems.FriendlyName.Route"); + node.Value.Icon = "fas fa-directions"; } try { - var routeValues = JsonConvert.DeserializeObject(request.Entity.Model); - var routeName = string.Empty; - - if (routeValues.TryGetValue("routename", out var val)) + if (request.Entity.Model.HasValue()) { - routeName = val as string; - routeValues.Remove("routename"); - } + var routeValues = JsonConvert.DeserializeObject(request.Entity.Model); + var routeName = string.Empty; - if (routeName.HasValue()) - { - node.Value.Route(routeName, routeValues); - } - else - { - node.Value.Action(routeValues); + if (routeValues.TryGetValue("routename", out var val)) + { + routeName = val as string; + routeValues.Remove("routename"); + } + + if (routeName.HasValue()) + { + node.Value.Route(routeName, routeValues); + } + else + { + node.Value.Action(routeValues); + } } } catch { } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml index 4b15d0e059..9cf56cc2af 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Menu/ItemList.cshtml @@ -52,7 +52,7 @@ else
@if (item.Icon.HasValue()) { - + } @if (item.Text.HasValue()) diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml index f2aa67a5eb..03a0424fb5 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml @@ -3,26 +3,6 @@ @functions { private string Value => ViewData.Model != null ? Convert.ToString(ViewData.Model) : null; - - private dynamic GetTypeInfos(LinkType type) - { - switch (type) - { - case LinkType.Product: - return new { icon = "fa fa-cube", name = T("Common.Entity.Product").Text }; - case LinkType.Category: - return new { icon = "fa fa-sitemap", name = T("Common.Entity.Category").Text }; - case LinkType.Manufacturer: - return new { icon = "far fa-building", name = T("Common.Entity.Manufacturer").Text }; - case LinkType.Topic: - return new { icon = "far fa-file", name = T("Common.Entity.Topic").Text }; - case LinkType.File: - return new { icon = "far fa-folder-open", name = T("Common.File").Text }; - case LinkType.Url: - default: - return new { icon = "fa fa-link", name = T("Common.Url").Text }; - } - } } @{ @@ -52,10 +32,10 @@ From 60188601dee0a1d6fddd3d7d93e3b26031d1897f Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 4 Apr 2019 14:03:09 +0200 Subject: [PATCH 094/460] Menu builder --- .../Migrations/MigrationsConfiguration.cs | 12 ++++++---- .../SmartStore.Services/Cms/LinkResolver.cs | 14 +++++++---- .../Cms/LinkResolverInvalidator.cs | 8 ++++--- .../Cms/LinkResolverResult.cs | 7 +++--- .../Providers/CatalogMenuItemProvider.cs | 6 ++--- .../Menus/Providers/EntityMenuItemProvider.cs | 22 ++++++++++------- .../UI/Menus/Providers/IMenuItemProvider.cs | 5 ++++ .../Menus/Providers/MenuItemProviderBase.cs | 10 ++++---- .../Menus/Providers/RouteMenuItemProvider.cs | 24 ++++++++++++------- .../Administration/Views/Menu/ItemList.cshtml | 4 ++-- 10 files changed, 71 insertions(+), 41 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 1e1d9d2434..7856993229 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -739,6 +739,14 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "This menu is required by your shop and can therefore not be deleted.", "Dieses Menü wird von Ihrem Shop benötigt und kann daher nicht gelöscht werden."); + builder.AddOrUpdate("Admin.ContentManagement.Menus.CatalogNote", + "The category tree is dynamically integrated into the menu.", + "Der Warengruppenbaum wird dynamisch in das Menü eingebunden."); + + builder.AddOrUpdate("Admin.ContentManagement.Menus.SpecifyLinkTarget", + "Please specify link target", + "Bitte Link Ziel angeben"); + builder.AddOrUpdate("Providers.MenuItems.FriendlyName.Entity", "Internal object or URL", "Internes Objekt oder URL"); builder.AddOrUpdate("Providers.MenuItems.FriendlyName.Route", "Route", "Route"); builder.AddOrUpdate("Providers.MenuItems.FriendlyName.Catalog", "Categories", "Warengruppen"); @@ -746,10 +754,6 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.ContentManagement.Menus.RouteName", "Route name", "Name der Route"); builder.AddOrUpdate("Admin.ContentManagement.Menus.RouteValues", "Route values (JSON)", "Route Werte (JSON)"); - builder.AddOrUpdate("Admin.ContentManagement.Menus.CatalogNote", - "The category tree is dynamically integrated into the menu.", - "Der Warengruppenbaum wird dynamisch in das Menü eingebunden."); - builder.AddOrUpdate("Admin.ContentManagement.Menus.SystemName", "System name", "Systemname", diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index a2ea254799..b3955df188 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -115,7 +115,8 @@ public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable(LinkResolverData data, int languageId, Expression data.Id = summary.Id != 0 ? summary.Id : data.Id; data.SubjectToAcl = summary.SubjectToAcl; data.LimitedToStores = summary.LimitedToStores; + data.PictureId = summary.PictureId; data.Status = summary.Deleted ? LinkStatus.NotFound : summary.Published ? LinkStatus.Ok : LinkStatus.Hidden; @@ -347,5 +352,6 @@ internal class ResolverEntitySummary public bool Published { get; set; } public bool SubjectToAcl { get; set; } public bool LimitedToStores { get; set; } + public int? PictureId { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs index 92e9d3798b..64bafc1831 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs @@ -25,9 +25,11 @@ public partial class LinkResolverInvalidator : DbSaveHook nameof(Topic.Title), nameof(Topic.ShortTitle), nameof(Product.Name), - nameof(Category.Published), - nameof(Product.Deleted), - nameof(StoreMapping.StoreId), + nameof(Product.Deleted), + nameof(Product.MainPictureId), + nameof(Category.Published), + nameof(Category.PictureId), + nameof(StoreMapping.StoreId), nameof(UrlRecord.IsActive), nameof(UrlRecord.Slug) }; diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs index b33ca7294c..3b20f35c99 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs @@ -25,22 +25,23 @@ public class LinkResolverResult private string _link; /// - /// The raw expression without query string + /// The raw expression without query string. /// public string Expression { get; set; } /// - /// The query string part + /// The query string part. /// public string QueryString { get; set; } public LinkType Type { get; set; } public object Value { get; set; } - public string Slug { get; set; } public LinkStatus Status { get; set; } public string Label { get; set; } public int Id { get; set; } + public int? PictureId { get; set; } + public string Slug { get; set; } public string Link { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs index adccaaef22..a2ef10739a 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/CatalogMenuItemProvider.cs @@ -35,10 +35,10 @@ public CatalogMenuItemProvider( public override void Append(MenuItemProviderRequest request) { - if (request.Origin.IsCaseInsensitiveEqual("EditMenu")) + if (request.IsMenuEditing) { - var item = ConvertToMenuItem(request.Entity); - item.BadgeText = T("Providers.MenuItems.FriendlyName.Catalog"); + var item = ConvertToMenuItem(request); + item.Summary = T("Providers.MenuItems.FriendlyName.Catalog"); item.Icon = "fa fa-sitemap"; request.Parent.Append(item); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs index 28945d78d9..fed063738c 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/EntityMenuItemProvider.cs @@ -22,14 +22,10 @@ public EntityMenuItemProvider(ILinkResolver linkResolver) protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) { - if (request.Entity.Model.IsEmpty()) - { - return; - } - // Always resolve against current store, current customer and working language. var result = _linkResolver.Resolve(request.Entity.Model); node.Value.Url = result.Link; + node.Value.ImageId = result.PictureId; if (node.Value.Text.IsEmpty()) { @@ -42,7 +38,11 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode public MenuItemRecord Entity { get; set; } + + /// + /// Inidicates whether the request is for backend menu editing. + /// + public bool IsMenuEditing => Origin.IsCaseInsensitiveEqual("EditMenu"); } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 90d37a4d99..fe2a5c8678 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -1,5 +1,4 @@ using SmartStore.Collections; -using SmartStore.Core.Domain.Cms; using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.UI @@ -15,7 +14,7 @@ public virtual void Append(MenuItemProviderRequest request) Guard.NotNull(request.Entity, nameof(request.Entity)); // Add group header item. - if (request.Entity.BeginGroup && !request.Origin.IsCaseInsensitiveEqual("EditMenu")) + if (request.Entity.BeginGroup && !request.IsMenuEditing) { request.Parent.Append(new MenuItem { @@ -24,7 +23,7 @@ public virtual void Append(MenuItemProviderRequest request) }); } - var node = request.Parent.Append(ConvertToMenuItem(request.Entity)); + var node = request.Parent.Append(ConvertToMenuItem(request)); ApplyLink(request, node); } @@ -34,8 +33,9 @@ public virtual void Append(MenuItemProviderRequest request) ///
/// The entity to convert. /// Menu item. - protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) + protected virtual MenuItem ConvertToMenuItem(MenuItemProviderRequest request) { + var entity = request.Entity; var title = entity.GetLocalized(x => x.Title); var shortDescription = entity.GetLocalized(x => x.ShortDescription); @@ -73,7 +73,7 @@ protected virtual MenuItem ConvertToMenuItem(MenuItemRecord entity) menuItem.LinkHtmlAttributes.Add("id", entity.HtmlId); } - if (entity.Icon.HasValue()) + if (entity.Icon.HasValue() && !request.IsMenuEditing) { menuItem.Icon = IconExplorer.GetIconByName(entity.Icon).GetCssClass(entity.Style); } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs index 4d5b1b8eec..0231fa4478 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/RouteMenuItemProvider.cs @@ -17,12 +17,6 @@ public RouteMenuItemProvider() protected override void ApplyLink(MenuItemProviderRequest request, TreeNode node) { - if (request.Origin.IsCaseInsensitiveEqual("EditMenu")) - { - node.Value.BadgeText = T("Providers.MenuItems.FriendlyName.Route"); - node.Value.Icon = "fas fa-directions"; - } - try { if (request.Entity.Model.HasValue()) @@ -46,7 +40,19 @@ protected override void ApplyLink(MenuItemProviderRequest request, TreeNode @if (item.Icon.HasValue()) { - + } @if (item.Text.HasValue()) @@ -61,7 +61,7 @@ else } else { - [@T("Common.NoTitle")] + [@T(item.ResKey.NullEmpty() ?? "Common.NoTitle")] } From fdb5ee0b84baa836ecf5b4bac5ba531da67ec948 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 4 Apr 2019 15:40:22 +0200 Subject: [PATCH 095/460] Menu builder --- .../Infrastructure/AdminMenu.cs | 97 +++++++++++++++++++ .../Administration/SmartStore.Admin.csproj | 1 + 2 files changed, 98 insertions(+) create mode 100644 src/Presentation/SmartStore.Web/Administration/Infrastructure/AdminMenu.cs diff --git a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AdminMenu.cs b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AdminMenu.cs new file mode 100644 index 0000000000..75653c4ce2 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AdminMenu.cs @@ -0,0 +1,97 @@ +using System; +using SmartStore.Collections; +using SmartStore.Web.Framework.UI; + +namespace SmartStore.Admin.Infrastructure +{ + public class AdminMenu : MenuBase + { + public override string Name => "Admin"; + + public override bool ApplyPermissions => true; + + protected override string GetCacheKey() + { + var cacheKey = "{0}-{1}".FormatInvariant( + Services.WorkContext.WorkingLanguage.Id, + Services.WorkContext.CurrentCustomer.GetRolesIdent()); + + return cacheKey; + } + + protected override TreeNode Build() + { + var xmlSiteMap = new Telerik.Web.Mvc.XmlSiteMap(); + xmlSiteMap.LoadFrom("~/Administration/sitemap.config"); + + var rootNode = ConvertSitemapNodeToMenuItemNode(xmlSiteMap.RootNode); + return rootNode; + } + + protected virtual TreeNode ConvertSitemapNodeToMenuItemNode(Telerik.Web.Mvc.SiteMapNode node) + { + var item = new MenuItem(); + var treeNode = new TreeNode(item); + + var id = node.Attributes.ContainsKey("id") + ? node.Attributes["id"] as string + : null; + + treeNode.Id = id; + + if (node.RouteName.HasValue()) + { + item.RouteName = node.RouteName; + } + else if (node.ActionName.HasValue() && node.ControllerName.HasValue()) + { + item.ActionName = node.ActionName; + item.ControllerName = node.ControllerName; + } + else if (node.Url.HasValue()) + { + item.Url = node.Url; + } + item.RouteValues = node.RouteValues; + + item.Id = id; + item.Visible = node.Visible; + item.Text = node.Title; + item.Attributes.Merge(node.Attributes); + + if (node.Attributes.ContainsKey("permissionNames")) + { + item.PermissionNames = node.Attributes["permissionNames"] as string; + } + + if (node.Attributes.ContainsKey("resKey")) + { + item.ResKey = node.Attributes["resKey"] as string; + } + + if (node.Attributes.ContainsKey("iconClass")) + { + item.Icon = node.Attributes["iconClass"] as string; + } + + if (node.Attributes.ContainsKey("imageUrl")) + { + item.ImageUrl = node.Attributes["imageUrl"] as string; + } + + if (node.Attributes.ContainsKey("isGroupHeader")) + { + item.IsGroupHeader = Boolean.Parse(node.Attributes["isGroupHeader"] as string); + } + + // Iterate children recursively. + foreach (var childNode in node.ChildNodes) + { + var childTreeNode = ConvertSitemapNodeToMenuItemNode(childNode); + treeNode.Append(childTreeNode); + } + + return treeNode; + } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj index 325495ae3e..0cbde3c50a 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -231,6 +231,7 @@ + From 067b0864bc21ed10b881cc529cbc77671c2520e4 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 4 Apr 2019 19:53:55 +0200 Subject: [PATCH 096/460] Loading backend menu through menu service --- .../Administration/Controllers/CommonController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index c859c14f95..85c638e01e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -72,7 +72,7 @@ public class CommonController : AdminControllerBase private readonly IDbCache _dbCache; private readonly ITaskScheduler _taskScheduler; private readonly ICommonServices _services; - private readonly Lazy _siteMapService; + private readonly Lazy _menuService; public CommonController( Lazy paymentService, @@ -92,7 +92,7 @@ public CommonController( IDbCache dbCache, ITaskScheduler taskScheduler, ICommonServices services, - Lazy siteMapService) + Lazy menuService) { _paymentService = paymentService; _shippingService = shippingService; @@ -111,7 +111,7 @@ public CommonController( _dbCache = dbCache; _taskScheduler = taskScheduler; _services = services; - _siteMapService = siteMapService; + _menuService = menuService; } #region Navbar & Menu @@ -134,7 +134,7 @@ public ActionResult Navbar() [ChildActionOnly] public ActionResult Menu() { - var rootNode = _siteMapService.Value.GetRootNode("admin"); + var rootNode = _menuService.Value.GetRootNode("Admin"); return PartialView(rootNode); } From 662202731d0beccaeb93753719c6cfabe95cfc7d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 4 Apr 2019 20:38:36 +0200 Subject: [PATCH 097/460] Minor change --- .../Messages/MessageModelProvider.cs | 2 +- .../Administration/Views/Home/About.cshtml | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs index 7d18234bba..8cc14ed9d5 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs @@ -469,7 +469,7 @@ protected virtual object CreateModelPart(Store part, MessageContext messageConte var host = messageContext.BaseUri.ToString(); var logoInfo = _services.PictureService.GetPictureInfo(messageContext.Store.LogoPictureId); - // Issue: https://github.com/smartstoreag/SmartStoreNET/issues/1321 + // Issue: https://github.com/smartstore/SmartStoreNET/issues/1321 var m = new Dictionary { diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Home/About.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Home/About.cshtml index 56aa0998af..60802bf5ed 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Home/About.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Home/About.cshtml @@ -24,9 +24,9 @@

Version @SmartStore.Core.SmartStoreVersion.CurrentFullVersion
- Copyright © 1999-2018 SmartStore AG
+ Copyright © 1999-2019 SmartStore AG
www.smartstore.com, - github.com/smartstoreag/SmartStoreNET + github.com/smartstore/SmartStoreNET

@@ -98,15 +98,6 @@ @MSPLLicense() -

  • -
    - DotNetZip -
    - - @MSPLLicense() -
  • Entity Framework From 911264eff088d834336c14c099b6c43b5ae0b9d8 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 4 Apr 2019 21:42:25 +0200 Subject: [PATCH 098/460] Fixed some problems for touch devices --- .../Scripts/smartstore.megamenu.js | 12 ++------ .../Scripts/smartstore.system.js | 30 +++++++++++++++++-- .../Scripts/smartstore.touchevents.js | 3 +- .../Themes/Flex/Content/_megamenu.scss | 8 ++++- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js b/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js index 00fa10314c..a5aacb1d27 100644 --- a/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js +++ b/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js @@ -77,9 +77,9 @@ navElems.on("click", function (e) { var link = $(this).find(".nav-link"); - var opendMenuSelector = $(".nav-item.active .nav-link").data("target"); + var openedMenuSelector = $(".nav-item.active .nav-link").data("target"); - if (opendMenuSelector != link.data("target")) { + if (openedMenuSelector != link.data("target")) { e.preventDefault(); closeNow($(".nav-item.active .nav-link")); tryOpen(link); @@ -201,11 +201,6 @@ } megamenuContainer.evenIfHidden(function (el) { - var scrollCorrection = null; - var lastVisibleElem = null; - var firstVisibleElem = null; - var isFirstItemVisible = true; - var isLastItemVisible = false; megamenuContainer.find('ul').wrap(' +
    + @Html.SmartLabelFor(model => model.PaymentMethods) + @Html.DropDownList("PaymentMethods", Model.AvailablePaymentMethods, null, new { multiple = "multiple", @class = "form-control" }) +
    @if (Model.AvailableStores.Count > 1) {
    @@ -85,7 +89,6 @@ {
    } -
    @Html.SmartLabelFor(model => model.OrderStatusIds) @Html.DropDownList("OrderStatusIds", Model.AvailableOrderStatuses, null, new { multiple = "multiple", @class = "form-control" }) @@ -252,7 +255,8 @@ ShippingStatusIds: jQuery.makeArray($('#@Html.FieldIdFor(model => model.ShippingStatusIds)').val()).join(), StoreId: $('#@Html.FieldIdFor(model => model.StoreId)').val(), OrderGuid: $('#@Html.FieldIdFor(model => model.OrderGuid)').val(), - OrderNumber: $('#@Html.FieldIdFor(model => model.OrderNumber)').val() + OrderNumber: $('#@Html.FieldIdFor(model => model.OrderNumber)').val(), + PaymentMethods: jQuery.makeArray($('#@Html.FieldIdFor(model => model.PaymentMethods)').val()).join(), }; e.data = searchModel; } From f202ad092976ca4eb20f313987cb4bbc1542f7f9 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Fri, 5 Apr 2019 17:44:03 +0200 Subject: [PATCH 102/460] MegaMenu: Minor fixes --- .../Scripts/smartstore.megamenu.js | 35 +++++++++++++------ .../Themes/Flex/Content/_megamenu.scss | 25 ++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js b/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js index a5aacb1d27..0dd3766b52 100644 --- a/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js +++ b/src/Presentation/SmartStore.Web/Scripts/smartstore.megamenu.js @@ -134,7 +134,8 @@ }); } - function alignDrop(popper, drop, container) { + function alignDrop(popper, drop, container) { + var nav = $(".navbar-nav", container), left, right, @@ -142,8 +143,15 @@ dropWidth = drop.width(), containerWidth = container.width(); - if (!rtl) { - left = Math.ceil(popper.position().left + parseInt(nav.css('margin-left'))); + if (!rtl) { + + if (!Modernizr.touchevents) { + left = Math.ceil(popper.position().left + parseInt(nav.css('margin-left'))); + } + else { + left = Math.ceil(popper.position().left + nav.position().left); + } + right = "auto"; if (left < 0) { @@ -155,8 +163,14 @@ } } else { - left = "auto"; - right = Math.ceil(containerWidth - (popper.position().left + popperWidth)); + left = "auto"; + + if (!Modernizr.touchevents) { + right = Math.ceil(containerWidth - (popper.position().left + popperWidth)); + } + else { + right = Math.ceil(popper.position().right + nav.position().left); + } if (right < 0) { right = 0; @@ -179,14 +193,15 @@ // jQuery does not accept "!important" drop[0].style.setProperty('left', left, 'important'); - drop[0].style.setProperty('right', right, 'important'); + drop[0].style.setProperty('right', right, 'important'); } // correct dropdown position if (isSimple) { - var event = Modernizr.touchevents ? "click" : "mouseenter"; + var event = Modernizr.touchevents ? "click" : "mouseenter"; navElems.on(event, function (e) { + var navItem = $(this); var targetSelector = navItem.find(".nav-link").data("target"); if (!targetSelector) @@ -302,7 +317,7 @@ if (Modernizr.touchevents) { navWidth = nav.width(); var offset = nav.position().left; - curMarginStart = rtl ? (offset - 1) * -1 : offset; + curMarginStart = rtl ? (offset - 1) * -1 : offset; } else { navWidth = megamenu.width(); @@ -330,10 +345,10 @@ if (Modernizr.touchevents) { megamenu.tapmove(function () { closeNow($(".nav-item.active .nav-link")); - updateNavState(); + updateNavState(); }); } - + function onPageResized() { updateNavState(); diff --git a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_megamenu.scss b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_megamenu.scss index 121df72599..15edcf35a4 100644 --- a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_megamenu.scss +++ b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_megamenu.scss @@ -137,12 +137,18 @@ $mixed-megamenu-bg: $megamenu-bg; } .touchevents .megamenu .nav-slider { overflow-x: auto !important; - overflow: -moz-scrollbars-none; - -ms-overflow-style: none; } -.touchevents .megamenu .nav-slider::-webkit-scrollbar { - width: 0 !important; - height: 0 !important; +@include media-breakpoint-up(xl) { + .touchevents .megamenu .nav-slider { + overflow-x: auto !important; + overflow: -moz-scrollbars-none; + -ms-overflow-style: none; + } + + .touchevents .megamenu .nav-slider::-webkit-scrollbar { + width: 0 !important; + height: 0 !important; + } } // horizontal navbar navigation buttons @@ -158,11 +164,12 @@ $mixed-megamenu-bg: $megamenu-bg; &.alpha { left: 0; a { padding-right: 10px; } - } + } + &.omega { right: 0; a { padding-left: 10px; } - } + } .megamenu-nav-btn { border: none; @@ -172,7 +179,7 @@ $mixed-megamenu-bg: $megamenu-bg; padding: 0px; width: 40px; height: 98%; - box-shadow: none !important; + box-shadow: none !important; } i { @@ -188,7 +195,7 @@ $mixed-megamenu-bg: $megamenu-bg; opacity: 0.6; &:hover { opacity: 0.85; } - &:active { opacity: 1; } + &:active { opacity: 1; } } } .megamenu.megamenu-blend--next .megamenu-nav--next, From 029686c48d9c116612efe764e2a0d419bf421650 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 5 Apr 2019 22:00:19 +0200 Subject: [PATCH 103/460] Minor model renaming --- .../SmartStore.Web.Framework.csproj | 2 +- .../Components/Menu/{NavigationModel.cs => MenuModel.cs} | 5 ++--- .../SmartStore.Web/Controllers/CatalogController.cs | 8 ++++---- .../SmartStore.Web/Controllers/CatalogHelper.cs | 4 ++-- .../SmartStore.Web/Controllers/CommonController.cs | 2 +- .../Models/Common/{MenuModel.cs => MenuBarModel.cs} | 2 +- src/Presentation/SmartStore.Web/SmartStore.Web.csproj | 2 +- .../Views/Catalog/Partials/CatalogMenu.Navbar.cshtml | 2 +- .../Views/Catalog/Partials/CatalogMenu.cshtml | 2 +- .../Views/Catalog/Partials/CategoryMenu.cshtml | 2 +- .../Views/Catalog/Partials/OffCanvasMenuCategories.cshtml | 2 +- .../SmartStore.Web/Views/Common/Partials/Menu.cshtml | 2 +- 12 files changed, 17 insertions(+), 18 deletions(-) rename src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/{NavigationModel.cs => MenuModel.cs} (86%) rename src/Presentation/SmartStore.Web/Models/Common/{MenuModel.cs => MenuBarModel.cs} (92%) diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index c9cf056f8b..8097f58c9e 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -334,7 +334,7 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NavigationModel.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs similarity index 86% rename from src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NavigationModel.cs rename to src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs index 6941f23b5a..118c4390e7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NavigationModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - public class NavigationModel + public class MenuModel { private TreeNode _selectedNode; private bool _seekedSelectedNode; diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs index 427f941226..ff7a6fc196 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs @@ -275,13 +275,13 @@ public ActionResult Category(int categoryId, CatalogSearchQuery query) [ChildActionOnly] public ActionResult CategoryMenu(int currentCategoryId, int currentProductId = 0) { - var model = _helper.PrepareCategoryNavigationModel(currentCategoryId, currentProductId); + var model = _helper.PrepareCategoryMenuModel(currentCategoryId, currentProductId); return PartialView(model); } public ActionResult CatalogMenu(int currentCategoryId, int currentProductId = 0) { - var model = _helper.PrepareCategoryNavigationModel(currentCategoryId, currentProductId); + var model = _helper.PrepareCategoryMenuModel(currentCategoryId, currentProductId); return PartialView(model); } @@ -922,11 +922,11 @@ public ActionResult ClearCompareListAjax() /// EntityId of the category to which should be navigated in the OffCanvasMenu /// EntityId of the category that is currently displayed in the shop (WebViewPage.CurrentCategoryId) /// EntityId of the product that is currently displayed in the shop (WebViewPage.CurrentProductId) - /// PartialView with NavigationModel + /// PartialView with MenuModel [HttpPost] public ActionResult OffCanvasMenuCategories(int categoryId, int currentCategoryId, int currentProductId) { - var model = _helper.PrepareCategoryNavigationModel(currentCategoryId, currentProductId); + var model = _helper.PrepareCategoryMenuModel(currentCategoryId, currentProductId); ViewBag.SelectedNode = categoryId == 0 ? model.Root : ViewBag.SelectedNode = model.Root.SelectNodeById(categoryId) ?? model.Root.SelectNode(x => x.Value.EntityId == categoryId); diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index 339934b3d0..7bd0aac004 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -1359,13 +1359,13 @@ List Execute() } } - public NavigationModel PrepareCategoryNavigationModel(int currentCategoryId, int currentProductId) + public MenuModel PrepareCategoryMenuModel(int currentCategoryId, int currentProductId) { var root = GetCategoryMenu(); var breadcrumb = GetCategoryBreadCrumb(currentCategoryId, currentProductId); - var model = new NavigationModel + var model = new MenuModel { Root = root, Path = breadcrumb diff --git a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs index 013a173449..73e72c76cb 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs @@ -466,7 +466,7 @@ public ActionResult Menu() { var customer = _services.WorkContext.CurrentCustomer; - var model = new MenuModel + var model = new MenuBarModel { RecentlyAddedProductsEnabled = _catalogSettings.RecentlyAddedProductsEnabled, NewsEnabled = _newsSettings.Enabled, diff --git a/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs b/src/Presentation/SmartStore.Web/Models/Common/MenuBarModel.cs similarity index 92% rename from src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs rename to src/Presentation/SmartStore.Web/Models/Common/MenuBarModel.cs index 5d7ad16806..94085eca7b 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/MenuBarModel.cs @@ -2,7 +2,7 @@ namespace SmartStore.Web.Models.Common { - public partial class MenuModel : ModelBase + public partial class MenuBarModel : ModelBase { public bool NewsEnabled { get; set; } public bool BlogEnabled { get; set; } diff --git a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj index 107778e2f6..b6054b7797 100644 --- a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj +++ b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj @@ -401,7 +401,7 @@ - + diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml index ae307c7685..28ead8c11c 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml @@ -1,4 +1,4 @@ -@model NavigationModel +@model MenuModel @using SmartStore.Collections; @{ if (IsStoreClosed) diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml index f7c41b5f99..f7a95faade 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml @@ -1,4 +1,4 @@ -@model NavigationModel +@model MenuModel @using SmartStore.Collections; @{ diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CategoryMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CategoryMenu.cshtml index df9afc28b6..a7536679f8 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CategoryMenu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CategoryMenu.cshtml @@ -1,4 +1,4 @@ -@model NavigationModel +@model MenuModel @using SmartStore.Collections; diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/OffCanvasMenuCategories.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/OffCanvasMenuCategories.cshtml index 37a55196d0..9056a6f8b9 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/OffCanvasMenuCategories.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/OffCanvasMenuCategories.cshtml @@ -1,4 +1,4 @@ -@model NavigationModel +@model MenuModel @using SmartStore.Web.Models.Catalog; @using SmartStore.Collections; diff --git a/src/Presentation/SmartStore.Web/Views/Common/Partials/Menu.cshtml b/src/Presentation/SmartStore.Web/Views/Common/Partials/Menu.cshtml index 14ed257303..87ac39117b 100644 --- a/src/Presentation/SmartStore.Web/Views/Common/Partials/Menu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Common/Partials/Menu.cshtml @@ -1,4 +1,4 @@ -@model MenuModel +@model MenuBarModel @using SmartStore.Web.Models.Common; @{ string accountUrl = Model.IsAuthenticated ? Url.Action("Info", "Customer") : Url.RouteUrl("Login", new { ReturnUrl = Request.RawUrl }); From c5f69ffb7654a188748fa40e24ebb0bfd50950d3 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 5 Apr 2019 22:23:50 +0200 Subject: [PATCH 104/460] Remove invalid path chars from menu system name --- .../SmartStore.Services/Cms/Menus/MenuStorage.cs | 4 +++- .../Administration/Controllers/MenuController.cs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 48a873fbeb..39e6fa2fef 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -43,6 +43,8 @@ public virtual void InsertMenu(MenuRecord menu) { Guard.NotNull(menu, nameof(MenuRecord)); + menu.SystemName = menu.SystemName.ToValidPath(); + _menuRepository.Insert(menu); var systemNames = GetMenuSystemNames(false); @@ -56,7 +58,7 @@ public virtual void UpdateMenu(MenuRecord menu) { Guard.NotNull(menu, nameof(MenuRecord)); - var oldSystemName = menu.SystemName; + menu.SystemName = menu.SystemName.ToValidPath(); var modProps = _services.DbContext.GetModifiedProperties(menu); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs index 5a7731d0e4..5c233479b2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MenuController.cs @@ -120,7 +120,7 @@ public ActionResult Create() return View(model); } - [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] + [HttpPost, ValidateInput(false), ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Create(MenuRecordModel model, bool continueEditing, FormCollection form) { if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageMenus)) @@ -173,7 +173,7 @@ public ActionResult Edit(int id) return View(model); } - [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] + [HttpPost, ValidateInput(false), ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Edit(MenuRecordModel model, bool continueEditing, FormCollection form) { if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageMenus)) @@ -274,7 +274,7 @@ public ActionResult CreateItem(string providerName, int menuId, int parentItemId } // Do not name parameter "model" because of property of same name. - [HttpPost, ParameterBasedOnFormName("save-item-continue", "continueEditing")] + [HttpPost, ValidateInput(false), ParameterBasedOnFormName("save-item-continue", "continueEditing")] public ActionResult CreateItem(MenuItemRecordModel itemModel, bool continueEditing) { if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageMenus)) @@ -334,7 +334,7 @@ public ActionResult EditItem(int id) } // Do not name parameter "model" because of property of same name. - [HttpPost, ParameterBasedOnFormName("save-item-continue", "continueEditing")] + [HttpPost, ValidateInput(false), ParameterBasedOnFormName("save-item-continue", "continueEditing")] public ActionResult EditItem(MenuItemRecordModel itemModel, bool continueEditing) { if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageMenus)) From ae64e2d765b62b9c612bb83e8be7c9ad0bc08b7c Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 4 Apr 2019 23:04:36 +0200 Subject: [PATCH 105/460] Class rename & refactoring: MostRecentlyUsedList > TrimmedBuffer --- .../Collections/MostRecentlyUsedList.cs | 101 ----------------- .../Collections/TrimmedBuffer.cs | 107 ++++++++++++++++++ .../Controllers/CategoryController.cs | 2 +- .../Controllers/ManufacturerController.cs | 2 +- .../Controllers/ProductController.cs | 24 ++-- 5 files changed, 125 insertions(+), 111 deletions(-) delete mode 100644 src/Libraries/SmartStore.Core/Collections/MostRecentlyUsedList.cs create mode 100644 src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs diff --git a/src/Libraries/SmartStore.Core/Collections/MostRecentlyUsedList.cs b/src/Libraries/SmartStore.Core/Collections/MostRecentlyUsedList.cs deleted file mode 100644 index 114c4e24bf..0000000000 --- a/src/Libraries/SmartStore.Core/Collections/MostRecentlyUsedList.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace SmartStore.Collections -{ - public class MostRecentlyUsedList : IEnumerable - { - private readonly int _maxSize; - private readonly List _mru; - - public MostRecentlyUsedList(int maxSize) - { - _maxSize = maxSize; - _mru = new List(); - } - - public MostRecentlyUsedList(IEnumerable collection, int maxSize) - { - _maxSize = maxSize; - _mru = collection.ToList(); - - Normalize(); - } - - public MostRecentlyUsedList(string collection, int maxSize) - { - _maxSize = maxSize; - _mru = collection.SplitSafe(Delimiter).Cast().ToList(); - - Normalize(); - } - - public MostRecentlyUsedList(IEnumerable collection, T newItem, int maxSize) - { - _maxSize = maxSize; - _mru = collection.ToList(); - - Add(newItem); - } - - public MostRecentlyUsedList(string collection, T newItem, int maxSize) - { - _maxSize = maxSize; - _mru = collection.SplitSafe(Delimiter).Cast().Distinct().ToList(); - - Add(newItem); - } - - public static string Delimiter { get { return ";"; } } - - private void Normalize() - { - if (_maxSize >= 0) - { - while (_mru.Count > _maxSize) - _mru.RemoveAt(_mru.Count - 1); - } - } - - public IEnumerator GetEnumerator() - { - return _mru.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _mru.GetEnumerator(); - } - - public override string ToString() - { - return string.Join(Delimiter, _mru); - } - - public T this[int key] - { - get - { - return _mru[key]; - } - set - { - _mru[key] = value; - } - } - - public int Count { get { return _mru.Count; } } - - public void Add(T item) - { - int i = _mru.IndexOf(item); - if (i > -1) - _mru.RemoveAt(i); - - _mru.Insert(0, item); - - Normalize(); - } - } -} diff --git a/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs new file mode 100644 index 0000000000..e8ef948327 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs @@ -0,0 +1,107 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace SmartStore.Collections +{ + public class TrimmedBuffer : ICollection + { + private readonly int _maxSize; + private readonly List _list; + + public TrimmedBuffer(int maxSize) + : this(Enumerable.Empty(), default(T), maxSize) + { + } + + public TrimmedBuffer(IEnumerable collection, int maxSize) + : this(collection, default(T), maxSize) + { + } + + public TrimmedBuffer(string collection, int maxSize) + : this(collection.Convert>().Distinct(), default(T), maxSize) + { + } + + public TrimmedBuffer(string collection, T newItem, int maxSize) + : this(collection.Convert>().Distinct(), newItem, maxSize) + { + } + + public TrimmedBuffer(IEnumerable collection, T newItem, int maxSize) + { + Guard.NotNull(collection, nameof(collection)); + Guard.IsPositive(maxSize, nameof(maxSize)); + + _maxSize = maxSize; + _list = new List(collection); + + if (newItem != null) + { + Add(newItem); + } + + Trim(); + } + + private void Trim() + { + if (_maxSize >= 0) + { + while (_list.Count > _maxSize) + _list.RemoveAt(_list.Count - 1); + } + } + + public T this[int key] + { + get + { + return _list[key]; + } + set + { + _list[key] = value; + } + } + + public void Add(T item) + { + var i = _list.IndexOf(item); + if (i > -1) + _list.RemoveAt(i); + + _list.Insert(0, item); + + Trim(); + } + + public void Clear() => _list.Clear(); + + public bool Contains(T item) => _list.Contains(item); + + public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); + + public bool Remove(T item) => _list.Remove(item); + + public int Count => _list.Count; + + public bool IsReadOnly => false; + + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _list.GetEnumerator(); + } + + public override string ToString() + { + return string.Join(";", _list); + } + } +} diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs index e34b95b077..b389aac80f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs @@ -317,7 +317,7 @@ from c in categories var mainList = query.ToList(); - var mruList = new MostRecentlyUsedList( + var mruList = new TrimmedBuffer( _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), _catalogSettings.MostRecentlyUsedCategoriesMaxSize) .Reverse() diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs index 1411cbb70d..6b58b74b47 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs @@ -207,7 +207,7 @@ public ActionResult AllManufacturers(string label, int selectedId) var mainList = list.ToList(); - var mruList = new MostRecentlyUsedList( + var mruList = new TrimmedBuffer( _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), _catalogSettings.MostRecentlyUsedManufacturersMaxSize) .Reverse() diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index b6063da050..e6444f7048 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -1556,8 +1556,10 @@ public ActionResult ProductCategoryInsert(GridCommand command, ProductModel.Prod _categoryService.InsertProductCategory(productCategory); - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), - model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); + var mru = new TrimmedBuffer( + _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), + model.Category, + _catalogSettings.MostRecentlyUsedCategoriesMaxSize); _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); } @@ -1582,8 +1584,10 @@ public ActionResult ProductCategoryUpdate(GridCommand command, ProductModel.Prod if (categoryChanged) { - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), - model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); + var mru = new TrimmedBuffer( + _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), + model.Category, + _catalogSettings.MostRecentlyUsedCategoriesMaxSize); _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); } @@ -1664,8 +1668,10 @@ public ActionResult ProductManufacturerInsert(GridCommand command, ProductModel. _manufacturerService.InsertProductManufacturer(productManufacturer); - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), - model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); + var mru = new TrimmedBuffer( + _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), + model.Manufacturer, + _catalogSettings.MostRecentlyUsedManufacturersMaxSize); _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); } @@ -1691,8 +1697,10 @@ public ActionResult ProductManufacturerUpdate(GridCommand command, ProductModel. if (manufacturerChanged) { - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), - model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); + var mru = new TrimmedBuffer( + _workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), + model.Manufacturer, + _catalogSettings.MostRecentlyUsedManufacturersMaxSize); _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); } From e1dc78a979e4efd916fd3a0b691d2e38a5d64206 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 5 Apr 2019 00:03:01 +0200 Subject: [PATCH 106/460] Implemented SyncedCollection --- .../Collections/ConcurrentMultimap.cs | 79 +++---- .../Collections/SyncedCollection.cs | 192 ++++++++++++++++++ .../Collections/TrimmedBuffer.cs | 8 +- .../Extensions/CollectionExtensions.cs | 18 +- .../SmartStore.Core/SmartStore.Core.csproj | 3 +- 5 files changed, 239 insertions(+), 61 deletions(-) create mode 100644 src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs diff --git a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs index c279c2b2f2..86d5b00249 100644 --- a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs +++ b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs @@ -16,9 +16,9 @@ namespace SmartStore.Collections /// The type of value. [JsonConverter(typeof(ConcurrentMultiMapConverter))] [Serializable] - public class ConcurrentMultimap : IEnumerable>> + public class ConcurrentMultimap : IEnumerable>> { - private readonly ConcurrentDictionary> _dict; + private readonly ConcurrentDictionary> _dict; private readonly Func, ICollection> _collectionCreator; public ConcurrentMultimap() @@ -51,25 +51,28 @@ public ConcurrentMultimap( Func, ICollection> collectionCreator) { _collectionCreator = collectionCreator; - _dict = new ConcurrentDictionary>( + _dict = new ConcurrentDictionary>( ConvertItems(items), comparer ?? EqualityComparer.Default); } - private IEnumerable>> ConvertItems(IEnumerable>> items) + private IEnumerable>> ConvertItems(IEnumerable>> items) { if (items == null) yield break; foreach (var item in items) { - yield return new KeyValuePair>(item.Key, CreateCollection(item.Value)); + yield return new KeyValuePair>(item.Key, CreateCollection(item.Value)); } } - protected virtual ICollection CreateCollection(IEnumerable values) + protected virtual SyncedCollection CreateCollection(IEnumerable values) { - return (_collectionCreator ?? Multimap.DefaultCollectionCreator)(values ?? Enumerable.Empty()); + var creator = _collectionCreator ?? Multimap.DefaultCollectionCreator; + var col = creator(values ?? Enumerable.Empty()); + + return col.AsSynchronized(); } /// @@ -111,7 +114,7 @@ public int ValueCount(TKey key) /// Gets the collection of values stored under the specified key. /// /// The key. - public virtual ICollection this[TKey key] + public virtual SyncedCollection this[TKey key] { get { @@ -129,11 +132,11 @@ public virtual ICollection Keys } /// - /// Gets all value collections as readonly to prevent non-blocking list operations. + /// Gets all value collections. /// - public virtual IEnumerable> Values + public virtual ICollection> Values { - get { return _dict.Values.Select(x => x.AsReadOnly()); } + get { return _dict.Values; } } /// @@ -145,7 +148,7 @@ public virtual void TryAdd(TKey key, TValue value) { if (!GetOrCreateValues(key, new[] { value }, out var col)) { - ExecuteCollectionAction(col, x => x.Add(value)); + col.Add(value); } } @@ -160,7 +163,7 @@ public virtual void TryAddRange(TKey key, IEnumerable values) if (!GetOrCreateValues(key, values, out var col)) { - ExecuteCollectionAction(col, x => x.AddRange(values)); + col.AddRange(values); } } @@ -178,14 +181,11 @@ public virtual bool TryRemove(TKey key, TValue value) var col = _dict[key]; var removed = false; - ExecuteCollectionAction(col, x => + removed = col.Remove(value); + if (col.Count == 0) { - removed = col.Remove(value); - if (col.Count == 0) - { - _dict.TryRemove(key, out _); - } - }); + _dict.TryRemove(key, out _); + } return removed; } @@ -204,14 +204,7 @@ public virtual bool TryRemoveRange(TKey key, IEnumerable values) if (_dict.TryGetValue(key, out var col)) { - ExecuteCollectionAction(col, x => - { - foreach (var value in values) - { - if (col.Remove(value)) - numRemoved++; - } - }); + numRemoved = col.RemoveRange(values); } return numRemoved > 0; @@ -222,7 +215,7 @@ public virtual bool TryRemoveRange(TKey key, IEnumerable values) /// /// The key. /// True if any such values existed; otherwise false. - public virtual bool TryRemoveAll(TKey key, out ICollection collection) + public virtual bool TryRemoveAll(TKey key, out SyncedCollection collection) { return _dict.TryRemove(key, out collection); } @@ -269,13 +262,13 @@ IEnumerator IEnumerable.GetEnumerator() /// Returns an enumerator that iterates through the multimap. ///
  • /// An object that can be used to iterate through the multimap. - public virtual IEnumerator>> GetEnumerator() + public virtual IEnumerator>> GetEnumerator() { foreach (var pair in _dict) yield return pair; } - private bool GetOrCreateValues(TKey key, IEnumerable initial, out ICollection col) + private bool GetOrCreateValues(TKey key, IEnumerable initial, out SyncedCollection col) { // Return true when created var created = false; @@ -289,30 +282,6 @@ private bool GetOrCreateValues(TKey key, IEnumerable initial, out IColle return created; } - private void ExecuteCollectionAction(ICollection col, Action> action) - { - bool lockTaken = false; - try - { - var isBlocking = col is SynchronizedCollection; - - if (!isBlocking) - { - // Don't take lock on an already blocking collection. - Monitor.Enter(col, ref lockTaken); - } - - action(col); - } - finally - { - if (lockTaken) - { - Monitor.Exit(col); - } - } - } - #region Static members public static ConcurrentMultimap CreateFromLookup(ILookup source) diff --git a/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs b/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs new file mode 100644 index 0000000000..089d9090e4 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace SmartStore.Collections +{ + public sealed class SyncedCollection : ICollection + { + // INFO: Don't call it SynchronizedCollection because of framework dupe. + private readonly ICollection _col; + + public SyncedCollection(ICollection wrappedCollection) + : this(wrappedCollection, new object()) + { + } + + public SyncedCollection(ICollection wrappedCollection, object syncRoot) + { + Guard.NotNull(wrappedCollection, nameof(wrappedCollection)); + Guard.NotNull(syncRoot, nameof(syncRoot)); + + _col = wrappedCollection; + SyncRoot = syncRoot; + } + + public object SyncRoot { get; } + + public void AddRange(IEnumerable collection) + { + lock (SyncRoot) + { + _col.AddRange(collection); + } + } + + public void Insert(int index, T item) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.Insert(index, item); + } + } + + throw new NotSupportedException(); + } + + public void InsertRange(int index, IEnumerable values) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.InsertRange(index, values); + } + } + + throw new NotSupportedException(); + } + + public int RemoveRange(IEnumerable values) + { + int numRemoved = 0; + + lock (SyncRoot) + { + foreach (var value in values) + { + if (_col.Remove(value)) + numRemoved++; + } + } + + return numRemoved; + } + + public void RemoveRange(int index, int count) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.RemoveRange(index, count); + } + } + + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + lock (SyncRoot) + { + if (_col is List list) + { + list.RemoveAt(index); + } + else + { + var item = _col.ElementAtOrDefault(index); + if (item != null) + { + _col.Remove(item); + } + } + } + } + + public T this[int index] + { + get + { + lock (SyncRoot) + { + return _col.ElementAt(index); + } + } + } + + #region ICollection + + public int Count + { + get + { + lock (SyncRoot) + { + return _col.Count(); + } + } + } + + public bool IsReadOnly => _col.IsReadOnly; + + public void Add(T item) + { + lock (SyncRoot) + { + _col.Add(item); + } + } + + public void Clear() + { + lock (SyncRoot) + { + _col.Clear(); + } + } + + public bool Contains(T item) + { + lock (SyncRoot) + { + return _col.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + _col.CopyTo(array, arrayIndex); + } + } + + public bool Remove(T item) + { + lock (SyncRoot) + { + return _col.Remove(item); + } + } + + public IEnumerator GetEnumerator() + { + lock (SyncRoot) + { + return _col.GetEnumerator(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _col.GetEnumerator(); + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs index e8ef948327..ae0edfb907 100644 --- a/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs +++ b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs @@ -4,7 +4,7 @@ namespace SmartStore.Collections { - public class TrimmedBuffer : ICollection + public sealed class TrimmedBuffer : ICollection { private readonly int _maxSize; private readonly List _list; @@ -54,15 +54,15 @@ private void Trim() } } - public T this[int key] + public T this[int index] { get { - return _list[key]; + return _list[index]; } set { - _list[key] = value; + _list[index] = value; } } diff --git a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs index 3085a78161..5bd474e782 100644 --- a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using SmartStore.Collections; namespace SmartStore { @@ -22,7 +23,22 @@ public static void AddRange(this ICollection initial, IEnumerable other } } - public static bool IsNullOrEmpty(this ICollection source) + public static SyncedCollection AsSynchronized(this ICollection source) + { + return AsSynchronized(source, new object()); + } + + public static SyncedCollection AsSynchronized(this ICollection source, object syncRoot) + { + if (source is SyncedCollection sc) + { + return sc; + } + + return new SyncedCollection(source, syncRoot); + } + + public static bool IsNullOrEmpty(this ICollection source) { return source == null || source.Count == 0; } diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 1cc3539562..b5b5c2f42c 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -175,6 +175,7 @@ + @@ -281,7 +282,7 @@ - + From c3efa8744897d686d610e132f9fcbabd569f5685 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sat, 6 Apr 2019 03:56:13 +0200 Subject: [PATCH 107/460] A lot of low-level stuff and micro-optimization --- .gitignore | 1 + .../Infrastructure/WebAppTypeFinder.cs | 2 +- .../Plugins/PluginDescriptor.cs | 79 ++--- .../SmartStore.Core/Plugins/PluginFinder.cs | 8 +- .../Plugins/PluginManager.Cache.cs | 28 +- .../SmartStore.Core/Plugins/PluginManager.cs | 274 ++++++++++-------- src/Libraries/SmartStore.Core/WebHelper.cs | 5 +- .../SmartStore.Data/Caching/EfDbModelStore.cs | 37 +++ .../Caching/EfMappingViewCache.cs | 46 +++ .../Caching/EfViewCacheFactory.cs | 125 ++++++++ .../Properties/AssemblyInfo.cs | 1 + .../Setup/MigrateDatabaseInitializer.cs | 8 +- .../SmartStore.Data/SmartDbConfiguration.cs | 9 +- .../SmartStore.Data/SmartStore.Data.csproj | 3 + .../Events/DefaultConsumerFactory.cs | 11 +- .../Localization/LocalizationService.cs | 2 +- .../Services/GoogleFeedService.cs | 2 +- .../UI/Components/Menu/MenuItemBuilder.cs | 4 - src/Presentation/SmartStore.Web/Global.asax | 2 +- .../SmartStore.Web/Global.asax.cs | 6 +- .../Partials/CatalogMenu.Navbar.cshtml | 2 + .../Views/Catalog/Partials/CatalogMenu.cshtml | 1 + src/Presentation/SmartStore.Web/Web.config | 4 +- 23 files changed, 466 insertions(+), 194 deletions(-) create mode 100644 src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs create mode 100644 src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs create mode 100644 src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs diff --git a/.gitignore b/.gitignore index 118d11be55..85bd82b7ff 100644 --- a/.gitignore +++ b/.gitignore @@ -300,6 +300,7 @@ Kopie von* /src/Presentation/SmartStore.Web/App_Data/ImportProfiles/ /src/Presentation/SmartStore.Web/App_Data/Indexing/ /src/Presentation/SmartStore.Web/App_Data/Lucene/ +/src/Presentation/SmartStore.Web/App_Data/EfCache/ /src/Presentation/SmartStore.Web/App_Data/Settings.txt /src/Presentation/SmartStore.Web/App_Data/InstalledPlugins.txt /src/Presentation/SmartStore.Web/App_Data/Licenses.lic diff --git a/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs index f06b87c086..a437eebd7d 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs @@ -95,7 +95,7 @@ protected virtual void LoadPluginAssemblies() AppDomain.CurrentDomain.GetAssemblies().Select(x => x.FullName), StringComparer.OrdinalIgnoreCase); - var pluginAssemblies = new HashSet(PluginManager.ReferencedPlugins.Select(x => x.ReferencedAssembly).Where(x => x != null)); + var pluginAssemblies = new HashSet(PluginManager.ReferencedPlugins.Select(x => x.Assembly.Assembly)); foreach (var assembly in pluginAssemblies) { diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs b/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs index 35b2b3e8c9..ab6a32754e 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs @@ -11,6 +11,30 @@ namespace SmartStore.Core.Plugins { + /// + /// Information about an assembly referenced by a plugin. + /// + public sealed class AssemblyReference + { + /// + /// File in ASP.NET temp folder + /// + public FileInfo File { get; internal set; } + /// + /// The original assembly file that a shadow copy was made from it + /// + public FileInfo OriginalFile { get; internal set; } + /// + /// The assembly that has been shadow copied and was loaded into the AppDomain + /// + public Assembly Assembly { get; internal set; } + + /// + /// Exception thrown during activation/probing + /// + internal Exception ActivationException { get; set; } + } + [DataContract] public class PluginDescriptor : IComparable { @@ -19,17 +43,16 @@ public class PluginDescriptor : IComparable public PluginDescriptor() { - this.Version = new Version("1.0"); - this.MinAppVersion = SmartStoreVersion.Version; + Version = new Version("1.0"); + MinAppVersion = SmartStoreVersion.Version; } // Unit tests - public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFile, Type pluginType) + public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFile, Type pluginClrType) : this() { - this.ReferencedAssembly = referencedAssembly; - this.OriginalAssemblyFile = originalAssemblyFile; - this.PluginType = pluginType; + Assembly = new AssemblyReference { Assembly = referencedAssembly, OriginalFile = originalAssemblyFile, File = originalAssemblyFile }; + PluginClrType = pluginClrType; } /// @@ -42,6 +65,12 @@ public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFi /// public string PhysicalPath { get; set; } + /// + /// Gets the folder name + /// + [DataMember] + public string FolderName { get; internal set; } + /// /// The virtual path of the runtime plugin /// @@ -79,33 +108,23 @@ public string BrandImageFileName } /// - /// Plugin type + /// Plugin runtime type /// - public Type PluginType { get; set; } - - /// - /// The assembly that has been shadow copied and was loaded into the AppDomain - /// - public Assembly ReferencedAssembly { get; internal set; } - - /// - /// The original assembly file that a shadow copy was made from it - /// - public FileInfo OriginalAssemblyFile { get; internal set; } + public Type PluginClrType { get; set; } /// - /// The list of assembly files found in the plugin folder, except the main plugin assembly. + /// The plugin assembly. /// - public IEnumerable ReferencedLocalAssemblyFiles { get; internal set; } + public AssemblyReference Assembly { get; internal set; } = new AssemblyReference(); /// - /// Gets any exception thrown during plugin activation & initializing + /// List of assemblies files found in the plugin folder, except the main plugin assembly. /// - public Exception ActivationException { get; internal set; } + internal AssemblyReference[] ReferencedLocalAssemblies { get; set; } = new AssemblyReference[0]; - /// - /// Gets or sets the plugin group - /// + /// + /// Gets or sets the plugin group + /// [DataMember] public string Group { get; internal set; } @@ -123,12 +142,6 @@ public bool IsInKnownGroup [DataMember] public string FriendlyName { get; set; } - /// - /// Gets the folder name - /// - [DataMember] - public string FolderName { get; internal set; } - /// /// Gets or sets the system name /// @@ -243,10 +256,10 @@ public string ResourceRootKey public T Instance() where T : class, IPlugin { object instance; - if (!EngineContext.Current.ContainerManager.TryResolve(PluginType, null, out instance)) + if (!EngineContext.Current.ContainerManager.TryResolve(PluginClrType, null, out instance)) { // Not registered - instance = EngineContext.Current.ContainerManager.ResolveUnregistered(PluginType); + instance = EngineContext.Current.ContainerManager.ResolveUnregistered(PluginClrType); } var typedInstance = instance as T; diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs index 4677d009f6..f45a4a012c 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs @@ -35,7 +35,7 @@ private void LoadPlugins() foreach (var plugin in plugins) { _nameMap[plugin.SystemName] = plugin; - _assemblyMap[plugin.ReferencedAssembly] = plugin; + _assemblyMap[plugin.Assembly.Assembly] = plugin; } } @@ -48,7 +48,7 @@ private void LoadPlugins() public virtual IEnumerable GetPlugins(bool installedOnly = true) where T : class, IPlugin { foreach (var plugin in _plugins) - if (typeof(T).IsAssignableFrom(plugin.PluginType)) + if (typeof(T).IsAssignableFrom(plugin.PluginClrType)) if (!installedOnly || plugin.Installed) yield return plugin.Instance(); } @@ -75,7 +75,7 @@ public virtual IEnumerable GetPluginDescriptors(bool instal where T : class, IPlugin { foreach (var plugin in _plugins) - if (typeof(T).IsAssignableFrom(plugin.PluginType)) + if (typeof(T).IsAssignableFrom(plugin.PluginClrType)) if (!installedOnly || plugin.Installed) yield return plugin; } @@ -121,7 +121,7 @@ public virtual PluginDescriptor GetPluginDescriptorBySystemName(string system { if (!installedOnly || descriptor.Installed) { - if (typeof(T).IsAssignableFrom(descriptor.PluginType)) + if (typeof(T).IsAssignableFrom(descriptor.PluginClrType)) return descriptor; } } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs b/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs index 2d4e6ae541..dff9c225b5 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs @@ -38,10 +38,14 @@ private static bool DetectAndCleanStalePlugins(IEnumerable plu .Select(x => new FileInfo(Path.Combine(_shadowCopyDir.FullName, x))) .ToArray(); - foreach (var f in staleFiles) + // If that fails, then we will try to remove it the hard way by renaming it to .delete if it doesn't delete nicely + if (staleFiles.Length > 5) { - // If that fails, then we will try to remove it the hard way by renaming it to .delete if it doesn't delete nicely - TryDeleteStaleFile(f); + staleFiles.AsParallel().ForAll(f => TryDeleteStaleFile(f)); + } + else + { + staleFiles.Each(f => TryDeleteStaleFile(f)); } return true; @@ -132,14 +136,11 @@ private static void SavePluginsAssemblies(IEnumerable plugins) foreach (var p in plugins) { - if (p.ReferencedLocalAssemblyFiles != null) - { - p.ReferencedLocalAssemblyFiles.Each(x => list.Add(x.Name)); - } + p.ReferencedLocalAssemblies.Each(x => list.Add(x.OriginalFile.Name)); - if (p.OriginalAssemblyFile != null) + if (p.Assembly.OriginalFile != null) { - list.Add(p.OriginalAssemblyFile.Name); + list.Add(p.Assembly.OriginalFile.Name); } } @@ -166,15 +167,12 @@ private static int ComputePluginsHash(IEnumerable plugins) // Add each *.dll, Description.txt, web.config to the hash foreach (var p in plugins) { - if (p.OriginalAssemblyFile != null) + if (p.Assembly.OriginalFile != null) { - hashCombiner.Add(p.OriginalAssemblyFile); + hashCombiner.Add(p.Assembly.OriginalFile); } - if (p.ReferencedLocalAssemblyFiles != null) - { - p.ReferencedLocalAssemblyFiles.Each(x => hashCombiner.Add(x)); - } + p.ReferencedLocalAssemblies.Each(x => hashCombiner.Add(x.OriginalFile)); hashCombiner.Add(Path.Combine(p.PhysicalPath, "Description.txt")); hashCombiner.Add(Path.Combine(p.PhysicalPath, "web.config")); diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs index 6a95a30aae..8e33e28483 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs @@ -16,6 +16,7 @@ using SmartStore.Utilities; using SmartStore.Utilities.Threading; using SmartStore.Core.Data; +using System.Threading.Tasks; // Contributor: Umbraco (http://www.umbraco.com). Thanks a lot! // SEE THIS POST for full details of what this does @@ -109,9 +110,8 @@ public static void Initialize() DynamicModuleUtility.RegisterModule(typeof(AutofacRequestLifetimeHttpModule)); - #region Plugins - var incompatiblePlugins = new HashSet(StringComparer.OrdinalIgnoreCase); + var dirty = false; using (Locker.GetWriteLock()) { @@ -122,68 +122,21 @@ public static void Initialize() // If plugins state is dirty, we copy files over to the dynamic folder, // otherwise we just reference the previously copied file. - var dirty = DetectAndCleanStalePlugins(compatiblePlugins); + dirty = DetectAndCleanStalePlugins(compatiblePlugins); - foreach (var plugin in plugins) - { - if (plugin.Incompatible) - { - incompatiblePlugins.Add(plugin.SystemName); - continue; - } - else - { - _referencedPlugins[plugin.SystemName] = plugin; - } + //// Perf: Initialize/probe all plugins in parallel + //plugins.AsParallel().ForAll(x => DeployPlugin(x, dirty)); - try - { - // Shadow copy main plugin assembly - plugin.ReferencedAssembly = Probe(plugin.OriginalAssemblyFile, dirty); - - // Shadow copy other referenced plugin local assemblies - if (plugin.ReferencedLocalAssemblyFiles != null) - { - foreach (var assemblyFile in plugin.ReferencedLocalAssemblyFiles) - { - Probe(assemblyFile, dirty); - } - } - - if (!plugin.Installed) - { - _inactiveAssemblies.Add(plugin.ReferencedAssembly); - } - - // Initialize: Find IPlugin, IPreApplicationStart, IConfigurable etc. - ActivatePlugin(plugin); - } - catch (UnauthorizedAccessException) - { - // Throw the exception if its UnauthorizedAccessException as this will - // be because we most likely cannot copy to the dynamic folder. - throw; - } - catch (ReflectionTypeLoadException ex) - { - var msg = string.Empty; - foreach (var e in ex.LoaderExceptions) - { - msg += e.Message + Environment.NewLine; - } - - HandlePluginActivationException(ex, plugin, msg, incompatiblePlugins); - } - catch (Exception ex) - { - var msg = string.Empty; - for (var e = ex; e != null; e = e.InnerException) - { - msg += e.Message + Environment.NewLine; - } + // Retry when failed, because during parallel execution assembly loading MAY fail. + // Therefore we retry initialization for failed plugins, but sequentially this time. + foreach (var p in plugins) + { + // INFO: this seems redundant, but it's ok: + // DeployPlugin() only probes assemblies that are not loaded yet. + DeployPlugin(p, dirty); - HandlePluginActivationException(ex, plugin, msg, incompatiblePlugins); - } + // Finalize + FinalizePlugin(p); } if (dirty && DataSettings.DatabaseIsInstalled()) @@ -199,7 +152,64 @@ public static void Initialize() IncompatiblePlugins = incompatiblePlugins.AsReadOnly(); } - #endregion + void DeployPlugin(PluginDescriptor p, bool shadowCopy) + { + if (p.Incompatible) + { + // Do nothing if plugin is incompatible + return; + } + + // First copy referenced local assemblies (if any) + for (int i = 0; i < p.ReferencedLocalAssemblies.Length; ++i) + { + var refAr = p.ReferencedLocalAssemblies[i]; + if (refAr.Assembly == null) + { + Probe(refAr, p, shadowCopy); + } + } + + // Then copy main plugin assembly + var ar = p.Assembly; + if (ar.Assembly == null) + { + Probe(ar, p, shadowCopy); + if (ar.Assembly != null) + { + // Activate (even if uninstalled): Find IPlugin, IPreApplicationStart, IConfigurable etc. + ActivatePlugin(p); + } + } + } + + void FinalizePlugin(PluginDescriptor p) + { + _referencedPlugins[p.SystemName] = p; + + if (p.Incompatible) + { + incompatiblePlugins.Add(p.SystemName); + return; + } + + var firstFailedAssembly = p.ReferencedLocalAssemblies.FirstOrDefault(x => x.ActivationException != null); + if (firstFailedAssembly == null && p.Assembly.ActivationException != null) + { + firstFailedAssembly = p.Assembly; + } + if (firstFailedAssembly != null) + { + Debug.WriteLine(firstFailedAssembly.ActivationException.Message); + p.Incompatible = true; + incompatiblePlugins.Add(p.SystemName); + } + + if ((!p.Installed || firstFailedAssembly != null) && p.Assembly.Assembly != null) + { + _inactiveAssemblies.Add(p.Assembly.Assembly); + } + } } /// @@ -216,7 +226,7 @@ private static IEnumerable LoadPluginDescriptors() if (!pluginsDir.Exists) { pluginsDir.Create(); - yield break; + return Enumerable.Empty(); } // Determine all plugin folders: ~/Plugins/{SystemName} @@ -225,17 +235,14 @@ private static IEnumerable LoadPluginDescriptors() .OrderBy(x => x.Name) .ToArray(); - var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(); + var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile().AsSynchronized(); // Load/activate all plugins - foreach (var d in allPluginDirs) - { - var descriptor = LoadPluginDescriptor(d, installedPluginSystemNames); - if (descriptor != null) - { - yield return descriptor; - } - } + return allPluginDirs + .AsParallel() + .AsOrdered() + .Select(d => LoadPluginDescriptor(d, installedPluginSystemNames)) + .Where(x => x != null); } private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollection installedPluginSystemNames) @@ -282,9 +289,9 @@ private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollectio .ToDictionarySafe(x => x.Name, StringComparer.OrdinalIgnoreCase); // Set 'OriginalAssemblyFile' property - descriptor.OriginalAssemblyFile = pluginBinaries.Get(descriptor.PluginFileName); + descriptor.Assembly.OriginalFile = pluginBinaries.Get(descriptor.PluginFileName); - if (descriptor.OriginalAssemblyFile == null) + if (descriptor.Assembly.OriginalFile == null) { throw new SmartException("The main assembly '{0}' for plugin '{1}' could not be found.".FormatInvariant(descriptor.PluginFileName, descriptor.SystemName)); } @@ -294,7 +301,7 @@ private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollectio .Where(x => !x.Key.IsCaseInsensitiveEqual(descriptor.PluginFileName)) .Select(x => x.Value); - descriptor.ReferencedLocalAssemblyFiles = otherAssemblyFiles.ToArray(); + descriptor.ReferencedLocalAssemblies = otherAssemblyFiles.Select(x => new AssemblyReference { OriginalFile = x }).ToArray(); return descriptor; } @@ -302,15 +309,15 @@ private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollectio private static void ActivatePlugin(PluginDescriptor plugin) { // Init plugin type (only one plugin per assembly is allowed) - var exportedTypes = plugin.ReferencedAssembly.ExportedTypes; bool pluginFound = false; bool preStarterFound = !plugin.Installed; + var exportedTypes = plugin.Assembly.Assembly.GetExportedTypes(); foreach (var t in exportedTypes) { if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract) { - plugin.PluginType = t; + plugin.PluginClrType = t; plugin.IsConfigurable = typeof(IConfigurable).IsAssignableFrom(t); pluginFound = true; } @@ -332,22 +339,6 @@ private static void ActivatePlugin(PluginDescriptor plugin) } } - private static void HandlePluginActivationException(Exception ex, PluginDescriptor plugin, string msg, ICollection incompatiblePlugins) - { - msg = "Error loading plugin '{0}'".FormatInvariant(plugin.SystemName) + Environment.NewLine + msg; - - var fail = new SmartException(msg, ex); - Debug.WriteLine(fail.Message); - - if (plugin.ReferencedAssembly != null) - { - _inactiveAssemblies.Add(plugin.ReferencedAssembly); - } - - plugin.ActivationException = fail; - incompatiblePlugins.Add(plugin.SystemName); - } - /// /// Mark plugin as installed /// @@ -527,51 +518,96 @@ private static void SetNativeDllPath() /// /// Perform file deploy /// - /// Plugin main dll file info + /// Assembly reference to probe /// Reference to the shadow copied Assembly - private static Assembly Probe(FileInfo plugin, bool performShadowCopy) + private static Assembly Probe(AssemblyReference ar, PluginDescriptor d, bool shadowCopy) { - if (plugin.Directory == null || plugin.Directory.Parent == null) + var file = ar.OriginalFile; + + try { - throw new InvalidOperationException("The plugin directory for the " + plugin.Name + - " file exists in a folder outside of the allowed SmartStore folder hierarchy"); + if (file.Directory == null || file.Directory.Parent == null) + { + throw new InvalidOperationException("The plugin directory for the " + file.Name + + " file exists in a folder outside of the allowed SmartStore folder hierarchy"); + } + + ar.File = InitializeFullTrust(file, shadowCopy); + + // We can now register the plugin definition + ar.Assembly = Assembly.Load(AssemblyName.GetAssemblyName(ar.File.FullName)); + + // Add the reference to the build manager + if (ar.Assembly != null) + { + // Loading assembly can fail in parallel loops. + // In this case, we'll probe again later in a sequential loop. + BuildManager.AddReferencedAssembly(ar.Assembly); + } + + ar.ActivationException = null; } + catch (UnauthorizedAccessException) + { + // Throw the exception if its UnauthorizedAccessException as this will + // be because we most likely cannot copy to the dynamic folder. + throw; + } + catch (ReflectionTypeLoadException ex) + { + var msg = string.Empty; + foreach (var e in ex.LoaderExceptions) + { + msg += e.Message + Environment.NewLine; + } - var probedPlugin = InitializeFullTrust(plugin, performShadowCopy); + ar.ActivationException = CreateException(msg, ex); + } + catch (Exception ex) + { + var msg = string.Empty; + for (var e = ex; e != null; e = e.InnerException) + { + msg += e.Message + Environment.NewLine; + } - // We can now register the plugin definition - var probedAssembly = Assembly.Load(AssemblyName.GetAssemblyName(probedPlugin.FullName)); + ar.ActivationException = CreateException(msg, ex); + } - // Add the reference to the build manager - BuildManager.AddReferencedAssembly(probedAssembly); + return ar.Assembly; - return probedAssembly; - } + Exception CreateException(string message, Exception innerException) + { + return new SmartException( + "Error loading plugin '{0}'".FormatInvariant(d.SystemName) + Environment.NewLine + message, + innerException); + } + } /// /// Used to initialize plugins when running in Full Trust /// - /// Plugin main dll file + /// Plugin dll file /// Shadow copied file - private static FileInfo InitializeFullTrust(FileInfo plugin, bool performShadowCopy) + private static FileInfo InitializeFullTrust(FileInfo dll, bool shadowCopy) { - var probedPlugin = new FileInfo(Path.Combine(_shadowCopyDir.FullName, plugin.Name)); + var probedDll = new FileInfo(Path.Combine(_shadowCopyDir.FullName, dll.Name)); // If instructed to not perform the copy, just return the path to where it is supposed to be - if (!performShadowCopy && probedPlugin.Exists) - return probedPlugin; + if (!shadowCopy && probedDll.Exists) + return probedDll; try { - File.Copy(plugin.FullName, probedPlugin.FullName, true); + File.Copy(dll.FullName, probedDll.FullName, true); } catch (UnauthorizedAccessException) { - throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedPlugin.Directory.FullName)); + throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); } catch (IOException) { - Debug.WriteLine(probedPlugin.FullName + " is locked, attempting to rename"); + Debug.WriteLine(probedDll.FullName + " is locked, attempting to rename"); // This occurs when the files are locked, // For some reason devenv locks plugin files some times and for another crazy reason you are allowed to rename them @@ -579,23 +615,23 @@ private static FileInfo InitializeFullTrust(FileInfo plugin, bool performShadowC try { // If all else fails during the cleanup and we cannot copy over so we need to rename with a GUID - var deleteName = GetNewDeleteName(probedPlugin); - File.Move(probedPlugin.FullName, deleteName); + var deleteName = GetNewDeleteName(probedDll); + File.Move(probedDll.FullName, deleteName); } catch (UnauthorizedAccessException) { - throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedPlugin.Directory.FullName)); + throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); } catch (IOException exc) { - throw new IOException(probedPlugin.FullName + " rename failed, cannot initialize plugin", exc); + throw new IOException(probedDll.FullName + " rename failed, cannot initialize plugin", exc); } // OK, we've made it this so far, now retry the shadow copy - File.Copy(plugin.FullName, probedPlugin.FullName, true); + File.Copy(dll.FullName, probedDll.FullName, true); } - return probedPlugin; + return probedDll; } /// diff --git a/src/Libraries/SmartStore.Core/WebHelper.cs b/src/Libraries/SmartStore.Core/WebHelper.cs index e5a0bf1ca0..042afaf379 100644 --- a/src/Libraries/SmartStore.Core/WebHelper.cs +++ b/src/Libraries/SmartStore.Core/WebHelper.cs @@ -496,10 +496,7 @@ private void DeleteMvcTypeCacheFiles() File.Delete(Path.Combine(userCacheDir, "MVC-ControllerTypeCache.xml")); File.Delete(Path.Combine(userCacheDir, "MVC-AreaRegistrationTypeCache.xml")); } - catch - { - - } + catch { } } private bool TryWriteBinFolder() diff --git a/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs new file mode 100644 index 0000000000..235db8acb0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity.Infrastructure; +using System.Diagnostics; +using System.IO; + +namespace SmartStore.Data.Caching +{ + public sealed class EfDbModelStore : DefaultDbModelStore + { + public EfDbModelStore(string location) + : base(location) + { } + + public override DbCompiledModel TryLoad(Type contextType) + { + string path = GetFilePath(contextType); + + if (File.Exists(path)) + { + var cachedModelCreatedOn = File.GetLastWriteTimeUtc(path); + var assemblyCreatedOn = File.GetLastWriteTimeUtc(contextType.Assembly.Location); + if (assemblyCreatedOn > cachedModelCreatedOn) + { + File.Delete(path); + Debug.WriteLine("Cached db model obsolete. Re-creating cached db model edmx."); + } + } + else + { + Debug.WriteLine("No cached db model found. Creating cached db model edmx."); + } + + return base.TryLoad(contextType); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs new file mode 100644 index 0000000000..de96d2823d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.Entity.Infrastructure.MappingViews; +using System.Data.Entity.Core.Metadata.Edm; + +namespace SmartStore.Data.Caching +{ + public class EfMappingViewCache : DbMappingViewCache + { + private readonly string _hash; + private readonly Dictionary _views; + + public EfMappingViewCache(object json) + { + // TODO + } + + public EfMappingViewCache(string hash, Dictionary views) + { + Guard.NotEmpty(hash, nameof(hash)); + Guard.NotNull(views, nameof(views)); + + _hash = hash; + _views = views.ToDictionary(kvp => GetExtentFullName(kvp.Key), kvp => kvp.Value); + } + + public override string MappingHashValue + { + get { return _hash; } + } + + public override DbMappingView GetView(EntitySetBase extent) + { + _views.TryGetValue(GetExtentFullName(extent), out var mappingView); + return mappingView; + } + + private string GetExtentFullName(EntitySetBase entitySet) + { + return entitySet.EntityContainer.Name + "." + entitySet.Name; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs b/src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs new file mode 100644 index 0000000000..d5cdf29978 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.Entity.Infrastructure.MappingViews; +using System.Data.Entity.Core.Metadata.Edm; +using System.Data.Entity.Core.Mapping; +using System.Diagnostics; +using System.Data.Entity.Infrastructure; +using System.Data.Entity; +using System.Collections.Concurrent; + +namespace SmartStore.Data.Caching +{ + public class EfViewCacheFactory : DbMappingViewCacheFactory + { + private readonly static ConcurrentDictionary _lookup = + new ConcurrentDictionary(); + + public static void SetContext() + where TContext : DbContext, new() + { + using (var ctx = new TContext()) + { + SetContext(ctx); + } + } + + /// + /// Sets a for a mapping represented + /// by a derived class. + /// + /// A derived class instance containing + /// mapping to set the view cache factory for. + /// View cache factory + /// + /// This method must be called before EntityFramework generates views for the mapping + /// (which typically happens on the first query). + /// cannot be set more than once for the same + /// . + /// + public static void SetContext(DbContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); + + var objectContext = ((IObjectContextAdapter)ctx).ObjectContext; + var itemCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); + var factory = new EfViewCacheFactory(); + + itemCollection.MappingViewCacheFactory = factory; + _lookup.TryAdd(factory, itemCollection); + } + + /// + /// Returns a instance for the . + /// + /// View cache factory to return for. + /// A instance for the . + public static StorageMappingItemCollection GetMappingItemCollection(DbMappingViewCacheFactory viewCacheFactory) + { + Guard.NotNull(viewCacheFactory, nameof(viewCacheFactory)); + + if (!_lookup.TryGetValue(viewCacheFactory, out var mappingItemCollection)) + { + throw new InvalidOperationException("No StorageMappingItemCollection instance found for the provided DbMappingViewCacheFactory."); + } + + return mappingItemCollection; + } + + /// + /// Creates a new instance of the class for + /// the given and . + /// + /// The name of the conceptual model container. + /// The name of the store model container. + /// A new instance of the class. + public override DbMappingViewCache Create(string conceptualModelContainerName, string storeModelContainerName) + { + var mappingItemCollection = GetMappingItemCollection(); + + if (mappingItemCollection == null) + { + throw new InvalidOperationException("View cache not set for this mapping item collection"); + } + + var hash = mappingItemCollection.ComputeMappingHashValue(conceptualModelContainerName, storeModelContainerName); + + //var viewsXml = Load(conceptualModelContainerName, storeModelContainerName); + //if (viewsXml != null) + //{ + // var viewsForMapping = GetViewsForMapping(viewsXml, conceptualModelContainerName, storeModelContainerName); + + // if (viewsForMapping != null && (string)viewsForMapping.Attribute("hash") == hash) + // { + // return new MappingViewCache(viewsForMapping); + // } + //} + + var views = GenerateViews(mappingItemCollection, conceptualModelContainerName, storeModelContainerName); + + //viewsXml = CreateOrUpdateViewsXml(viewsXml, hash, conceptualModelContainerName, storeModelContainerName, views); + //Save(viewsXml); + + return new EfMappingViewCache(hash, views); + } + + // virtual for mocking + internal virtual StorageMappingItemCollection GetMappingItemCollection() + { + return GetMappingItemCollection(this); + } + + // virtual for mocking + internal virtual Dictionary GenerateViews( + StorageMappingItemCollection mappingItemCollection, + string conceptualModelContainerName, + string storeModelContainerName) + { + var errors = new List(); + return mappingItemCollection.GenerateViews(conceptualModelContainerName, storeModelContainerName, errors); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Properties/AssemblyInfo.cs b/src/Libraries/SmartStore.Data/Properties/AssemblyInfo.cs index 11132fd2a1..31d6040e23 100644 --- a/src/Libraries/SmartStore.Data/Properties/AssemblyInfo.cs +++ b/src/Libraries/SmartStore.Data/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; + [assembly: AssemblyTitle("SmartStore.Data")] [assembly: InternalsVisibleTo("SmartStore.Web")] [assembly: InternalsVisibleTo("SmartStore.Data.Tests")] diff --git a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs index c92790df60..f7e0b31d0f 100644 --- a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs @@ -5,6 +5,7 @@ using System.Data.Entity.Migrations; using System.Linq; using SmartStore.Core.Data; +using SmartStore.Data.Caching; using SmartStore.Data.Migrations; using SmartStore.Utilities; @@ -96,10 +97,11 @@ public virtual void InitializeDatabase(TContext context) } else { - var coreConfig = config as MigrationsConfiguration; - if (coreConfig != null && context is SmartObjectContext) + ////// DB is up-to-date and no migration ran. + //EfViewCacheFactory.SetContext(context); + + if (config is MigrationsConfiguration coreConfig && context is SmartObjectContext) { - // DB is up-to-date and no migration ran. // Call the main Seed method anyway (on every startup), // we could have locale resources or settings to add/update. coreConfig.SeedDatabase(context as SmartObjectContext); diff --git a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs index 163b21ba8a..cb0757750d 100644 --- a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs +++ b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs @@ -6,6 +6,9 @@ using SmartStore.Core.Infrastructure; using SmartStore.Data.Caching; using System.Web.Hosting; +using System.Data.Entity.Infrastructure; +using System.Data.Entity.Infrastructure.DependencyResolution; +using SmartStore.Utilities; namespace SmartStore.Data { @@ -13,6 +16,10 @@ public class SmartDbConfiguration : DbConfiguration { public SmartDbConfiguration() { + //// DB model caching + //var modelStore = new EfDbModelStore(CommonHelper.MapPath("~/App_Data/EfCache")); + //AddDependencyResolver(new SingletonDependencyResolver(modelStore)); + IEfDataProvider provider = null; try { @@ -22,7 +29,7 @@ public SmartDbConfiguration() { /* SmartStore is not installed yet! */ } - + if (provider != null) { base.SetDefaultConnectionFactory(provider.GetConnectionFactory()); diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 25f45f52ea..016a2141ef 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -131,6 +131,9 @@ + + + diff --git a/src/Libraries/SmartStore.Services/Events/DefaultConsumerFactory.cs b/src/Libraries/SmartStore.Services/Events/DefaultConsumerFactory.cs index 72382fd1c7..3d7dc166ea 100644 --- a/src/Libraries/SmartStore.Services/Events/DefaultConsumerFactory.cs +++ b/src/Libraries/SmartStore.Services/Events/DefaultConsumerFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using SmartStore.Core.Events; using SmartStore.Core.Plugins; +using SmartStore.Core.Infrastructure; namespace SmartStore.Services.Events { @@ -29,7 +30,7 @@ public IEnumerable> GetConsumers(bool? resolveAsyncs = null) if (isActive && (resolveAsyncs == null || (resolveAsyncs.Value == isAsync))) { - if (pluginDescriptor == null || IsActiveForStore(pluginDescriptor, _services.StoreContext.CurrentStore.Id)) + if (pluginDescriptor == null || IsActiveForStore(pluginDescriptor)) { yield return consumer.Value; } @@ -46,8 +47,14 @@ public bool HasAsyncConsumer } } - private bool IsActiveForStore(PluginDescriptor plugin, int storeId) + private bool IsActiveForStore(PluginDescriptor plugin) { + int storeId = 0; + if (EngineContext.Current.IsFullyInitialized) + { + storeId = _services.StoreContext.CurrentStore.Id; + } + if (storeId == 0) { return true; diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs index 60a56d3b8e..d85eafb668 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs @@ -319,7 +319,7 @@ public virtual void ImportPluginResourcesFromXml( bool updateTouchedResources = true, IList filterLanguages = null) { - var directory = new DirectoryInfo(Path.Combine(pluginDescriptor.OriginalAssemblyFile.Directory.FullName, "Localization")); + var directory = new DirectoryInfo(Path.Combine(pluginDescriptor.Assembly.OriginalFile.Directory.FullName, "Localization")); if (!directory.Exists) return; diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs index 764edb7f2b..c2a8ff01a3 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs @@ -302,7 +302,7 @@ public List GetTaxonomyList(string searchTerm) try { var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(GoogleMerchantCenterFeedPlugin.SystemName); - var fileDir = Path.Combine(descriptor.OriginalAssemblyFile.Directory.FullName, "Files"); + var fileDir = Path.Combine(descriptor.Assembly.OriginalFile.Directory.FullName, "Files"); var fileName = "taxonomy.{0}.txt".FormatInvariant(_services.WorkContext.WorkingLanguage.LanguageCulture ?? "de-DE"); var path = Path.Combine(fileDir, fileName); var filter = searchTerm.HasValue(); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs index db03c5b813..785aa05415 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using SmartStore.Utilities; namespace SmartStore.Web.Framework.UI { - public class MenuItemBuilder : NavigationItemBuilder { private readonly IList _permissionNames; 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#" %> diff --git a/src/Presentation/SmartStore.Web/Global.asax.cs b/src/Presentation/SmartStore.Web/Global.asax.cs index fbcb3ad4ba..774b34ad3d 100644 --- a/src/Presentation/SmartStore.Web/Global.asax.cs +++ b/src/Presentation/SmartStore.Web/Global.asax.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net; using System.Web; using System.Web.Hosting; using System.Web.Mvc; @@ -7,6 +8,7 @@ using System.Web.Routing; using System.Web.WebPages; using AutoMapper; +using FluentValidation; using FluentValidation.Mvc; using JavaScriptEngineSwitcher.Core; using JavaScriptEngineSwitcher.Msie; @@ -17,6 +19,7 @@ using SmartStore.Core.Infrastructure; using SmartStore.Core.Themes; using SmartStore.Services.Tasks; +using SmartStore.Web.Framework; using SmartStore.Web.Framework.Bundling; using SmartStore.Web.Framework.Filters; using SmartStore.Web.Framework.Localization; @@ -25,9 +28,6 @@ using SmartStore.Web.Framework.Theming; using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Web.Framework.Validators; -using System.Net; -using FluentValidation; -using SmartStore.Web.Framework; namespace SmartStore.Web { diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml index 28ead8c11c..a77265ecb5 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml @@ -1,5 +1,7 @@ @model MenuModel + @using SmartStore.Collections; + @{ if (IsStoreClosed) { diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml index f7a95faade..d1be7c637e 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.cshtml @@ -1,4 +1,5 @@ @model MenuModel + @using SmartStore.Collections; @{ diff --git a/src/Presentation/SmartStore.Web/Web.config b/src/Presentation/SmartStore.Web/Web.config index 075e63e12d..f773eaf336 100644 --- a/src/Presentation/SmartStore.Web/Web.config +++ b/src/Presentation/SmartStore.Web/Web.config @@ -74,7 +74,7 @@ --> @@ -82,7 +82,7 @@ - + From fc314d3b8a4edad74e1b049375d63b2bc7c352e6 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 8 Apr 2019 13:55:05 +0200 Subject: [PATCH 108/460] Please execute update-database -TargetMigration TopicHtmlIdAndBodyCss --- .../SmartStore.Licensing.dll | Bin 259584 -> 260096 bytes .../SmartStore.Core/Domain/Cms/MenuRecord.cs | 12 + .../Migrations/201903301029426_Menus.resx | 126 ---- ...r.cs => 201904071537124_Menus.Designer.cs} | 2 +- ...9426_Menus.cs => 201904071537124_Menus.cs} | 14 +- .../Migrations/201904071537124_Menus.resx | 126 ++++ .../Migrations/MigrationsConfiguration.cs | 12 + .../SmartStore.Data/SmartStore.Data.csproj | 10 +- .../SmartStore.Data/Utilities/DataMigrator.cs | 560 +++++++++--------- .../DependencyRegistrar.cs | 3 +- .../SmartStore.Web.Framework.csproj | 3 + .../UI/Components/ComponentFactory.cs | 16 +- .../UI/Components/Menu/Menu.cs | 9 + .../UI/Components/Menu/MenuBuilder.cs | 18 + .../UI/Components/Menu/MenuExtensions.cs | 36 +- .../UI/Components/Menu/MenuModel.cs | 2 + .../UI/Components/Menu/MenuRenderer.cs | 51 ++ .../Components/ViewBasedComponentRenderer.cs | 18 +- .../Models/Menus/MenuRecordModel.cs | 8 + .../Views/Menu/_CreateOrUpdate.cshtml | 4 + .../Views/Shared/Partials/Menu.cshtml | 24 +- .../SmartStore.Web/SmartStore.Web.csproj | 1 + .../Views/Common/Partials/Menu.cshtml | 5 +- .../Views/Shared/Menus/Dropdown.cshtml | 77 +++ 24 files changed, 686 insertions(+), 451 deletions(-) delete mode 100644 src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.resx rename src/Libraries/SmartStore.Data/Migrations/{201903301029426_Menus.Designer.cs => 201904071537124_Menus.Designer.cs} (93%) rename src/Libraries/SmartStore.Data/Migrations/{201903301029426_Menus.cs => 201904071537124_Menus.cs} (89%) create mode 100644 src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs create mode 100644 src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml diff --git a/lib/SmartStore.Licensing/SmartStore.Licensing.dll b/lib/SmartStore.Licensing/SmartStore.Licensing.dll index 2d948e052f8e812881def10c9b92fe0a7129e99f..8bc1f00372a3cbb5d67ff44d4016853413cb2fd8 100644 GIT binary patch literal 260096 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

    UUy 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%0A|h# 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 VM4MUcGa@U{@i#x^`)_|wfgcPfPQL&E literal 259584 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

    -RKtilR>^y69^F? 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

    q(ya9{sZ=Z(Ai5cZTWVZ}e{} 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

    PLr8Kf*00(K^_Rix!6Pe?|DpqNoAWe@ZsV2 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[wqc^Pty>8xfmfUd{Vew@?t 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%0A}GL-LH4|%DqMzOKMGXqJUgDZ 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 diff --git a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs index d0e2539bad..f9ad7a57bc 100644 --- a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs @@ -36,6 +36,18 @@ public virtual ICollection Items [Index("IX_Menu_SystemName_IsSystemMenu", Order = 1)] public bool IsSystemMenu { get; set; } + ///

    + /// Gets or sets the menu template name. + /// + [StringLength(400)] + public string Template { get; set; } + + /// + /// Gets or sets the widget zone name. + /// + [StringLength(4000)] + public string WidgetZone { get; set; } + /// /// Gets or sets the title. /// diff --git a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.resx b/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.resx deleted file mode 100644 index 9f2b3bf542..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 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 - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs similarity index 93% rename from src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.Designer.cs rename to src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs index dc86212fac..ce3dec458c 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.Designer.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs @@ -13,7 +13,7 @@ public sealed partial class Menus : IMigrationMetadata string IMigrationMetadata.Id { - get { return "201903301029426_Menus"; } + get { return "201904071537124_Menus"; } } string IMigrationMetadata.Source diff --git a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.cs b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs similarity index 89% rename from src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.cs rename to src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs index 9e9652ea5e..92c5d19baa 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201903301029426_Menus.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs @@ -3,8 +3,6 @@ namespace SmartStore.Data.Migrations using System.Data.Entity.Migrations; using System.Web.Hosting; using SmartStore.Core.Data; - using SmartStore.Core.Domain.Customers; - using SmartStore.Core.Domain.Security; using SmartStore.Data.Setup; using SmartStore.Data.Utilities; @@ -19,6 +17,8 @@ public override void Up() Id = c.Int(nullable: false, identity: true), SystemName = c.String(nullable: false, maxLength: 400), IsSystemMenu = c.Boolean(nullable: false), + Template = c.String(maxLength: 400), + WidgetZone = c.String(maxLength: 4000), Title = c.String(maxLength: 400), Published = c.Boolean(nullable: false), LimitedToStores = c.Boolean(nullable: false), @@ -81,16 +81,6 @@ public override void Down() public void Seed(SmartObjectContext context) { - var permissionMigrator = new PermissionMigrator(context); - - permissionMigrator.AddPermission(new PermissionRecord - { - Name = "Admin area. Manage Menus", - SystemName = "ManageMenus", - Category = "Content Management" - }, new string[] { SystemCustomerRoleNames.Administrators }); - - if (HostingEnvironment.IsHosted && DataSettings.DatabaseIsInstalled()) { DataMigrator.CreateSystemMenus(context); diff --git a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx new file mode 100644 index 0000000000..efd7e90a61 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 7856993229..7eeccd35cb 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -760,6 +760,18 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "The system name of the menu.", "Der Systemname des Menüs."); + builder.AddOrUpdate("Admin.ContentManagement.Menus.Template", + "Design template", + "Design Vorlage", + "The template defines the way how the menu is displayed.", + "Über die Vorlage wird die Darstellungsart des Menüs festgelegt."); + + builder.AddOrUpdate("Admin.ContentManagement.Menus.WidgetZone", + "Widget zone", + "Widget Zone", + "Specifies widget zones in which the menu should be displayed.", + "Legt Widget Zonen fest, in denen das Menü dargestellt werden soll."); + builder.AddOrUpdate("Admin.ContentManagement.Menus.Title", "Title", "Titel", diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 016a2141ef..f21bb6af13 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -657,9 +657,9 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - - 201903301029426_Menus.cs + + + 201904071537124_Menus.cs @@ -1182,8 +1182,8 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - 201903301029426_Menus.cs + + 201904071537124_Menus.cs diff --git a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs index 995de7dad2..c4ffaa2bda 100644 --- a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs +++ b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs @@ -15,8 +15,10 @@ using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Security; using SmartStore.Core.Infrastructure; using SmartStore.Core.IO; +using SmartStore.Data.Setup; using SmartStore.Utilities; using EfState = System.Data.Entity.EntityState; @@ -455,12 +457,6 @@ public static void CreateSystemMenus(IDbContext context) const string routeProvider = "route"; const string routeTemplate = "{{\"routename\":\"{0}\"}}"; - var menuSet = ctx.Set(); - var menuItemSet = ctx.Set(); - var defaultLang = ctx.Set().OrderBy(x => x.DisplayOrder).First(); - var manufacturerCount = ctx.Set().Count(); - var order = 0; - var resourceNames = new string[] { "Footer.Info", "Footer.Service", @@ -494,296 +490,320 @@ public static void CreateSystemMenus(IDbContext context) "CustomerSettings.UserRegistrationType" }; - var resources = ctx.Set().AsNoTracking() - .Where(x => x.LanguageId == defaultLang.Id && resourceNames.Contains(x.ResourceName)) - .Select(x => new { x.ResourceName, x.ResourceValue }) - .ToList() - .ToDictionarySafe(x => x.ResourceName, x => x.ResourceValue); - - var settings = ctx.Set().AsNoTracking() - .Where(x => x.StoreId == 0 && settingNames.Contains(x.Name)) - .Select(x => new { x.Name, x.Value }) - .ToList() - .ToDictionarySafe(x => x.Name, x => x.Value); + Dictionary resources = null; + Dictionary settings = null; + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var permissionMigrator = new PermissionMigrator(ctx); + permissionMigrator.AddPermission(new PermissionRecord + { + Name = "Admin area. Manage Menus", + SystemName = "ManageMenus", + Category = "Content Management" + }, new string[] { SystemCustomerRoleNames.Administrators }); + + var menuSet = context.Set(); + var menuItemSet = context.Set(); + var defaultLang = context.Set().OrderBy(x => x.DisplayOrder).First(); + var manufacturerCount = context.Set().Count(); + var order = 0; + + resources = context.Set().AsNoTracking() + .Where(x => x.LanguageId == defaultLang.Id && resourceNames.Contains(x.ResourceName)) + .Select(x => new { x.ResourceName, x.ResourceValue }) + .ToList() + .ToDictionarySafe(x => x.ResourceName, x => x.ResourceValue); + + settings = context.Set().AsNoTracking() + .Where(x => x.StoreId == 0 && settingNames.Contains(x.Name)) + .Select(x => new { x.Name, x.Value }) + .ToList() + .ToDictionarySafe(x => x.Name, x => x.Value); + + #region System menus + + var mainMenu = menuSet.Add(new MenuRecord + { + SystemName = "Main", + IsSystemMenu = true, + Published = true, + Template = "Navbar", + Title = GetResource("Admin.Catalog.Categories") + }); - #region System menus + var footerInfo = menuSet.Add(new MenuRecord + { + SystemName = "FooterInformation", + IsSystemMenu = true, + Published = true, + Template = "LinkList", + Title = "Footer - " + GetResource("Footer.Info") + }); - var mainMenu = menuSet.Add(new MenuRecord - { - SystemName = "Main", - IsSystemMenu = true, - Published = true, - Title = GetResource("Admin.Catalog.Categories") - }); + var footerService = menuSet.Add(new MenuRecord + { + SystemName = "FooterService", + IsSystemMenu = true, + Published = true, + Template = "LinkList", + Title = "Footer - " + GetResource("Footer.Service") + }); - var footerInfo = menuSet.Add(new MenuRecord - { - SystemName = "FooterInformation", - IsSystemMenu = true, - Published = true, - Title = "Footer - " + GetResource("Footer.Info") - }); + var footerCompany = menuSet.Add(new MenuRecord + { + SystemName = "FooterCompany", + IsSystemMenu = true, + Published = true, + Template = "LinkList", + Title = "Footer - " + GetResource("Footer.Company") + }); - var footerService = menuSet.Add(new MenuRecord - { - SystemName = "FooterService", - IsSystemMenu = true, - Published = true, - Title = "Footer - " + GetResource("Footer.Service") - }); + var serviceMenu = menuSet.Add(new MenuRecord + { + SystemName = "HelpAndService", + IsSystemMenu = true, + Published = true, + Template = "Dropdown", + Title = GetResource("Menu.ServiceMenu") + }); - var footerCompany = menuSet.Add(new MenuRecord - { - SystemName = "FooterCompany", - IsSystemMenu = true, - Published = true, - Title = "Footer - " + GetResource("Footer.Company") - }); + scope.Commit(); - var serviceMenu = menuSet.Add(new MenuRecord - { - SystemName = "HelpAndService", - IsSystemMenu = true, - Published = true, - Title = GetResource("Menu.ServiceMenu") - }); + #endregion - ctx.SaveChanges(); + #region Main and footer menus - #endregion + menuItemSet.Add(new MenuItemRecord + { + MenuId = mainMenu.Id, + ProviderName = "catalog", + Published = true + }); - #region Main and footer menus + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerInfo.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("ManufacturerList"), + Title = GetResource("Manufacturers.List"), + DisplayOrder = ++order, + Published = manufacturerCount > 0 + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerInfo.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), + Title = GetResource("Products.NewProducts"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerInfo.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), + Title = GetResource("Products.RecentlyViewedProducts"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerInfo.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("CompareProducts"), + Title = GetResource("Products.Compare.List"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) + }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = mainMenu.Id, - ProviderName = "catalog", - Published = true - }); + scope.Commit(); + order = 0; - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerInfo.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("ManufacturerList"), - Title = GetResource("Manufacturers.List"), - DisplayOrder = ++order, - Published = manufacturerCount > 0 - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerInfo.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), - Title = GetResource("Products.NewProducts"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerInfo.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), - Title = GetResource("Products.RecentlyViewedProducts"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerInfo.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("CompareProducts"), - Title = GetResource("Products.Compare.List"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) - }); - - ctx.SaveChanges(); - order = 0; - - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerService.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("contactus"), - Title = GetResource("ContactUs"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerService.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("Blog"), - Title = GetResource("Blog"), - DisplayOrder = ++order, - Published = GetSetting("BlogSettings.Enabled", false) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerService.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("Boards"), - Title = GetResource("Forum.Forums"), - DisplayOrder = ++order, - Published = GetSetting("ForumSettings.ForumsEnabled", false) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerService.Id, - ProviderName = entityProvider, - Model = "topic:shippinginfo", - Title = GetResource("ShippingReturns"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerService.Id, - ProviderName = entityProvider, - Model = "topic:paymentinfo", - Title = GetResource("Paymentinfo"), - DisplayOrder = ++order - }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("contactus"), + Title = GetResource("ContactUs"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("Blog"), + Title = GetResource("Blog"), + DisplayOrder = ++order, + Published = GetSetting("BlogSettings.Enabled", false) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("Boards"), + Title = GetResource("Forum.Forums"), + DisplayOrder = ++order, + Published = GetSetting("ForumSettings.ForumsEnabled", false) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = entityProvider, + Model = "topic:shippinginfo", + Title = GetResource("ShippingReturns"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = entityProvider, + Model = "topic:paymentinfo", + Title = GetResource("Paymentinfo"), + DisplayOrder = ++order + }); - ctx.SaveChanges(); - order = 0; + scope.Commit(); + order = 0; - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerCompany.Id, - ProviderName = entityProvider, - Model = "topic:aboutus", - Title = GetResource("AboutUs"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerCompany.Id, - ProviderName = entityProvider, - Model = "topic:imprint", - Title = GetResource("Imprint"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerCompany.Id, - ProviderName = entityProvider, - Model = "topic:disclaimer", - Title = GetResource("Disclaimer"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerCompany.Id, - ProviderName = entityProvider, - Model = "topic:privacyinfo", - Title = GetResource("PrivacyNotice"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = footerCompany.Id, - ProviderName = entityProvider, - Model = "topic:conditionsofuse", - Title = GetResource("ConditionsOfUse"), - DisplayOrder = ++order - }); - - if (GetSetting("CustomerSettings.UserRegistrationType", "").IsCaseInsensitiveEqual("Disabled")) - { menuItemSet.Add(new MenuItemRecord { MenuId = footerCompany.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("Login"), - Title = GetResource("Account.Login"), + ProviderName = entityProvider, + Model = "topic:aboutus", + Title = GetResource("AboutUs"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerCompany.Id, + ProviderName = entityProvider, + Model = "topic:imprint", + Title = GetResource("Imprint"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerCompany.Id, + ProviderName = entityProvider, + Model = "topic:disclaimer", + Title = GetResource("Disclaimer"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerCompany.Id, + ProviderName = entityProvider, + Model = "topic:privacyinfo", + Title = GetResource("PrivacyNotice"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerCompany.Id, + ProviderName = entityProvider, + Model = "topic:conditionsofuse", + Title = GetResource("ConditionsOfUse"), DisplayOrder = ++order }); - } - ctx.SaveChanges(); - order = 0; + if (GetSetting("CustomerSettings.UserRegistrationType", "").IsCaseInsensitiveEqual("Disabled")) + { + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerCompany.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("Login"), + Title = GetResource("Account.Login"), + DisplayOrder = ++order + }); + } - #endregion + scope.Commit(); + order = 0; - #region Help & Service + #endregion - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), - Title = GetResource("Products.NewProducts"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("ManufacturerList"), - Title = GetResource("Manufacturers.List"), - DisplayOrder = ++order, - Published = manufacturerCount > 0 - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), - Title = GetResource("Products.RecentlyViewedProducts"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = routeProvider, - Model = routeTemplate.FormatInvariant("CompareProducts"), - Title = GetResource("Products.Compare.List"), - DisplayOrder = ++order, - Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) - }); - - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = entityProvider, - Model = "topic:aboutus", - Title = GetResource("AboutUs"), - DisplayOrder = ++order, - BeginGroup = true - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = entityProvider, - Model = "topic:disclaimer", - Title = GetResource("Disclaimer"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = entityProvider, - Model = "topic:shippinginfo", - Title = GetResource("ShippingReturns"), - DisplayOrder = ++order - }); - menuItemSet.Add(new MenuItemRecord - { - MenuId = serviceMenu.Id, - ProviderName = entityProvider, - Model = "topic:conditionsofuse", - Title = GetResource("ConditionsOfUse"), - DisplayOrder = ++order - }); + #region Help & Service - ctx.SaveChanges(); - order = 0; + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyAddedProducts"), + Title = GetResource("Products.NewProducts"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.RecentlyAddedProductsEnabled", true) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("ManufacturerList"), + Title = GetResource("Manufacturers.List"), + DisplayOrder = ++order, + Published = manufacturerCount > 0 + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("RecentlyViewedProducts"), + Title = GetResource("Products.RecentlyViewedProducts"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.RecentlyViewedProductsEnabled", true) + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("CompareProducts"), + Title = GetResource("Products.Compare.List"), + DisplayOrder = ++order, + Published = GetSetting("CatalogSettings.CompareProductsEnabled", true) + }); - #endregion + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = entityProvider, + Model = "topic:aboutus", + Title = GetResource("AboutUs"), + DisplayOrder = ++order, + BeginGroup = true + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = entityProvider, + Model = "topic:disclaimer", + Title = GetResource("Disclaimer"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = entityProvider, + Model = "topic:shippinginfo", + Title = GetResource("ShippingReturns"), + DisplayOrder = ++order + }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = serviceMenu.Id, + ProviderName = entityProvider, + Model = "topic:conditionsofuse", + Title = GetResource("ConditionsOfUse"), + DisplayOrder = ++order + }); + + scope.Commit(); + order = 0; + #endregion + + } #region Utilities diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index be03ec78ce..99ed92619e 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -829,8 +829,9 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As>(); builder.RegisterType().As>(); builder.RegisterType().As>(); + builder.RegisterType().As>(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().SingleInstance(); diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 8097f58c9e..54b6572371 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -306,7 +306,10 @@ + + + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs index 43b2d6dcfc..fc9d22326b 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs @@ -1,10 +1,10 @@ using System; -using System.Web.Mvc; using System.ComponentModel; +using System.Web.Mvc; using SmartStore.Core; namespace SmartStore.Web.Framework.UI -{ +{ public class ComponentFactory : IHideObjectMembers { public ComponentFactory(HtmlHelper helper) @@ -56,7 +56,7 @@ public virtual PagerBuilder Pager(int pageCount) public virtual PagerBuilder Pager(IPageable model) { - Guard.NotNull(model, "model"); + Guard.NotNull(model, nameof(model)); return new PagerBuilder(new Pager(), this.HtmlHelper).Model(model); } @@ -70,6 +70,16 @@ public virtual FileUploaderBuilder FileUploader() return new FileUploaderBuilder(new FileUploader(), this.HtmlHelper); } + public virtual MenuBuilder Menu(string name, string viewName = null) + { + Guard.NotEmpty(name, nameof(name)); + + return new MenuBuilder(new Menu(), HtmlHelper) + .Name(name) + .ViewName(viewName) + .WithRenderer(new MenuRenderer()); + } + #endregion } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs new file mode 100644 index 0000000000..06a1f33073 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs @@ -0,0 +1,9 @@ +namespace SmartStore.Web.Framework.UI +{ + public class Menu : Component + { + public override bool NameIsRequired => true; + + public string ViewName { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs new file mode 100644 index 0000000000..f8d6d1b138 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs @@ -0,0 +1,18 @@ +using System.Web.Mvc; + +namespace SmartStore.Web.Framework.UI +{ + public class MenuBuilder : ComponentBuilder, TModel> + { + public MenuBuilder(Menu component, HtmlHelper htmlHelper) + : base(component, htmlHelper) + { + } + + public MenuBuilder ViewName(string value) + { + Component.ViewName = value; + return this; + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs index e69643f095..0e866379e1 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs @@ -3,12 +3,31 @@ using System.Linq; using SmartStore.Collections; using SmartStore.Core.Domain.Cms; +using SmartStore.Core.Localization; +using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.UI { public static class MenuExtensions { - public static IEnumerable> GetBreadcrumb(this TreeNode node) + public static string GetItemText(this TreeNode node, Localizer localizer) + { + string result = null; + + if (node.Value.ResKey.HasValue()) + { + result = localizer(node.Value.ResKey).Text; + } + + if (!result.HasValue() || result.IsCaseInsensitiveEqual(node.Value.ResKey)) + { + result = node.Value.Text; + } + + return result; + } + + public static IEnumerable> GetBreadcrumb(this TreeNode node) { var breadcrumb = node.Trail.Where(x => !x.IsRoot); return breadcrumb; @@ -88,12 +107,23 @@ public static TreeNode GetTree( Guard.NotNull(items, nameof(items)); Guard.NotNull(itemProviders, nameof(itemProviders)); - var root = new TreeNode(new MenuItem()); if (!items.Any()) { - return root; + return new TreeNode(new MenuItem()); } + // Prepare root node. It represents the MenuRecord. + var menu = items.First().Menu; + var rootItem = new MenuItem + { + EntityId = menu.Id, + Text = menu.GetLocalized(x => x.Title) + }; + var root = new TreeNode(rootItem) + { + Id = menu.SystemName + }; + var parent = root; MenuItemRecord prevItem = null; diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs index 118c4390e7..3a235e1cfd 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs @@ -9,6 +9,8 @@ public class MenuModel private TreeNode _selectedNode; private bool _seekedSelectedNode; + public string Name { get; set; } + public TreeNode Root { get; set; } public IList> Path { get; set; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs new file mode 100644 index 0000000000..79307ba8a2 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs @@ -0,0 +1,51 @@ +using System.Web.Mvc.Html; +using SmartStore.Collections; +using SmartStore.Core.Infrastructure; + +namespace SmartStore.Web.Framework.UI +{ + public class MenuRenderer : ViewBasedComponentRenderer + { + protected IMenuService _menuService; + + public MenuRenderer() + : this(EngineContext.Current.Resolve()) + { + } + + public MenuRenderer(IMenuService menuService) + { + _menuService = menuService; + } + + public override void Render() + { + var model = CreateModel(); + HtmlHelper.RenderPartial(GetViewName(), model); + } + + public override string ToHtmlString() + { + var model = CreateModel(); + return HtmlHelper.Partial(GetViewName(), model).ToString(); + } + + protected virtual MenuModel CreateModel() + { + var root = _menuService.GetMenu(Component.Name)?.Root; + + var model = new MenuModel + { + Name = Component.Name, + Root = root ?? new TreeNode(new MenuItem()) + }; + + return model; + } + + protected override string GetViewName() + { + return Component.ViewName.NullEmpty() ?? ("Menus/" + Component.Name); + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs index 686aecfe04..0ef84c00a0 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs @@ -1,9 +1,8 @@ -using System; -using System.Web.Mvc.Html; +using System.Web.Mvc.Html; namespace SmartStore.Web.Framework.UI { - public class ViewBasedComponentRenderer : ComponentRenderer where TComponent : Component + public class ViewBasedComponentRenderer : ComponentRenderer where TComponent : Component { private readonly string _viewName; @@ -12,21 +11,26 @@ public ViewBasedComponentRenderer() { } - public ViewBasedComponentRenderer(string viewName) + public ViewBasedComponentRenderer(string viewName) { Guard.NotEmpty(viewName, nameof(viewName)); _viewName = viewName; } - public override void Render() + protected virtual string GetViewName() + { + return "Components/" + _viewName; + } + + public override void Render() { - HtmlHelper.RenderPartial("Components/" + _viewName, base.Component); + HtmlHelper.RenderPartial(GetViewName(), Component); } public override string ToHtmlString() { - return HtmlHelper.Partial("Components/" + _viewName, base.Component).ToString(); + return HtmlHelper.Partial(GetViewName(), Component).ToString(); } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs index acd18c1cc0..20b660a9ef 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using FluentValidation; using FluentValidation.Attributes; @@ -19,6 +20,13 @@ public class MenuRecordModel : TabbableModel, ILocalizedModel model.IsSystemMenu) @Html.ValidationSummary(false) +@*TODO:*@ +@Html.HiddenFor(model => model.Template) +@Html.HiddenFor(model => model.WidgetZone) + @Html.SmartStore().TabStrip().Name("menu-edit").Style(TabsStyle.Material).Items(x => { x.Add().Text(T("Admin.Common.General").Text).Content(TabInfo()).Selected(true); diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml index 55f7b11608..a7c333f40a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml @@ -14,23 +14,6 @@ } return node.Value.GenerateUrl(this.ViewContext.RequestContext); } - - public string GetItemText(TreeNode node) - { - string result = null; - - if (node.Value.ResKey.HasValue()) - { - result = T(node.Value.ResKey).ToHtmlString(); - } - - if (!result.HasValue() || result.IsCaseInsensitiveEqual(node.Value.ResKey)) - { - result = node.Value.Text; - } - - return result; - } }
    From 610fcaff80b15a785c4de6e196f13becab196d65 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 9 Apr 2019 19:43:09 +0200 Subject: [PATCH 118/460] Reverted removal of MenuExtensions.GetBreadcrumb() helper method --- .../UI/Components/Menu/MenuExtensions.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs index 57fdced645..83ac9f6314 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs @@ -10,7 +10,14 @@ namespace SmartStore.Web.Framework.UI { public static class MenuExtensions { - public static string GetItemText(this TreeNode node, Localizer localizer) + public static IEnumerable> GetBreadcrumb(this TreeNode node) + { + Guard.NotNull(node, nameof(node)); + + return node.Trail.Where(x => !x.IsRoot); + } + + public static string GetItemText(this TreeNode node, Localizer localizer) { string result = null; From d1f009fbaf1630c81d6cdb7e40e3ce9ecbaa1470 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 9 Apr 2019 20:57:52 +0200 Subject: [PATCH 119/460] Create EfCache directory before accessing it ;-) --- src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs | 3 ++- .../{EfViewCacheFactory.cs => EfMappingViewCacheFactory.cs} | 6 +++--- .../SmartStore.Data/Setup/MigrateDatabaseInitializer.cs | 2 +- src/Libraries/SmartStore.Data/SmartDbConfiguration.cs | 4 +++- src/Libraries/SmartStore.Data/SmartStore.Data.csproj | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) rename src/Libraries/SmartStore.Data/Caching/{EfViewCacheFactory.cs => EfMappingViewCacheFactory.cs} (96%) diff --git a/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs index bd818c67c7..393becfbf3 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs @@ -13,7 +13,8 @@ public sealed class EfDbModelStore : DefaultDbModelStore { public EfDbModelStore(string location) : base(location) - { } + { + } public override DbCompiledModel TryLoad(Type contextType) { diff --git a/src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs similarity index 96% rename from src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs rename to src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs index 57164e83f0..021da40be7 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfViewCacheFactory.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs @@ -13,7 +13,7 @@ namespace SmartStore.Data.Caching { - public class EfViewCacheFactory : DbMappingViewCacheFactory + public class EfMappingViewCacheFactory : DbMappingViewCacheFactory { private readonly static ConcurrentDictionary _lookup = new ConcurrentDictionary(); @@ -21,7 +21,7 @@ public class EfViewCacheFactory : DbMappingViewCacheFactory private readonly string _viewFilePath; private readonly static object _lock = new object(); - private EfViewCacheFactory(string viewFilePath) + private EfMappingViewCacheFactory(string viewFilePath) { _viewFilePath = viewFilePath; } @@ -56,7 +56,7 @@ public static void SetContext(DbContext ctx) var itemCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); var type = ctx.GetType(); var filePath = Path.Combine(CommonHelper.MapPath("~/App_Data/EfCache"), type.Namespace + "." + type.Name + ".views"); - var factory = new EfViewCacheFactory(filePath); + var factory = new EfMappingViewCacheFactory(filePath); itemCollection.MappingViewCacheFactory = factory; _lookup.TryAdd(factory, itemCollection); diff --git a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs index 8ca368e692..c55b10a240 100644 --- a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs @@ -98,7 +98,7 @@ public virtual void InitializeDatabase(TContext context) else { // DB is up-to-date and no migration ran. - EfViewCacheFactory.SetContext(context); + EfMappingViewCacheFactory.SetContext(context); if (config is MigrationsConfiguration coreConfig && context is SmartObjectContext) { diff --git a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs index 58004bc315..3f6c70b9ba 100644 --- a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs +++ b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs @@ -17,7 +17,9 @@ public class SmartDbConfiguration : DbConfiguration public SmartDbConfiguration() { // DB model caching - var modelStore = new EfDbModelStore(CommonHelper.MapPath("~/App_Data/EfCache")); + var cacheLocation = CommonHelper.MapPath("~/App_Data/EfCache"); + System.IO.Directory.CreateDirectory(cacheLocation); + var modelStore = new EfDbModelStore(cacheLocation); AddDependencyResolver(new SingletonDependencyResolver(modelStore)); IEfDataProvider provider = null; diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index f21bb6af13..2c42cb1c73 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -132,7 +132,7 @@ - + From 12fe19f4e1a56514a577421f0ab2e245e0063132 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 10 Apr 2019 12:02:19 +0200 Subject: [PATCH 120/460] Menu builder --- .../UI/Components/Menu/MenuRenderer.cs | 14 ++-- .../UI/Menus/DatabaseMenu.cs | 78 +++++++++++++++++-- .../UI/Menus/IMenu.cs | 41 +++++----- .../UI/Menus/MenuBase.cs | 7 +- .../Controllers/CatalogHelper.cs | 4 +- .../SmartStore.Web/SmartStore.Web.csproj | 1 + .../Views/Shared/Layouts/_Layout.cshtml | 5 +- .../Views/Shared/Menus/Main.cshtml | 62 +++++++++++++++ .../Views/Shared/Menus/Navbar.cshtml | 6 +- 9 files changed, 179 insertions(+), 39 deletions(-) create mode 100644 src/Presentation/SmartStore.Web/Views/Shared/Menus/Main.cshtml diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs index 79307ba8a2..4daf9ded38 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs @@ -1,5 +1,4 @@ using System.Web.Mvc.Html; -using SmartStore.Collections; using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.UI @@ -32,13 +31,16 @@ public override string ToHtmlString() protected virtual MenuModel CreateModel() { - var root = _menuService.GetMenu(Component.Name)?.Root; + var model = new MenuModel { Name = Component.Name }; + var menu = _menuService.GetMenu(Component.Name); - var model = new MenuModel + if (menu != null) { - Name = Component.Name, - Root = root ?? new TreeNode(new MenuItem()) - }; + model.Root = menu.Root; + model.Path = menu.GetCurrentBreadcrumb(); + + menu.ResolveElementCounts(model.SelectedNode, false); + } return model; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs index c1146e3d2a..4dcfa712f7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/DatabaseMenu.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Web; using SmartStore.Collections; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Logging; using SmartStore.Services; +using SmartStore.Services.Catalog; using SmartStore.Services.Cms; using SmartStore.Services.Search; @@ -21,8 +23,10 @@ internal class DatabaseMenu : MenuBase private static object s_lock = new object(); private readonly Lazy _catalogSearchService; + private readonly Lazy _categoryService; private readonly IMenuStorage _menuStorage; private readonly ILogger _logger; + private readonly HttpContextBase _httpContext; private readonly DbQuerySettings _querySettings; private readonly Lazy _catalogSettings; private readonly IDictionary> _menuItemProviders; @@ -31,8 +35,10 @@ public DatabaseMenu( string menuName, ICommonServices services, Lazy catalogSearchService, + Lazy categoryService, IMenuStorage menuStorage, ILogger logger, + HttpContextBase httpContext, IMenuPublisher menuPublisher, DbQuerySettings querySettings, Lazy catalogSettings, @@ -44,8 +50,10 @@ public DatabaseMenu( Services = services; _catalogSearchService = catalogSearchService; + _categoryService = categoryService; _menuStorage = menuStorage; _logger = logger; + _httpContext = httpContext; MenuPublisher = menuPublisher; _querySettings = querySettings; _catalogSettings = catalogSettings; @@ -58,29 +66,26 @@ public DatabaseMenu( public override void ResolveElementCounts(TreeNode curNode, bool deep = false) { - if (curNode == null) + if (curNode == null || Name != "Main" || !_catalogSettings.Value.ShowCategoryProductNumber) { return; } try { - // Only consider child elements of type category. - Func, bool> predicate = x => !x.Value.ElementsCount.HasValue && x.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category)); - using (Services.Chronometer.Step($"DatabaseMenu.ResolveElementsCount() for {curNode.Value.Text.EmptyNull()}")) { // Perf: only resolve counts for categories in the current path. while (curNode != null) { - if (curNode.Children.Any(predicate)) + if (curNode.Children.Any(x => !x.Value.ElementsCount.HasValue)) { lock (s_lock) { - if (curNode.Children.Any(predicate)) + if (curNode.Children.Any(x => !x.Value.ElementsCount.HasValue)) { var nodes = deep ? curNode.SelectNodes(x => true, false) : curNode.Children.AsEnumerable(); - nodes = nodes.Where(x => x.Value.EntityId != 0 && x.Value.EntityName.IsCaseInsensitiveEqual(nameof(Category))); + nodes = nodes.Where(x => x.Value.EntityId != 0); foreach (var node in nodes) { @@ -123,6 +128,65 @@ public override void ResolveElementCounts(TreeNode curNode, bool deep } } + public override IList> GetCurrentBreadcrumb() + { + if (Name != "Main" || !_httpContext.TryGetRouteData(out var rd)) + { + return base.GetCurrentBreadcrumb(); + } + + var controller = rd.Values["controller"] as string; + var action = rd.Values["action"] as string; + var currentCategoryId = 0; + var currentProductId = 0; + + if (controller.IsCaseInsensitiveEqual("catalog") && action.IsCaseInsensitiveEqual("category")) + { + currentCategoryId = rd.Values["categoryId"]?.ToString()?.ToInt() ?? 0; + } + if (controller.IsCaseInsensitiveEqual("product") && action.IsCaseInsensitiveEqual("productdetails")) + { + currentProductId = rd.Values["productId"]?.ToString()?.ToInt() ?? 0; + } + + if (currentCategoryId == 0 && currentProductId == 0) + { + return base.GetCurrentBreadcrumb(); + } + + var cacheKey = $"sm.temp.category.breadcrumb.{currentCategoryId}-{currentProductId}"; + var breadcrumb = Services.RequestCache.Get(cacheKey, () => + { + var root = Root; + TreeNode node = null; + + if (currentCategoryId > 0) + { + node = root.SelectNodeById(currentCategoryId) ?? root.SelectNode(x => x.Value.EntityId == currentCategoryId); + } + + if (node == null && currentProductId > 0) + { + var productCategories = _categoryService.Value.GetProductCategoriesByProductId(currentProductId); + if (productCategories.Any()) + { + currentCategoryId = productCategories[0].Category.Id; + node = root.SelectNodeById(currentCategoryId) ?? root.SelectNode(x => x.Value.EntityId == currentCategoryId); + } + } + + if (node != null) + { + var path = node.GetBreadcrumb().ToList(); + return path; + } + + return new List>(); + }); + + return breadcrumb; + } + protected override TreeNode Build() { var entities = _menuStorage.GetMenuItems(Name, Services.StoreContext.CurrentStore.Id); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs index 064cac585e..98ee18b71d 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - /// - /// A hierarchical navigation menu - /// - public interface IMenu // INFO: replaces ISiteMap + /// + /// A hierarchical navigation menu + /// + public interface IMenu // INFO: replaces ISiteMap { /// /// Gets the menu system name (e.g. main, footer, service, admin etc.) @@ -25,23 +24,29 @@ public interface IMenu // INFO: replaces ISiteMap bool ApplyPermissions { get; } /// - /// Resolves the contained elements count (e.g. the products count in the catalog sitemap) + /// Resolves the contained elements count (e.g. the products count on a category page). /// /// The current node /// false resolves counts for direct children of only, true traverses the whole sub-tree void ResolveElementCounts(TreeNode curNode, bool deep = false); - /// - /// Gets all cached trees from the underlying cache storage - /// - /// A dictionary of trees (Key: cache key, Value: tree instance) - /// - /// Multiple trees are created per sitemap depending - /// on language, customer-(roles), store and other parameters. - /// This method does not create anything, but returns all - /// previously processed and cached sitemap variations. - /// - IDictionary> GetAllCachedMenus(); + /// + /// Gets the current breadcrumb (e.g. for the current category page). + /// + /// Current breadcrumb if available, otherwise an empty list. + IList> GetCurrentBreadcrumb(); + + /// + /// Gets all cached trees from the underlying cache storage + /// + /// A dictionary of trees (Key: cache key, Value: tree instance) + /// + /// Multiple trees are created per sitemap depending + /// on language, customer-(roles), store and other parameters. + /// This method does not create anything, but returns all + /// previously processed and cached sitemap variations. + /// + IDictionary> GetAllCachedMenus(); /// /// Removes the menu from the application cache diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs index afb1d5d341..460025872e 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs @@ -104,7 +104,12 @@ public virtual void ResolveElementCounts(TreeNode curNode, bool deep = { } - public IDictionary> GetAllCachedMenus() + public virtual IList> GetCurrentBreadcrumb() + { + return new List>(); + } + + public IDictionary> GetAllCachedMenus() { var cache = Services.Cache; var keys = cache.Keys(MENU_PATTERN_KEY.FormatInvariant(this.Name)); diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index 96c7c71926..1b30cc95e7 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -1314,14 +1314,14 @@ public IList> GetCategoryBreadCrumb(int currentCategoryId, in if (node != null) { - var path = node.Trail.Where(x => !x.IsRoot).ToList(); + var path = node.GetBreadcrumb().ToList(); return path; } return new List>(); }); - return breadcrumb; + return breadcrumb; } public IList PrepareProductSpecificationModel(Product product) diff --git a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj index 7fbb031404..67996065ef 100644 --- a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj +++ b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj @@ -1822,6 +1822,7 @@ + Web.config diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml index f0d14c3065..f9fc9f1cb0 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml @@ -57,9 +57,10 @@
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Main.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Main.cshtml new file mode 100644 index 0000000000..de61e7cfe8 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Main.cshtml @@ -0,0 +1,62 @@ +@model MenuModel + +@using SmartStore.Collections; + +@{ + Layout = ""; + + var cutOffItems = Model.Root.Children + .Where(x => x.Value.Id == null && x.GetMetadata("spare", false)) + .ToList(); +} + +
    + @{ Html.RenderPartial("Menus/Navbar"); } +
    + +
    + @foreach (var node in Model.Root.Children) + { + + } + + @if (cutOffItems.Any()) + { + + } +
    + +@helper CreateDrilldownMenu(IEnumerable> nodes, bool isRoot) +{ + if (isRoot) + { + @Html.Raw("") + } +} + + \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml index 042dad0ff9..7d74899d2c 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml @@ -70,15 +70,15 @@ continue; } + var itemState = node.GetNodePathState(Model.Path); + var isMegaMenuActive = node.GetMetadata("IsMegaMenuActive", false); + var isDropDownActive = (isMegaMenuActive == null ? true : (bool)isMegaMenuActive) && node.HasChildren; var itemText = node.GetItemText(T); var itemUrl = item.GenerateUrl(this.Url); var relAttr = item.LinkHtmlAttributes.TryGetValue("rel", out obj) ? (string)obj : string.Empty; var target = item.LinkHtmlAttributes.TryGetValue("target", out obj) ? (string)obj : string.Empty; var cssClass = item.LinkHtmlAttributes.TryGetValue("class", out obj) ? (string)obj : string.Empty; var shortDescription = item.LinkHtmlAttributes.TryGetValue("title", out obj) ? (string)obj : string.Empty; - var itemState = node.GetNodePathState(Model.Path); - var isMegaMenuActive = node.GetMetadata("IsMegaMenuActive", false); - var isDropDownActive = (isMegaMenuActive == null ? true : (bool)isMegaMenuActive) && node.HasChildren; if (item.IsGroupHeader) { From 5917eb5458e87645ed9594bd7a74df3e2b3147d1 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 10 Apr 2019 14:07:09 +0200 Subject: [PATCH 121/460] Added CSS for display of one product in product lists --- .../SmartStore.Web/Themes/Flex/Content/_artlist.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_artlist.scss b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_artlist.scss index dc6da7dfab..1bfe0452cc 100644 --- a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_artlist.scss +++ b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_artlist.scss @@ -619,6 +619,13 @@ $art-border-color: #ddd; padding-right: $grid-gutter-width / 2; } + @include media-breakpoint-up(xs) { + &.artlist-1-col .art { + flex-basis: 100% !important; + max-width: initial !important; + } + } + @include media-breakpoint-up(sm) { &.artlist-4-cols, &.artlist-5-cols, From d21192a10b43e32a12d4272c45aa9ae2fd157b34 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 10 Apr 2019 14:25:35 +0200 Subject: [PATCH 122/460] Added JsonIgnore to IBindableBlock properties --- .../SmartStore.Services/Cms/Blocks/IBindableBlock.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs index 57fd5a08d4..3638b5e31c 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs @@ -61,6 +61,7 @@ public abstract class BindableBlockBase : IBindableBlock public virtual string BindEntityName { get; set; } public virtual int? BindEntityId { get; set; } + [JsonIgnore] public bool CanBind { get @@ -69,13 +70,16 @@ public bool CanBind } } + [JsonIgnore] public bool IsLoaded { get { return DataItem != null; } } + [JsonIgnore] public IDictionary DataItem { get; set; } + [JsonIgnore] public bool IsBound { get; set; } public void Reset() From 67cf883ec4ea3c777d71cae65f1e29f0b0eefb88 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 10 Apr 2019 16:56:15 +0200 Subject: [PATCH 123/460] Minor FontAwesome fix --- src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss index a26b0e2eef..4d0f1d6325 100644 --- a/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss +++ b/src/Presentation/SmartStore.Web/Themes/Flex/Content/_menu.scss @@ -35,7 +35,7 @@ $ocm-selected-color: $warning; .catmenu-path-item.expanded { color: $body-color; - &:after { content: '\f148'; font-size: inherit; } + &:after { content: '\f3bf'; font-size: inherit; } } .catmenu-path-item.active { From 24f940cda6ab19b0264040fc21c43353633113ce Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 10 Apr 2019 16:56:09 +0200 Subject: [PATCH 124/460] Minor correction --- .../UI/Menus/Providers/MenuItemProviderBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs index 92e6c7b99b..c089a065ef 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/Providers/MenuItemProviderBase.cs @@ -37,7 +37,7 @@ protected virtual MenuItem ConvertToMenuItem(MenuItemProviderRequest request) { var entity = request.Entity; var title = entity.GetLocalized(x => x.Title); - var shortDescription = entity.GetLocalized(x => x.ShortDescription); + string shortDescription = entity.GetLocalized(x => x.ShortDescription); var menuItem = new MenuItem { From 364538d6b2ff51a8c6cee80da31fb3018de90ccb Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 11 Apr 2019 13:25:22 +0200 Subject: [PATCH 125/460] Please execute update-database -TargetMigration TopicHtmlIdAndBodyCss --- .../SmartStore.Core/Domain/Cms/MenuRecord.cs | 23 +++- .../Migrations/201904071537124_Menus.resx | 126 ------------------ ...r.cs => 201904110735029_Menus.Designer.cs} | 2 +- ...7124_Menus.cs => 201904110735029_Menus.cs} | 1 + .../Migrations/201904110735029_Menus.resx | 126 ++++++++++++++++++ .../Migrations/MigrationsConfiguration.cs | 5 + .../SmartStore.Data/SmartStore.Data.csproj | 10 +- .../Cms/Menus/IMenuStorage.cs | 9 ++ .../SmartStore.Services/Cms/Menus/MenuInfo.cs | 11 ++ .../Cms/Menus/MenuStorage.cs | 78 ++++++++++- .../SmartStore.Services.csproj | 1 + .../DependencyRegistrar.cs | 5 + .../SmartStore.Web.Framework.csproj | 1 + .../UI/Menus/IMenuService.cs | 7 +- .../UI/Menus/MenuBase.cs | 9 +- .../UI/Menus/MenuService.cs | 32 ++++- .../UI/Menus/UserMenuFilter.cs | 36 +++++ .../Models/Menus/MenuRecordModel.cs | 3 + .../Views/Menu/_CreateOrUpdate.cshtml | 9 ++ .../Controllers/CommonController.cs | 20 ++- .../SmartStore.Web/SmartStore.Web.csproj | 1 + .../Views/Common/Partials/UserMenu.cshtml | 3 + .../Views/Shared/Menus/Dropdown.cshtml | 26 +++- 23 files changed, 390 insertions(+), 154 deletions(-) delete mode 100644 src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx rename src/Libraries/SmartStore.Data/Migrations/{201904071537124_Menus.Designer.cs => 201904110735029_Menus.Designer.cs} (93%) rename src/Libraries/SmartStore.Data/Migrations/{201904071537124_Menus.cs => 201904110735029_Menus.cs} (98%) create mode 100644 src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.resx create mode 100644 src/Libraries/SmartStore.Services/Cms/Menus/MenuInfo.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs create mode 100644 src/Presentation/SmartStore.Web/Views/Common/Partials/UserMenu.cshtml diff --git a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs index f9ad7a57bc..c8ebedf1c1 100644 --- a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; @@ -60,6 +62,11 @@ public virtual ICollection Items [Index("IX_Menu_Published")] public bool Published { get; set; } = true; + /// + /// Gets or sets the order for widget registration. + /// + public int DisplayOrder { get; set; } + /// /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores. /// @@ -71,5 +78,19 @@ public virtual ICollection Items ///
    [Index("IX_Menu_SubjectToAcl")] public bool SubjectToAcl { get; set; } + + /// + /// Helper function to get the comma-separated WidgetZone property as an enumerable of strings. + /// + /// Widget zones. + public IEnumerable GetWidgetZones() + { + if (WidgetZone.IsEmpty()) + { + return Enumerable.Empty(); + } + + return WidgetZone.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()); + } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx b/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx deleted file mode 100644 index efd7e90a61..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 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 - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.Designer.cs similarity index 93% rename from src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs rename to src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.Designer.cs index ce3dec458c..b943551be8 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.Designer.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.Designer.cs @@ -13,7 +13,7 @@ public sealed partial class Menus : IMigrationMetadata string IMigrationMetadata.Id { - get { return "201904071537124_Menus"; } + get { return "201904110735029_Menus"; } } string IMigrationMetadata.Source diff --git a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs similarity index 98% rename from src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs rename to src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs index 92c5d19baa..77ae35f704 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201904071537124_Menus.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs @@ -21,6 +21,7 @@ public override void Up() WidgetZone = c.String(maxLength: 4000), Title = c.String(maxLength: 400), Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), LimitedToStores = c.Boolean(nullable: false), SubjectToAcl = c.Boolean(nullable: false), }) diff --git a/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.resx b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.resx new file mode 100644 index 0000000000..3c944a5f47 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 7eeccd35cb..cc86bbb6cb 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -784,6 +784,11 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Specifies whether the menu is visible in the shop.", "Legt fest, ob das Menü im Shop sichtbar ist."); + builder.AddOrUpdate("Admin.ContentManagement.Menus.DisplayOrder", + "Display order", + "Reihenfolge", + "Specifies the display order of the widget zones.", + "Legt die Darstellungreihenfolge der Widget Zonen fest."); builder.AddOrUpdate("Admin.ContentManagement.Menus.Item.ParentItem", "Parent menu item", diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 2c42cb1c73..4be41259d5 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -657,9 +657,9 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - - 201904071537124_Menus.cs + + + 201904110735029_Menus.cs @@ -1182,8 +1182,8 @@ 201902211855242_TopicHtmlIdAndBodyCss.cs - - 201904071537124_Menus.cs + + 201904110735029_Menus.cs diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs index ca24cca72b..f8b0bf386a 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using SmartStore.Core; using SmartStore.Core.Domain.Cms; +using SmartStore.Core.Domain.Customers; namespace SmartStore.Services.Cms { @@ -40,6 +41,14 @@ IPagedList GetAllMenus( int pageIndex = 0, int pageSize = int.MaxValue); + /// + /// Gets info about user menus. + /// + /// Customer roles to check access for. null to use current customer's roles. + /// Store identifier. 0 to use current store. + /// Menu infos. + IList GetUserMenusInfo(IEnumerable roles = null, int storeId = 0); + /// /// Gets a menu by identifier. /// diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuInfo.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuInfo.cs new file mode 100644 index 0000000000..afabc97e8b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuInfo.cs @@ -0,0 +1,11 @@ +namespace SmartStore.Services.Cms +{ + public class MenuInfo + { + public int Id { get; set; } + public string SystemName { get; set; } + public string Template { get; set; } + public string[] WidgetZones { get; set; } + public int DisplayOrder { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 39e6fa2fef..63f01d7862 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -1,10 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Data.Entity; using System.Linq; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Cms; +using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; using SmartStore.Data.Caching; @@ -14,6 +16,8 @@ namespace SmartStore.Services.Cms public partial class MenuStorage : IMenuStorage { private const string MENU_SYSTEMNAME_CACHE_KEY = "MenuStorage:SystemNames"; + private const string MENU_USER_CACHE_KEY = "MenuStorage:Menus:User-{0}-{1}"; + private const string MENU_PATTERN_KEY = "MenuStorage:Menus:*"; private readonly IRepository _menuRepository; private readonly IRepository _menuItemRepository; @@ -52,6 +56,8 @@ public virtual void InsertMenu(MenuRecord menu) { systemNames.Add(menu.SystemName); } + + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); } public virtual void UpdateMenu(MenuRecord menu) @@ -77,6 +83,8 @@ public virtual void UpdateMenu(MenuRecord menu) systemNames.Add(menu.SystemName); } } + + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); } public virtual void DeleteMenu(MenuRecord menu) @@ -91,6 +99,8 @@ public virtual void DeleteMenu(MenuRecord menu) { systemNames.Remove(menu.SystemName); } + + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); } public virtual IPagedList GetAllMenus( @@ -100,10 +110,57 @@ public virtual IPagedList GetAllMenus( int pageIndex = 0, int pageSize = int.MaxValue) { - var query = BuildMenuQuery(0, systemName, storeId, includeHidden); + var query = BuildMenuQuery(0, storeId, systemName, null, includeHidden); return new PagedList(query, pageIndex, pageSize); } + public virtual IList GetUserMenusInfo(IEnumerable roles = null, int storeId = 0) + { + if (roles == null) + { + roles = _services.WorkContext.CurrentCustomer.CustomerRoles; + } + + if (storeId == 0) + { + storeId = _services.StoreContext.CurrentStore.Id; + } + + var cacheKey = MENU_USER_CACHE_KEY.FormatInvariant(storeId, string.Join(",", roles.Where(x => x.Active).Select(x => x.Id))); + + var userMenusInfo = _services.Cache.Get(cacheKey, () => + { + var query = BuildMenuQuery(0, storeId, null, false, false, true, false, true); + + var data = query.Select(x => new + { + x.Id, + x.SystemName, + x.Template, + x.WidgetZone, + x.DisplayOrder + }) + .ToList(); + + var result = data.Select(x => new MenuInfo + { + Id = x.Id, + SystemName = x.SystemName, + Template = x.Template, + DisplayOrder = x.DisplayOrder, + WidgetZones = x.WidgetZone.EmptyNull().Trim() + .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(y => y.Trim()) + .ToArray() + }) + .ToList(); + + return result; + }); + + return userMenusInfo; + } + public virtual MenuRecord GetMenuById(int id) { if (id == 0) @@ -245,15 +302,19 @@ private ISet GetMenuSystemNames(bool create) protected virtual IQueryable BuildMenuQuery( int id, - string systemName, int storeId, + string systemName, + bool? isSystemMenu, bool includeHidden, bool groupBy = true, - bool sort = true) + bool sort = true, + bool untracked = false) { var applied = false; var entityName = nameof(MenuRecord); - var query = _menuRepository.Table.Where(x => includeHidden || x.Published); + var query = untracked ? _menuRepository.TableUntracked : _menuRepository.Table; + + query = query.Where(x => includeHidden || x.Published); if (id != 0) { @@ -265,6 +326,11 @@ protected virtual IQueryable BuildMenuQuery( query = query.Where(x => x.SystemName == systemName); } + if (isSystemMenu.HasValue) + { + query = query.Where(x => x.IsSystemMenu == isSystemMenu.Value); + } + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) { query = @@ -317,7 +383,7 @@ protected virtual IQueryable BuildMenuItemQuery( bool includeHidden) { var singleMenu = menuId != 0 || (systemName.HasValue() && storeId != 0); - var menuQuery = BuildMenuQuery(menuId, systemName, storeId, includeHidden, !singleMenu, !singleMenu); + var menuQuery = BuildMenuQuery(menuId, storeId, systemName, null, includeHidden, !singleMenu, !singleMenu); if (singleMenu) { diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 7e4b214e9b..5b2b02b73a 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -210,6 +210,7 @@ + diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 99ed92619e..dd8957d1af 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -873,6 +873,11 @@ protected override void Load(ContainerBuilder builder) m.For(em => em.AppendsMultipleItems, attribute.AppendsMultipleItems); }); } + + if (DataSettings.DatabaseIsInstalled()) + { + builder.RegisterType().AsActionFilterFor(); + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 54b6572371..168bbae5bf 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -390,6 +390,7 @@ + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs index 5fed9fd0bd..069e8d3059 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs @@ -10,5 +10,10 @@ public partial interface IMenuService // INFO: replaces ISiteMapService TreeNode GetCurrentNode(string menuName, ControllerContext controllerContext); void ResolveElementCounts(string menuName, TreeNode curNode, bool deep = false); void ClearCache(string menuName); - } + + /// + /// Registers actions to render user menus in widget zones. + /// + void ProcessMenus(); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs index 460025872e..843650ffbd 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuBase.cs @@ -37,22 +37,21 @@ public TreeNode Root { var root = Build(); - // Menu publisher MenuPublisher.RegisterMenus(root, this.Name); - // Apply permission if (this.ApplyPermissions) { DoApplyPermissions(root); } - Services.EventPublisher.Publish(new MenuBuiltEvent(this.Name, root)); - return root; } }); - return rootNode; + // Always fire event to be able to add items programmatically. + Services.EventPublisher.Publish(new MenuBuiltEvent(this.Name, rootNode)); + + return rootNode; } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs index 82c43106d3..debed39839 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs @@ -2,18 +2,30 @@ using System.Linq; using System.Web.Mvc; using SmartStore.Collections; +using SmartStore.Core.Logging; +using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI.Menus { public partial class MenuService : IMenuService { + protected readonly IChronometer _chronometer; + protected readonly IWidgetProvider _widgetProvider; + protected readonly IMenuStorage _menuStorage; protected readonly IMenuResolver[] _menuResolvers; private TreeNode _currentNode; private bool _currentNodeResolved; - public MenuService(IEnumerable menuResolvers) + public MenuService( + IChronometer chronometer, + IWidgetProvider widgetProvider, + IMenuStorage menuStorage, + IEnumerable menuResolvers) { + _chronometer = chronometer; + _widgetProvider = widgetProvider; + _menuStorage = menuStorage; _menuResolvers = menuResolvers.OrderBy(x => x.Order).ToArray(); } @@ -62,5 +74,23 @@ public virtual void ClearCache(string menuName) { GetMenu(menuName)?.ClearCache(); } + + public virtual void ProcessMenus() + { + using (_chronometer.Step("ProcessMenus")) + { + var menusInfo = _menuStorage.GetUserMenusInfo(); + + foreach (var info in menusInfo) + { + _widgetProvider.RegisterAction( + info.WidgetZones, + "UserMenu", + "Common", + new { area = "", systemName = info.SystemName, template = info.Template }, + info.DisplayOrder); + } + } + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs new file mode 100644 index 0000000000..f468f251e5 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs @@ -0,0 +1,36 @@ +using System.Web.Mvc; + +namespace SmartStore.Web.Framework.UI +{ + public class UserMenuFilter : IActionFilter, IResultFilter + { + private readonly IMenuService _menuService; + + public UserMenuFilter(IMenuService menuService) + { + _menuService = menuService; + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { + if (filterContext.IsChildAction || !filterContext.Result.IsHtmlViewResult()) + { + return; + } + + _menuService.ProcessMenus(); + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } +} diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs index 88ddbacb23..d37a76bb3e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Menus/MenuRecordModel.cs @@ -35,6 +35,9 @@ public class MenuRecordModel : TabbableModel, ILocalizedModel model.WidgetZone)
    +
    +
    + @Html.SmartLabelFor(model => model.DisplayOrder) +
    +
    + @Html.EditorFor(model => model.DisplayOrder) + @Html.ValidationMessageFor(model => model.DisplayOrder) +
    +
    @(Html.LocalizedEditor("menu-info-localized", diff --git a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs index 73e72c76cb..c1d5d15157 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs @@ -883,7 +883,21 @@ public ActionResult GdprConsent(bool isSmall) return PartialView(model); } - - #endregion - } + + [ChildActionOnly] + public ActionResult UserMenu(string systemName, string template) + { + if (systemName.HasValue() && template.HasValue()) + { + ViewBag.SystemName = systemName; + ViewBag.Template = template; + + return PartialView(); + } + + return new EmptyResult(); + } + + #endregion + } } diff --git a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj index 67996065ef..b3ce3503dc 100644 --- a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj +++ b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj @@ -1823,6 +1823,7 @@ + Web.config diff --git a/src/Presentation/SmartStore.Web/Views/Common/Partials/UserMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Common/Partials/UserMenu.cshtml new file mode 100644 index 0000000000..eb2926498b --- /dev/null +++ b/src/Presentation/SmartStore.Web/Views/Common/Partials/UserMenu.cshtml @@ -0,0 +1,3 @@ +@( + Html.SmartStore().Menu((string)ViewBag.SystemName, "Menus/" + ViewBag.Template) +) \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml index a7d2ece600..85ca6af2db 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml @@ -7,12 +7,28 @@ { return; } + + // Support both cases: the dropdown-link is Model.Root or the first item if it has children. + if (root.HasChildren && root.Children.Count == 1 && root.FirstChild.HasChildren) + { + root = root.FirstChild; + } + + object obj = null; + var item = root.Value; + var itemText = root.GetItemText(T); + var linkId = item.LinkHtmlAttributes.TryGetValue("id", out obj) ? (string)obj : string.Empty; + var cssClass = item.LinkHtmlAttributes.TryGetValue("class", out obj) ? (string)obj : string.Empty; + var shortDescription = item.LinkHtmlAttributes.TryGetValue("title", out obj) ? (string)obj : string.Empty; }
    @@ -117,8 +113,6 @@ @{/*
      @{ - Html.RenderWidget("footer_col_2_before"); - var contactUsUrl = Url.RouteUrl("contactus"); var shippingInfoLink = LinkResolver.Resolve("topic:shippinginfo"); var paymentInfoLink = LinkResolver.Resolve("topic:paymentinfo"); @@ -144,8 +138,6 @@ {
    • @paymentInfoLink.Label
    • } - - @{ Html.RenderWidget("footer_col_2_after"); }
    */}
    @@ -162,8 +154,6 @@ @(Html.SmartStore().Menu("FooterCompany", "Menus/LinkList")) @{/*
      - @{ Html.RenderWidget("footer_col_3_before"); } - @foreach (var link in footerLinks) { if (link.Status == LinkStatus.Ok) @@ -176,8 +166,6 @@ {
    • @T("Account.Login")
    • } - - @{ Html.RenderWidget("footer_col_3_after"); }
    */} @@ -226,7 +214,6 @@ @{ Html.RenderWidget("gdpr_consent_small"); } @Html.ValidationMessageFor(x => x.NewsletterEmail) -
    diff --git a/src/Presentation/SmartStore.Web/Views/Common/Partials/ServiceMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Common/Partials/ServiceMenu.cshtml index ea5c00bb7f..fdc9ad70ed 100644 --- a/src/Presentation/SmartStore.Web/Views/Common/Partials/ServiceMenu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Common/Partials/ServiceMenu.cshtml @@ -4,8 +4,6 @@ @using SmartStore.Services.Cms; @{ - Html.RenderWidget("servicemenu_before"); - var links = new List { LinkResolver.Resolve("topic:aboutus"), @@ -41,5 +39,3 @@ @link.Label } } - -@{ Html.RenderWidget("servicemenu_after"); } diff --git a/src/Presentation/SmartStore.Web/Views/Customer/Partials/MyAccountMenu.cshtml b/src/Presentation/SmartStore.Web/Views/Customer/Partials/MyAccountMenu.cshtml index 93182bb528..3742339f4f 100644 --- a/src/Presentation/SmartStore.Web/Views/Customer/Partials/MyAccountMenu.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Customer/Partials/MyAccountMenu.cshtml @@ -13,8 +13,6 @@ @T("Account.Navigation")
    - @{ Html.RenderWidget("myaccount_menu_firstitem_before"); } - @foreach (var item in Model.Root.Children.Where(x => x.Value.Visible).Select(x => x.Value)) { @@ -26,8 +24,6 @@ } } - - @{ Html.RenderWidget("myaccount_menu_lastitem_after"); }
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml index 85ca6af2db..6494e7f358 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Dropdown.cshtml @@ -49,8 +49,6 @@ var hasImages = nodes.Any(x => x.Value.ImageUrl.HasValue()); } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/LinkList.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/LinkList.cshtml index 3eb3344c7e..0e207be9a5 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/LinkList.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/LinkList.cshtml @@ -15,8 +15,6 @@ \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/ListGroup.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/ListGroup.cshtml index 68b5146f51..e9b9b5b6e9 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/ListGroup.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/ListGroup.cshtml @@ -23,8 +23,6 @@ }
    - @*@{ Html.RenderWidget("myaccount_menu_firstitem_before"); }*@ - @foreach (var node in Model.Root.Children.Where(x => x.Value.Visible)) { var item = node.Value; @@ -56,8 +54,6 @@ } } - - @*@{ Html.RenderWidget("myaccount_menu_lastitem_after"); }*@
    \ No newline at end of file From 0ca1b4dc48458fd0321daa66e76c54011caa09bf Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 11 Apr 2019 16:11:51 +0200 Subject: [PATCH 128/460] Menu builder: admin menu does not need an action method anymore --- .../Controllers/CommonController.cs | 16 +--------------- .../Administration/SmartStore.Admin.csproj | 2 +- .../{Partials/Menu.cshtml => Menus/Admin.cshtml} | 11 +++-------- .../Views/Shared/Partials/Navbar.cshtml | 4 ++-- 4 files changed, 7 insertions(+), 26 deletions(-) rename src/Presentation/SmartStore.Web/Administration/Views/Shared/{Partials/Menu.cshtml => Menus/Admin.cshtml} (92%) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index 85c638e01e..b690d6deb9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -72,7 +72,6 @@ public class CommonController : AdminControllerBase private readonly IDbCache _dbCache; private readonly ITaskScheduler _taskScheduler; private readonly ICommonServices _services; - private readonly Lazy _menuService; public CommonController( Lazy paymentService, @@ -91,8 +90,7 @@ public CommonController( IGenericAttributeService genericAttributeService, IDbCache dbCache, ITaskScheduler taskScheduler, - ICommonServices services, - Lazy menuService) + ICommonServices services) { _paymentService = paymentService; _shippingService = shippingService; @@ -111,11 +109,8 @@ public CommonController( _dbCache = dbCache; _taskScheduler = taskScheduler; _services = services; - _menuService = menuService; } - #region Navbar & Menu - [ChildActionOnly] public ActionResult Navbar() { @@ -131,15 +126,6 @@ public ActionResult Navbar() return PartialView(); } - [ChildActionOnly] - public ActionResult Menu() - { - var rootNode = _menuService.Value.GetRootNode("Admin"); - return PartialView(rootNode); - } - - #endregion - #region CheckUpdate public async Task CheckUpdate(bool enforce = false) diff --git a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj index 0cbde3c50a..e121a21320 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -617,7 +617,7 @@ - + Designer diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Menus/Admin.cshtml similarity index 92% rename from src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml rename to src/Presentation/SmartStore.Web/Administration/Views/Shared/Menus/Admin.cshtml index a7c333f40a..b6add8baea 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Menu.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Menus/Admin.cshtml @@ -1,9 +1,6 @@ -@model TreeNode - -@using SmartStore.Services.Security; -@using SmartStore.Core.Domain.Security; -@using SmartStore.Collections; +@using SmartStore.Collections; @using SmartStore.Web.Framework.UI; +@model MenuModel @functions { public string GetNodeLink(TreeNode node) @@ -18,7 +15,7 @@ - @helper CreateDropdown(IEnumerable> nodes, bool isRoot) { var isFirst = true; diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml index a9f312f227..4a665a1c6a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml @@ -27,8 +27,8 @@ From 01609e71446064a8c7b4273777cde26e686bb24f Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 11 Apr 2019 16:22:27 +0200 Subject: [PATCH 129/460] Menu builder: removed obsolete widget zones --- src/Presentation/SmartStore.Web/App_Data/widgetzones.json | 2 -- .../Views/Catalog/Partials/CatalogMenu.Navbar.cshtml | 4 ---- .../SmartStore.Web/Views/Shared/Menus/Navbar.cshtml | 4 ---- 3 files changed, 10 deletions(-) diff --git a/src/Presentation/SmartStore.Web/App_Data/widgetzones.json b/src/Presentation/SmartStore.Web/App_Data/widgetzones.json index d52444c0f1..2890d3e01b 100644 --- a/src/Presentation/SmartStore.Web/App_Data/widgetzones.json +++ b/src/Presentation/SmartStore.Web/App_Data/widgetzones.json @@ -105,8 +105,6 @@ "header_menu_bottom", "shopbar_tools_before", "shopbar_tools_after", - "mainmenu_before_first_item", - "mainmenu_after_last_item", "breadcrumb_before", "breadcrumb_after", "account_dropdown_before", diff --git a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml index a77265ecb5..02ab6d4e76 100644 --- a/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Catalog/Partials/CatalogMenu.Navbar.cshtml @@ -54,8 +54,6 @@ data-current-product-id="@CurrentProductId" data-current-manufacturer-id="@CurrentManufacturerId"> - @{ Html.RenderWidget("mainmenu_before_first_item"); } - @foreach(var node in Model.Root.Children) { var item = node.Value; @@ -104,8 +102,6 @@ } - - @{ Html.RenderWidget("mainmenu_after_last_item"); }
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml index 7d74899d2c..fba673f8c9 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Menus/Navbar.cshtml @@ -56,8 +56,6 @@ data-current-product-id="@CurrentProductId" data-current-manufacturer-id="@CurrentManufacturerId"> - @*@{ Html.RenderWidget("mainmenu_before_first_item"); }*@ - @foreach (var node in root.Children) { var item = node.Value; @@ -128,8 +126,6 @@ } - - @*@{ Html.RenderWidget("mainmenu_after_last_item"); }*@
    From b264ddb4022fd0eb06cfc5ca4268243b9e493544 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Apr 2019 20:02:03 +0200 Subject: [PATCH 130/460] Random numbers sometimes identical... weird! --- .../SmartStore.Core/Utilities/CommonHelper.cs | 15 ++++++----- .../Export/DynamicEntityHelper.cs | 3 ++- .../Messages/MessageFactory.cs | 3 ++- .../Theming/WebViewPage.cs | 26 +++++++++---------- .../Controllers/CommonController.cs | 4 ++- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs b/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs index 2f337b8c39..9a0be968f5 100644 --- a/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs @@ -19,6 +19,7 @@ namespace SmartStore.Utilities public static partial class CommonHelper { private static bool? _isDevEnvironment; + private readonly static Random _random = new Random(); /// /// Generate random digit code @@ -27,12 +28,14 @@ public static partial class CommonHelper /// Result string public static string GenerateRandomDigitCode(int length) { - var random = new Random(); - string str = string.Empty; - for (int i = 0; i < length; i++) - str = String.Concat(str, random.Next(10).ToString()); - return str; - } + var buffer = new int[length]; + for (int i = 0; i < length; ++i) + { + buffer[i] = _random.Next(10); + } + + return string.Join("", buffer); + } /// /// Returns an random interger number within a specified rage diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs index 7258f5d6c9..97cc3004c3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs @@ -31,6 +31,7 @@ using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Seo; +using SmartStore.Utilities; namespace SmartStore.Services.DataExchange.Export { @@ -106,7 +107,7 @@ private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObjec string[] appendText = ctx.Projection.AppendDescriptionText.SplitSafe(","); if (appendText.Length > 0) { - var rnd = new Random().Next(0, appendText.Length - 1); + var rnd = CommonHelper.GenerateRandomInteger(0, appendText.Length - 1); description = description.Grow(appendText.SafeGet(rnd), " "); } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs index 61fb676fbc..a70a2b480e 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs @@ -22,6 +22,7 @@ using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Templating; +using SmartStore.Utilities; namespace SmartStore.Services.Messages { @@ -626,7 +627,7 @@ private object GetModelFromExpression(string expression, IDictionary 0) { // Fetch a random one - var skip = new Random().Next(count); + var skip = CommonHelper.GenerateRandomInteger(0, count); result = query.OrderBy(x => x.Id).Skip(() => skip).FirstOrDefault(); } else diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs index 046c612f70..63cf24718d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs @@ -130,19 +130,19 @@ public override void InitHelpers() public HelperResult RenderWrappedSection(string name, object wrapperHtmlAttributes) { Action action = delegate(TextWriter tw) - { - var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(wrapperHtmlAttributes); - var tagBuilder = new TagBuilder("div"); - tagBuilder.MergeAttributes(htmlAttributes); - - var section = this.RenderSection(name, false); - if (section != null) - { - tw.Write(tagBuilder.ToString(TagRenderMode.StartTag)); - section.WriteTo(tw); - tw.Write(tagBuilder.ToString(TagRenderMode.EndTag)); - } - }; + { + var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(wrapperHtmlAttributes); + var tagBuilder = new TagBuilder("div"); + tagBuilder.MergeAttributes(htmlAttributes); + + var section = this.RenderSection(name, false); + if (section != null) + { + tw.Write(tagBuilder.ToString(TagRenderMode.StartTag)); + section.WriteTo(tw); + tw.Write(tagBuilder.ToString(TagRenderMode.EndTag)); + } + }; return new HelperResult(action); } diff --git a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs index c1d5d15157..93b2cff41e 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs @@ -30,6 +30,7 @@ using SmartStore.Services.Security; using SmartStore.Services.Seo; using SmartStore.Services.Topics; +using SmartStore.Utilities; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Filters; @@ -443,7 +444,8 @@ public ActionResult Footer() var hint = _services.Settings.GetSettingByKey("Rnd_SmCopyrightHint", string.Empty, store.Id); if (hint.IsEmpty()) { - hint = s_hints[new Random().Next(s_hints.Length)]; + hint = s_hints[CommonHelper.GenerateRandomInteger(0, s_hints.Length - 1)]; + _services.Settings.SetSetting("Rnd_SmCopyrightHint", hint, store.Id); } From 274f27cc6226d58448829efb75b1e5ebb586e083 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Apr 2019 21:55:45 +0200 Subject: [PATCH 131/460] Typo --- .../SmartStore.Web.Framework/DependencyRegistrar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index dd8957d1af..7f01ddd955 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -283,7 +283,7 @@ protected override void AttachToComponentRegistration(IComponentRegistry compone if (servicesProperty == null) return; - + registration.Metadata.Add("Property.ICommonServices", new FastProperty(servicesProperty)); registration.Activated += (sender, e) => @@ -1224,8 +1224,8 @@ class SettingsSource : IRegistrationSource BindingFlags.Static | BindingFlags.NonPublic); public IEnumerable RegistrationsFor( - Service service, - Func> registrations) + Service service, + Func> registrations) { if (service is TypedService ts && typeof(ISettings).IsAssignableFrom(ts.ServiceType)) { From d052a5baca72990201aced7822197a099cdae755 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 11 Apr 2019 21:08:35 +0200 Subject: [PATCH 132/460] Menu builder: menu cache invalidation required when MenuRecord or MenuItemRecord changes Fixes switching a menu from not published to published shows no effect in the frontend --- .../Cms/Menus/MenuStorage.cs | 11 +++- .../DependencyRegistrar.cs | 1 - .../SmartStore.Web.Framework.csproj | 1 + .../UI/Menus/MenuInvalidator.cs | 64 +++++++++++++++++++ .../UI/Menus/MenuService.cs | 2 +- 5 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuInvalidator.cs diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 63f01d7862..0d4db0a440 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -73,9 +73,16 @@ public virtual void UpdateMenu(MenuRecord menu) var systemNames = GetMenuSystemNames(false); if (systemNames != null) { - if (modProps.TryGetValue(nameof(menu.Published), out var original) && original.Convert() == true) + if (modProps.TryGetValue(nameof(menu.Published), out var original)) { - systemNames.Remove(menu.SystemName); + if (original.Convert() == true) + { + systemNames.Remove(menu.SystemName); + } + else + { + systemNames.Add(menu.SystemName); + } } else if (modProps.TryGetValue(nameof(menu.SystemName), out original)) { diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 7f01ddd955..37635f7b2c 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -89,7 +89,6 @@ using SmartStore.Web.Framework.Theming; using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Web.Framework.UI; -using SmartStore.Web.Framework.UI.Menus; using SmartStore.Web.Framework.WebApi; using SmartStore.Web.Framework.WebApi.Configuration; using Module = Autofac.Module; diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 168bbae5bf..7e388992a1 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -375,6 +375,7 @@ + diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuInvalidator.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuInvalidator.cs new file mode 100644 index 0000000000..c5605f258b --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuInvalidator.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using SmartStore.Core; +using SmartStore.Core.Data.Hooks; +using SmartStore.Core.Domain.Cms; +using SmartStore.Services.Cms; + +namespace SmartStore.Web.Framework.UI +{ + public partial class MenuInvalidator : DbSaveHook + { + private readonly Lazy _menuService; + private readonly Lazy _menuStorage; + + private static readonly HashSet _candidateTypes = new HashSet(new Type[] + { + typeof(MenuRecord), + typeof(MenuItemRecord) + }); + + public MenuInvalidator( + Lazy menuService, + Lazy menuStorage) + { + _menuService = menuService; + _menuStorage = menuStorage; + } + + protected override void OnDeleted(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + protected override void OnInserted(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + protected override void OnUpdated(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + private void HookObject(BaseEntity entity, IHookedEntity entry) + { + var type = entry.EntityType; + + if (!_candidateTypes.Contains(type)) + { + throw new NotSupportedException(); + } + + if (entry.Entity is MenuRecord mr) + { + _menuService.Value.ClearCache(mr.SystemName); + } + else if (entry.Entity is MenuItemRecord mir) + { + var menu = mir.Menu ?? _menuStorage.Value.GetMenuById(mir.MenuId); + _menuService.Value.ClearCache(menu.SystemName); + } + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs index debed39839..999e4e30e5 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs @@ -5,7 +5,7 @@ using SmartStore.Core.Logging; using SmartStore.Services.Cms; -namespace SmartStore.Web.Framework.UI.Menus +namespace SmartStore.Web.Framework.UI { public partial class MenuService : IMenuService { From 95d4f356c46f7f521c75d0cab53cf0b85e74ed5b Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 11 Apr 2019 18:43:11 +0200 Subject: [PATCH 133/460] Enhanced ComponentRenderer code --- .../UI/Components/ComponentRenderer.cs | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs index 1bef5d85ee..d87955673a 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs @@ -51,43 +51,45 @@ public virtual void VerifyState() } } - protected void WriteHtml(HtmlTextWriter writer) - { - this.VerifyState(); - this.Component.Id = SanitizeId(this.Component.Id); - - this.WriteHtmlCore(writer); - } - - protected virtual void WriteHtmlCore(HtmlTextWriter writer) - { - throw new NotImplementedException(); - } + protected string SanitizeId(string id) + { + return id.SanitizeHtmlId(); + } - public virtual void Render() + public virtual void Render() { - var output = ((System.Web.Mvc.WebViewPage)this.HtmlHelper.ViewDataContainer).Output; - var str = ToHtmlString(); - output.Write(str); + RenderInternal(ViewContext.Writer); } public virtual string ToHtmlString() { using (var stringWriter = new StringWriter()) { - using (var htmlWriter = new HtmlTextWriter(stringWriter)) - { - this.WriteHtml(htmlWriter); - var str = stringWriter.ToString(); - return str; - } - } - } + RenderInternal(stringWriter); + var str = stringWriter.ToString(); + return str; + } + } + private void RenderInternal(TextWriter writer) + { + using (var htmlWriter = new HtmlTextWriter(writer)) + { + WriteHtmlCore(htmlWriter); + } + } - protected string SanitizeId(string id) - { - return id.SanitizeHtmlId(); - } + protected void WriteHtml(HtmlTextWriter writer) + { + this.VerifyState(); + this.Component.Id = SanitizeId(this.Component.Id); + + this.WriteHtmlCore(writer); + } + + protected virtual void WriteHtmlCore(HtmlTextWriter writer) + { + throw new NotImplementedException(); + } } } From 72ccac036dd31d29a2d2cae3c4465e6a2d7e4547 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 11 Apr 2019 21:37:32 +0200 Subject: [PATCH 134/460] MenuBuilder code review --- .../Cms/Menus/IMenuStorage.cs | 4 +- .../Cms/Menus/MenuStorage.cs | 41 +++-- .../DependencyRegistrar.cs | 2 +- .../UI/Menus/IMenu.cs | 2 +- .../UI/Menus/IMenuService.cs | 42 ++++- .../UI/Menus/MenuService.cs | 26 +-- .../UI/Menus/UserMenuFilter.cs | 37 +++-- .../Views/Shared/Partials/Navbar.cshtml | 22 +-- .../Views/Common/Partials/Footer.cshtml | 6 +- .../Views/Common/Partials/Menu.cshtml | 44 ++--- .../Views/Shared/Layouts/_Layout.cshtml | 2 +- .../Views/Shared/Menus/Dropdown.cshtml | 155 ++++++++---------- 12 files changed, 197 insertions(+), 186 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs index f8b0bf386a..6218e2c7b2 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/IMenuStorage.cs @@ -42,12 +42,12 @@ IPagedList GetAllMenus( int pageSize = int.MaxValue); /// - /// Gets info about user menus. + /// Gets cached infos about all user menus. /// /// Customer roles to check access for. null to use current customer's roles. /// Store identifier. 0 to use current store. /// Menu infos. - IList GetUserMenusInfo(IEnumerable roles = null, int storeId = 0); + IEnumerable GetUserMenuInfos(IEnumerable roles = null, int storeId = 0); /// /// Gets a menu by identifier. diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index 0d4db0a440..8aeb91aba4 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -15,7 +15,7 @@ namespace SmartStore.Services.Cms { public partial class MenuStorage : IMenuStorage { - private const string MENU_SYSTEMNAME_CACHE_KEY = "MenuStorage:SystemNames"; + private const string MENU_ALLSYSTEMNAMEs_CACHE_KEY = "MenuStorage:SystemNames"; private const string MENU_USER_CACHE_KEY = "MenuStorage:Menus:User-{0}-{1}"; private const string MENU_PATTERN_KEY = "MenuStorage:Menus:*"; @@ -121,7 +121,7 @@ public virtual IPagedList GetAllMenus( return new PagedList(query, pageIndex, pageSize); } - public virtual IList GetUserMenusInfo(IEnumerable roles = null, int storeId = 0) + public virtual IEnumerable GetUserMenuInfos(IEnumerable roles = null, int storeId = 0) { if (roles == null) { @@ -201,7 +201,9 @@ public virtual void InsertMenuItem(MenuItemRecord item) } _menuItemRepository.Insert(item); - } + + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); + } public virtual void UpdateMenuItem(MenuItemRecord item) { @@ -214,7 +216,9 @@ public virtual void UpdateMenuItem(MenuItemRecord item) } _menuItemRepository.Update(item); - } + + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); + } public virtual void DeleteMenuItem(MenuItemRecord item, bool deleteChilds = true) { @@ -226,20 +230,22 @@ public virtual void DeleteMenuItem(MenuItemRecord item, bool deleteChilds = true if (!deleteChilds) { _menuItemRepository.Delete(item); - return; } + else + { + var ids = new HashSet { item.Id }; + GetChildIds(item.Id, ids); - var ids = new HashSet { item.Id }; - GetChildIds(item.Id); - - foreach (var chunk in ids.Slice(200)) - { - var items = _menuItemRepository.Table.Where(x => chunk.Contains(x.Id)).ToList(); + foreach (var chunk in ids.Slice(200)) + { + var items = _menuItemRepository.Table.Where(x => chunk.Contains(x.Id)).ToList(); + _menuItemRepository.DeleteRange(items); + } + } - _menuItemRepository.DeleteRange(items); - } + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); - void GetChildIds(int parentId) + void GetChildIds(int parentId, HashSet ids) { var childIds = _menuItemRepository.TableUntracked .Where(x => x.ParentItemId == parentId) @@ -249,8 +255,7 @@ void GetChildIds(int parentId) if (childIds.Any()) { ids.AddRange(childIds); - - childIds.Each(x => GetChildIds(x)); + childIds.Each(x => GetChildIds(x, ids)); } } } @@ -293,9 +298,9 @@ public virtual IList GetMenuItems(string systemName, int storeId private ISet GetMenuSystemNames(bool create) { - if (create || _services.Cache.Contains(MENU_SYSTEMNAME_CACHE_KEY)) + if (create || _services.Cache.Contains(MENU_ALLSYSTEMNAMEs_CACHE_KEY)) { - return _services.Cache.GetHashSet(MENU_SYSTEMNAME_CACHE_KEY, () => + return _services.Cache.GetHashSet(MENU_ALLSYSTEMNAMEs_CACHE_KEY, () => { return _menuRepository.TableUntracked .Where(x => x.Published) diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 37635f7b2c..1e29835eb0 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -875,7 +875,7 @@ protected override void Load(ContainerBuilder builder) if (DataSettings.DatabaseIsInstalled()) { - builder.RegisterType().AsActionFilterFor(); + builder.RegisterType().AsResultFilterFor(); } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs index 98ee18b71d..a75a19e45e 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenu.cs @@ -6,7 +6,7 @@ namespace SmartStore.Web.Framework.UI /// /// A hierarchical navigation menu /// - public interface IMenu // INFO: replaces ISiteMap + public interface IMenu { /// /// Gets the menu system name (e.g. main, footer, service, admin etc.) diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs index 069e8d3059..9fef03c011 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/IMenuService.cs @@ -3,17 +3,45 @@ namespace SmartStore.Web.Framework.UI { - public partial interface IMenuService // INFO: replaces ISiteMapService + /// + /// Main service for menus + /// + public partial interface IMenuService { + /// + /// Gets a menu instance + /// + /// Name of a code-based or a persisted user menu. + /// Menu instance IMenu GetMenu(string name); + + /// + /// Gets the root node of a menu + /// + /// Name of a code-based or a persisted user menu. + /// The root menu item node. TreeNode GetRootNode(string menuName); - TreeNode GetCurrentNode(string menuName, ControllerContext controllerContext); + + /// + /// Resolves the current node + /// + /// Name of a code-based or a persisted user menu. + /// Controller context + /// The current menu item node. + TreeNode ResolveCurrentNode(string menuName, ControllerContext controllerContext); + + /// + /// Resolves all element counts for a tree subset, e.g. resolves the number of products in categories. + /// + /// Name of a code-based or a persisted user menu. + /// The node to begin resolution. + /// true: process ALL children of , false: process only direct children of . void ResolveElementCounts(string menuName, TreeNode curNode, bool deep = false); - void ClearCache(string menuName); - /// - /// Registers actions to render user menus in widget zones. - /// - void ProcessMenus(); + /// + /// Removes all cached menu variations for + /// + /// Name of a code-based or a persisted user menu. + void ClearCache(string menuName); } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs index 999e4e30e5..7f414d428e 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/MenuService.cs @@ -9,8 +9,6 @@ namespace SmartStore.Web.Framework.UI { public partial class MenuService : IMenuService { - protected readonly IChronometer _chronometer; - protected readonly IWidgetProvider _widgetProvider; protected readonly IMenuStorage _menuStorage; protected readonly IMenuResolver[] _menuResolvers; @@ -18,13 +16,9 @@ public partial class MenuService : IMenuService private bool _currentNodeResolved; public MenuService( - IChronometer chronometer, - IWidgetProvider widgetProvider, IMenuStorage menuStorage, IEnumerable menuResolvers) { - _chronometer = chronometer; - _widgetProvider = widgetProvider; _menuStorage = menuStorage; _menuResolvers = menuResolvers.OrderBy(x => x.Order).ToArray(); } @@ -50,7 +44,7 @@ public virtual TreeNode GetRootNode(string menuName) return GetMenu(menuName)?.Root; } - public virtual TreeNode GetCurrentNode(string menuName, ControllerContext controllerContext) + public virtual TreeNode ResolveCurrentNode(string menuName, ControllerContext controllerContext) { if (!_currentNodeResolved) { @@ -74,23 +68,5 @@ public virtual void ClearCache(string menuName) { GetMenu(menuName)?.ClearCache(); } - - public virtual void ProcessMenus() - { - using (_chronometer.Step("ProcessMenus")) - { - var menusInfo = _menuStorage.GetUserMenusInfo(); - - foreach (var info in menusInfo) - { - _widgetProvider.RegisterAction( - info.WidgetZones, - "UserMenu", - "Common", - new { area = "", systemName = info.SystemName, template = info.Template }, - info.DisplayOrder); - } - } - } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs b/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs index f468f251e5..235cd04a7a 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Menus/UserMenuFilter.cs @@ -1,14 +1,17 @@ using System.Web.Mvc; +using SmartStore.Services.Cms; namespace SmartStore.Web.Framework.UI { - public class UserMenuFilter : IActionFilter, IResultFilter + public class UserMenuFilter : IResultFilter { - private readonly IMenuService _menuService; + private readonly IWidgetProvider _widgetProvider; + private readonly IMenuStorage _menuStorage; - public UserMenuFilter(IMenuService menuService) + public UserMenuFilter(IWidgetProvider widgetProvider, IMenuStorage menuStorage) { - _menuService = menuService; + _widgetProvider = widgetProvider; + _menuStorage = menuStorage; } public void OnResultExecuting(ResultExecutingContext filterContext) @@ -18,19 +21,29 @@ public void OnResultExecuting(ResultExecutingContext filterContext) return; } - _menuService.ProcessMenus(); + ProcessUserMenus(); } public void OnResultExecuted(ResultExecutedContext filterContext) { } - public void OnActionExecuting(ActionExecutingContext filterContext) - { - } + /// + /// Registers actions to render user menus in widget zones. + /// + private void ProcessUserMenus() + { + var menusInfo = _menuStorage.GetUserMenuInfos(); - public void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + foreach (var info in menusInfo) + { + _widgetProvider.RegisterAction( + info.WidgetZones, + "UserMenu", + "Common", + new { area = "", systemName = info.SystemName, template = info.Template }, + info.DisplayOrder); + } + } + } } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml index 4a665a1c6a..7e58cd4bf9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shared/Partials/Navbar.cshtml @@ -4,16 +4,16 @@ @using SmartStore.Core; @{ - var currentCustomer = this.WorkContext.CurrentCustomer; - var currentLanguage = this.WorkContext.WorkingLanguage; - var userName = (string)ViewBag.UserName; - var stores = ((IList)ViewBag.Stores) ?? new List(); - string currentLanguageCode = currentLanguage.UniqueSeoCode.EmptyNull().ToLower(); - string communityUrl = (currentLanguageCode == "de" ? - "http://community.smartstore.com" : - "http://community.smartstore.com/index.php?/&k=880ea6a14ea49e853634fbdc5015a024&setlanguage=1&cal_id=&langid=1"); - string docsUrl = GenerateHelpUrl(""); - var checkUpdateResult = ViewBag.CheckUpdateResult as CheckUpdateResult; + var currentCustomer = this.WorkContext.CurrentCustomer; + var currentLanguage = this.WorkContext.WorkingLanguage; + var userName = (string)ViewBag.UserName; + var stores = ((IList)ViewBag.Stores) ?? new List(); + string currentLanguageCode = currentLanguage.UniqueSeoCode.EmptyNull().ToLower(); + string communityUrl = (currentLanguageCode == "de" ? + "http://community.smartstore.com" : + "http://community.smartstore.com/index.php?/&k=880ea6a14ea49e853634fbdc5015a024&setlanguage=1&cal_id=&langid=1"); + string docsUrl = GenerateHelpUrl(""); + var checkUpdateResult = ViewBag.CheckUpdateResult as CheckUpdateResult; } -