diff --git a/README.md b/README.md index acf1060718..51e24bdf95 100644 --- a/README.md +++ b/README.md @@ -1,144 +1,14 @@ -|
:information_source: **[Announcement: Porting Smartstore to cross-platform ASP.NET Core 5!](https://github.com/smartstore/Smartstore)**
| -| --- | -| We are currently working hard on porting Smartstore from classic ASP.NET MVC to ASP.NET Core 5. For best code quality we decided to port class by class and adapt the existing code to the new environment. Therefore we have created a **[new repository](https://github.com/smartstore/Smartstore)** where we do all the work. Of course, work in this repository continues as usual. **Give our new repository a star** to stay up-to-date, get involved or just watch how we're doing. Learn all about the latest development, participate actively and last but not least, don't miss the day of release. | +# Deprecation warning :warning: -
+This repository is outdated and no longer being actively maintained by the Smartstore devs. +Users & developers are encouraged to check out the successor built on **ASP.NET Core**: +**Smartstore 5**. -

- - Smartstore.NET +

+ + Announcing Smartstore 5 - - -
-
- -

- Smartstore.NET -

-

Ready. Sell. Grow.

- -

- GitHub release - GitHub All Releases - GitHub repo size - GitHub closed issues - GitHub commit activity - GitHub -

- -

- Try Online ∙ - Docs ∙ - Forum ∙ - Marketplace ∙ - Translations

-
- -Smartstore is a free, open source, full-featured e-commerce solution for companies of any size. It is web standards compliant and incorporates the newest Microsoft web technology stack. - -**Smartstore includes all essential features to create multilingual and multi-currency stores** targeting desktop or mobile devices and enabling SEO optimized rich product catalogs with support for an unlimited number of products and categories, variants, bundles, datasheets, ESD, discounts, coupons and many more. - -A comprehensive set of tools for CRM & CMS, sales, marketing, payment & shipping handling, etc. makes Smartstore a powerful all-in-one solution fulfilling all your needs. - -
-

- Smartstore.NET Demoshop -

-**Smartstore delivers a beautiful and configurable shop front-end out-of-the-box**, built with a design approach on the highest level, including components like `Bootstrap 4`, `Sass` and others. The supplied theme _Flex_ is modern, clean and fully responsive, giving buyers the best possible shopping experience on any device. - -The state-of-the-art architecture of Smartstore - with `ASP.NET 4.5` + `MVC 5`, `Entity Framework 6` and Domain Driven Design approach - makes it easy to extend, extremely flexible and essentially fun to work with ;-) - -* **Website:** [https://smartstore.com](https://smartstore.com) -* **Forum:** [http://community.smartstore.com](http://community.smartstore.com) -* **Marketplace:** [http://community.smartstore.com/marketplace](http://community.smartstore.com/marketplace) -* **Translations:** [http://translate.smartstore.com/](http://translate.smartstore.com/) -* **Documentation:** [Smartstore Documentation in English](http://docs.smartstore.com/display/SMNET) -* **Developer Extension:** [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SmartStoreAG.Smartstore) -* **Azure Marketplace:** [Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/smartstore-ag.smartstorenet?tab=Overview) - -

 

- -## Highlights - -### Technology & Design - -* State of the art architecture thanks to `ASP.NET 4.5`, `ASP.NET MVC 5`, `Entity Framework 6` and Domain Driven Design -* Easy to extend and extremely flexible thanks to modular design -* Highly scalable thanks to full page caching and web farm support -* A powerful theming engine lets you create themes & skins with minimum effort thanks to theme inheritance -* Point&Click Theme configuration -* Highly professional search framework based on Lucene.NET, delivering ultra fast faceted search results -* Powerful and lightning-fast media manager -* Powerful rule system for visual business rule creation -* Consistent and sophisticated use of modern components such as `Bootstrap 4`, `Vue.js`, `jQuery` `Sass` & more in the front and back end. -* Easy shop management thanks to modern and clean UI - -### Features - -* NEW: Media Manager -* NEW: Rule Builder -* Unlimited number of products and categories -* Multi-Store support -* Product Bundles -* RESTful WebApi -* Multi-language and RTL support -* CMS Page Builder -* CMS Menu Builder -* Modern, clean, SEO-optimized and fully responsive Theme based on Bootstrap 4 -* Ultra fast search framework with faceted search support -* Extremely scalable thanks to output caching, REDIS & Microsoft Azure support -* NEW: Tree-based permission management (ACL) with inheritance support -* *Trusted Shops* precertification -* 100% compliant with German jurisdiction -* Sales-, Customer- & Inventory-management -* Comprehensive CRM features -* Powerful Discount System -* Powerful layered navigation in the shop -* Numerous Payment and Shipping Providers and options -* Sophisticated Marketing & Promotion capabilities (Gift cards, Reward Points, discounts of any type and more) -* Reviews & Ratings -* CMS (Blog, Forum, custom pages & HTML content etc.) -* and many more... - - - -## Project Status - -Smartstore V4.0.0 has been released on 25 June, 2020. The highlights are: - -* **Media Manager**: Powerful and lightning-fast explorer/manager for media files (commercial plugin exclusively bundled with Pro Edition) -* **Rule Builder**: Powerful rule system for visual business rule creation - * Dozens of predefined rules out-of-the-box - * Supports logical operators (AND/OR) and unlimited grouping/nesting - * Cart rules: can be applied to discounts, shipping and payment methods - * Customer rules: used to auto-assign customers to customer groups for 1-to-1 content targeting - * Product rules: used to auto-assign products to categories based on filter definitions (e.g. price, brand, color, stock etc.) -* **New permission (ACL) system**: Granular tree-based permission management based on customer groups. Supports inheritance to allow or deny entire permission ranges -* **Personalized product recommendations**: Replaces homepage products dynamically with personalized product recommendations based on customer interests. (commercial plugin) -* **Geo Blocker**: Restricts shop access based on visitor country or IP address ranges (commercial plugin) -* **Dashboard with charts**: New dashboard widgets display key data like orders, customers, bestsellers etc. in nice graphical charts. - - - -## Try it online - -We have set up a live online demo for you so you are able to test Smartstore without local installation. Get a first impression and test all available features in the front- and in the backend. Please keep in mind that the backend demo is shared and other testers can modify data at the same time. - -* [**Frontend**](https://demo.smartstore.com/frontend/en) (User: demo, PWD: 1234) -* [**Backend**](https://demo.smartstore.com/backend/en/login) (User: demo, PWD: 1234) - - - -## How to install - -* Download the latest stable release from the download tab and unzip it to your web folder -* Setup a website in IIS and point the file directory to your unzipped folder -* Fire up your browser and follow the installation instructions -* Enjoy ;-) - -NOTE: Smartstore 4 requires [Visual C++ Redistributable für Visual Studio 2015](https://www.microsoft.com/en-US/download/details.aspx?id=52685) which is already pre-installed on most systems. If, nevertheless, it is missing on your web server, just download and execute the installer or ask your hosting provider to do that for you. ### System requirements diff --git a/assets/ready.set.go.jpg b/assets/ready.set.go.jpg new file mode 100644 index 0000000000..e69e37758c Binary files /dev/null and b/assets/ready.set.go.jpg differ diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs index 49d790dcf3..ee8df7af19 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs @@ -22,7 +22,7 @@ public LocalizationSettings() public bool UseImagesForLanguageSelection { get; set; } /// - /// A value indicating whether the browser user lannguage should be detected + /// A value indicating whether the browser user language should be detected /// public bool DetectBrowserUserLanguage { get; set; } @@ -42,6 +42,11 @@ public LocalizationSettings() /// /// This setting is ignored when SeoFriendlyUrlsForLanguagesEnabled is false public InvalidLanguageRedirectBehaviour InvalidLanguageRedirectBehaviour { get; set; } + + /// + /// Whether to display region/country name in language selector (e.g. "Deutsch (Deutschland)" instead of "Deutsch") + /// + public bool DisplayRegionInLanguageSelector { get; set; } } public enum DefaultLanguageRedirectBehaviour diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs index db219751cb..83f2dfe962 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs @@ -33,5 +33,6 @@ public class MessageTemplateNames public const string BackInStockCustomer = "Customer.BackInStock"; public const string SystemCampaign = "System.Campaign"; public const string SystemContactUs = "System.ContactUs"; + public const string SystemGeneric = "System.Generic"; } } diff --git a/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs b/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs index cebbe01c4a..1449395774 100644 --- a/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs +++ b/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs @@ -54,7 +54,32 @@ public static IEnumerable EnumerateParentCultures(CultureInfo cultu } } + public static bool TryGetCultureInfoForLocale(string locale, out CultureInfo culture) + { + culture = null; + + try + { + culture = CultureInfo.GetCultureInfoByIetfLanguageTag(locale); + return culture != null; + } + catch + { + return false; + } + } + public static string GetLanguageNativeName(string locale) + { + if (TryGetCultureInfoForLocale(locale, out var culture)) + { + return culture.NativeName; + } + + return null; + } + + public static string GetLanguageNativeName(string locale, bool englishName, bool shortName = false) { try { @@ -62,14 +87,74 @@ public static string GetLanguageNativeName(string locale) { var info = CultureInfo.GetCultureInfoByIetfLanguageTag(locale); if (info != null) - return info.NativeName; + { + if (shortName) + { + info = info.Parent; + } + + if (englishName) + { + return info.EnglishName; + } + else + { + return info.NativeName; + } + + } } } - catch { } + catch + { + } return null; } + public static string NormalizeLanguageDisplayName(string languageName, bool stripRegion = false, CultureInfo culture = null) + { + if (string.IsNullOrEmpty(languageName) || languageName.Length == 0) + { + return languageName; + } + + // First char to upper. + if (char.IsLower(languageName[0])) + { + languageName = (culture ?? CultureInfo.InvariantCulture).TextInfo.ToTitleCase(languageName); + } + + var bracketIndex = languageName.IndexOfAny(new[] { '(', '[' }); + var hasRegion = bracketIndex > -1; + var endBracket = ')'; + + if (hasRegion) + { + if (languageName[bracketIndex] == '[') + { + endBracket = ']'; + } + + if (stripRegion) + { + languageName = languageName.Substring(0, bracketIndex).TrimEnd(); + } + } + + // Remove everything after ',' within Region part + if (hasRegion && !stripRegion) + { + var commaIndex = languageName.IndexOf(','); + if (commaIndex > -1) + { + languageName = languageName.Substring(0, commaIndex) + endBracket; + } + } + + return languageName; + } + public static string GetCurrencySymbol(string locale) { try diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index f2738cabc8..cd84ae4268 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -86,8 +86,8 @@ ..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll True - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll True @@ -96,8 +96,8 @@ ..\..\packages\Microsoft.Web.Xdt.3.0.0\lib\net40\Microsoft.Web.XmlTransform.dll - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll @@ -118,13 +118,13 @@ - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll diff --git a/src/Libraries/SmartStore.Core/app.config b/src/Libraries/SmartStore.Core/app.config index 9657a5deb1..67916a0819 100644 --- a/src/Libraries/SmartStore.Core/app.config +++ b/src/Libraries/SmartStore.Core/app.config @@ -14,6 +14,18 @@ + + + + + + + + + + + + diff --git a/src/Libraries/SmartStore.Core/packages.config b/src/Libraries/SmartStore.Core/packages.config index 0c8d7ea5ba..efb4850fdf 100644 --- a/src/Libraries/SmartStore.Core/packages.config +++ b/src/Libraries/SmartStore.Core/packages.config @@ -9,13 +9,13 @@ - + - + - - + + \ 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 fb36ab4a3d..9afb1372cc 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -49,6 +49,11 @@ public void MigrateSettings(SmartObjectContext context) public void MigrateLocaleResources(LocaleResourcesBuilder builder) { + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DisplayRegionInLanguageSelector", + "Display region in language selector", + "Region in der Sprachauswahl anzeigen", + "Whether to display region/country name in language selector (e.g. 'Deutsch (Deutschland)' instead of 'Deutsch')", + "Zeigt den Namen der Region/des Landes in der Sprachauswahl an (z. B. 'Deutsch (Deutschland)' statt 'Deutsch')"); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 928105317a..fb594ed6a4 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -82,8 +82,8 @@ ..\..\packages\EntityFramework.SqlServerCompact.6.4.4\lib\net45\EntityFramework.SqlServerCompact.dll True - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll True @@ -97,8 +97,8 @@ True ..\..\packages\Microsoft.SqlServer.Scripting.11.0.2100.61\lib\Microsoft.SqlServer.Smo.dll - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -108,11 +108,11 @@ True ..\..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll diff --git a/src/Libraries/SmartStore.Data/app.config b/src/Libraries/SmartStore.Data/app.config index 68a1994474..a625161735 100644 --- a/src/Libraries/SmartStore.Data/app.config +++ b/src/Libraries/SmartStore.Data/app.config @@ -14,6 +14,18 @@ + + + + + + + + + + + + diff --git a/src/Libraries/SmartStore.Data/packages.config b/src/Libraries/SmartStore.Data/packages.config index d2fafe90f4..bc852168b3 100644 --- a/src/Libraries/SmartStore.Data/packages.config +++ b/src/Libraries/SmartStore.Data/packages.config @@ -3,10 +3,10 @@ - + - - - + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs index d6de776ade..6fb44522a1 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs @@ -180,6 +180,11 @@ public virtual void ClearCachedAttributeValues() } public virtual Multimap DeserializeProductVariantAttributes(string attributesXml) + { + return DeserializeAttributesXml(attributesXml, null); + } + + private Multimap DeserializeAttributesXml(string attributesXml, int[] includeAttributesIds = null) { var attrs = new Multimap(); if (String.IsNullOrEmpty(attributesXml)) @@ -195,7 +200,8 @@ public virtual Multimap DeserializeProductVariantAttributes(string string sid = node1.Attribute("ID").Value; if (sid.HasValue()) { - if (int.TryParse(sid, out var id)) + if (int.TryParse(sid, out var id) && + (includeAttributesIds == null || includeAttributesIds.Contains(id))) { // ProductVariantAttributeValue/Value foreach (var node2 in node1.Descendants("Value")) @@ -326,6 +332,11 @@ public virtual bool AreProductAttributesEqual(string attributeXml1, string attri var attributes1 = DeserializeProductVariantAttributes(attributeXml1); var attributes2 = DeserializeProductVariantAttributes(attributeXml2); + return AreProductAttributesEqual(attributes1, attributes2); + } + + private bool AreProductAttributesEqual(Multimap attributes1, Multimap attributes2) + { if (attributes1.Count != attributes2.Count) return false; @@ -383,9 +394,14 @@ public virtual ProductVariantAttributeCombination FindProductVariantAttributeCom if (combinations.Count == 0) return null; + var listTypeValues = ParseProductVariantAttributeValues(attributesXml); + var listTypeAttributesIds = listTypeValues.Select(x => x.ProductVariantAttributeId).ToArray(); + var listTypeAttributesMap = DeserializeAttributesXml(attributesXml, listTypeAttributesIds); + foreach (var combination in combinations) { - bool attributesEqual = AreProductAttributesEqual(combination.AttributesXml, attributesXml); + var combinationAttributesMap = DeserializeProductVariantAttributes(combination.AttributesXml); + bool attributesEqual = AreProductAttributesEqual(combinationAttributesMap, listTypeAttributesMap); if (attributesEqual) return _productAttributeService.GetProductVariantAttributeCombinationById(combination.Id); } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs index 52ffd8cd80..8ecf7912c0 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs @@ -377,7 +377,7 @@ public static string GetBasePriceInfo(this Product product, { var value = Convert.ToDecimal((productPrice / product.BasePriceAmount) * product.BasePriceBaseAmount); var valueFormatted = priceFormatter.FormatPrice(value, true, currency); - var amountFormatted = Math.Round(product.BasePriceAmount.Value, 2).ToString("G29"); + var amountFormatted = product.BasePriceAmount.Value.ToString("G29"); var infoTemplate = localizationService.GetResource("Products.BasePriceInfo"); var result = infoTemplate.FormatInvariant( diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs index c1f09c939b..076b161bf9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs @@ -1715,6 +1715,7 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) var publicDeployment = profile.Deployments.FirstOrDefault(x => x.DeploymentType == ExportDeploymentType.PublicFolder); ctx.Store = store; + ctx.LastId = 0; context.FileIndex = 0; context.Store = ToDynamic(ctx, ctx.Store); context.MaxFileNameLength = dataExchangeSettings.MaxFileNameLength; diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs index b5a92c3263..2371b71bc7 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs @@ -218,6 +218,7 @@ protected virtual LocalizedPropertyCollection GetLocalizedPropertyCollectionInte where x.LocaleKeyGroup == localeKeyGroup select x; + var splitEntityIds = false; var requestedSet = entityIds; if (entityIds != null && entityIds.Length > 0) @@ -243,7 +244,15 @@ protected virtual LocalizedPropertyCollection GetLocalizedPropertyCollectionInte else { requestedSet = entityIds; - query = query.Where(x => entityIds.Contains(x.EntityId)); + + if (entityIds.Length > 5000) + { + splitEntityIds = true; + } + else + { + query = query.Where(x => entityIds.Contains(x.EntityId)); + } } } @@ -252,7 +261,20 @@ protected virtual LocalizedPropertyCollection GetLocalizedPropertyCollectionInte query = query.Where(x => x.LanguageId == languageId); } - return new LocalizedPropertyCollection(localeKeyGroup, requestedSet, query.ToList()); + if (splitEntityIds) + { + var items = new List(); + foreach (var chunk in entityIds.Slice(5000)) + { + items.AddRange(query.Where(x => chunk.Contains(x.EntityId)).ToList()); + } + + return new LocalizedPropertyCollection(localeKeyGroup, requestedSet, items); + } + else + { + return new LocalizedPropertyCollection(localeKeyGroup, requestedSet, query.ToList()); + } } } diff --git a/src/Libraries/SmartStore.Services/Media/DownloadService.cs b/src/Libraries/SmartStore.Services/Media/DownloadService.cs index a51fd84193..89297b15b0 100644 --- a/src/Libraries/SmartStore.Services/Media/DownloadService.cs +++ b/src/Libraries/SmartStore.Services/Media/DownloadService.cs @@ -64,12 +64,22 @@ public virtual IList GetDownloadsFor(int entityId, string entityName, select x).ToList(); if (versionedFilesOnly) + { downloads = downloads.Where(x => !string.IsNullOrEmpty(x.FileVersion)).ToList(); + } if (downloads.Any()) { var idsOrderedByVersion = downloads - .Select(x => new { x.Id, Version = new SemanticVersion(x.FileVersion.HasValue() ? x.FileVersion : "1.0.0.0") }) + .Select(x => + { + if (SemanticVersion.TryParse(x.FileVersion.HasValue() ? x.FileVersion : "1.0.0.0", out var version)) + { + return new { x.Id, Version = version }; + } + + return new { x.Id, Version = new SemanticVersion(0, 0, 0, 0) }; + }) .OrderByDescending(x => x.Version) .Select(x => x.Id); @@ -178,7 +188,7 @@ public virtual bool IsDownloadAllowed(OrderItem orderItem) return false; var product = orderItem.Product; - if (product == null || !product.IsDownload) + if (product == null || product.Deleted || !product.IsDownload) return false; //payment status diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs index 10194e75d1..1c754697e7 100644 --- a/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs @@ -148,7 +148,7 @@ public string BackgroundColor public override QueryString Add(string name, string value, bool isUnique) { // Keep away invalid tokens from underlying query - if (_supportedTokens.TryGetValue(name, out var validator) && validator(name, value)) + if (name != null && _supportedTokens.TryGetValue(name, out var validator) && validator(name, value)) { return base.Add(name, value, isUnique); } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs index aa657d8b32..7c2ab26c7b 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs @@ -515,6 +515,14 @@ public virtual object[] GetTestModels(MessageContext messageContext) ["Address"] = "VatAddress" }); break; + case MessageTemplateNames.SystemGeneric: + result.Add(new NamedModelPart("Generic") + { + ["Email"] = "john@doe.com", + ["Subject"] = "Subject", + ["Body"] = LoremIpsum + }); + break; } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs index 6fb1b6a323..37045b041f 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs @@ -1152,7 +1152,7 @@ private TreeNode BuildModelTreePart(string modelName, object in return node; } - private IEnumerable> BuildModelTreePartForClass(object instance) + private IEnumerable> BuildModelTreePartForClass(object instance, HashSet instanceLookup = null) { var type = instance?.GetType(); @@ -1177,11 +1177,25 @@ private IEnumerable> BuildModelTreePartForClass(object { yield return new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Collection }); } - else + else if (pi.PropertyType.IsClass) { - var node = new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Complex }); - node.AppendRange(BuildModelTreePartForClass(prop.GetValue(instance))); - yield return node; + if (instanceLookup == null) + { + instanceLookup = new HashSet(ReferenceEqualityComparer.Default) { instance }; + } + + var childInstance = prop.GetValue(instance); + if (childInstance != null) + { + if (!instanceLookup.Contains(childInstance)) + { + instanceLookup.Add(childInstance); + + var node = new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Complex }); + node.AppendRange(BuildModelTreePartForClass(childInstance, instanceLookup)); + yield return node; + } + } } } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs b/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs index 26d9f53c5e..ee5e0689ad 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs @@ -145,7 +145,8 @@ public virtual MessageTemplate CopyMessageTemplate(MessageTemplate messageTempla Body = messageTemplate.Body, IsActive = messageTemplate.IsActive, EmailAccountId = messageTemplate.EmailAccountId, - LimitedToStores = messageTemplate.LimitedToStores + LimitedToStores = messageTemplate.LimitedToStores, + ModelTypes = messageTemplate.ModelTypes // INFO: we do not copy attachments }; diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 072564b9fb..377945466a 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -104,8 +104,8 @@ ..\..\packages\MaxMind.GeoIP2.3.2.0\lib\net45\MaxMind.GeoIP2.dll - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -119,8 +119,8 @@ ..\..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll True - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\..\packages\NReco.PdfGenerator.1.2.0\lib\net45\NReco.PdfGenerator.dll @@ -144,14 +144,14 @@ ..\..\packages\System.Linq.Dynamic.Core.1.2.1\lib\net46\System.Linq.Dynamic.Core.dll - - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll diff --git a/src/Libraries/SmartStore.Services/app.config b/src/Libraries/SmartStore.Services/app.config index 252642d5ed..446c32df9a 100644 --- a/src/Libraries/SmartStore.Services/app.config +++ b/src/Libraries/SmartStore.Services/app.config @@ -34,7 +34,19 @@ - + + + + + + + + + + + + + diff --git a/src/Libraries/SmartStore.Services/packages.config b/src/Libraries/SmartStore.Services/packages.config index 79508508e8..4046ac6a7f 100644 --- a/src/Libraries/SmartStore.Services/packages.config +++ b/src/Libraries/SmartStore.Services/packages.config @@ -14,17 +14,17 @@ - + - + - - + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index aa30c693c3..90e38591eb 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -93,9 +93,8 @@ ..\..\packages\Common.Logging.2.0.0\lib\2.0\Common.Logging.dll True - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - False + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll @@ -105,22 +104,19 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - False + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll - False + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll diff --git a/src/Plugins/SmartStore.AmazonPay/packages.config b/src/Plugins/SmartStore.AmazonPay/packages.config index 36f4b6da90..bfadbe9d06 100644 --- a/src/Plugins/SmartStore.AmazonPay/packages.config +++ b/src/Plugins/SmartStore.AmazonPay/packages.config @@ -7,10 +7,10 @@ - + - - - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/web.config b/src/Plugins/SmartStore.AmazonPay/web.config index 46a5771064..e3e561e6ca 100644 --- a/src/Plugins/SmartStore.AmazonPay/web.config +++ b/src/Plugins/SmartStore.AmazonPay/web.config @@ -62,7 +62,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -160,6 +160,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -167,4 +187,4 @@ - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj index 6913511e9e..a7ccb144e0 100644 --- a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj +++ b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj @@ -93,9 +93,8 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll diff --git a/src/Plugins/SmartStore.Clickatell/packages.config b/src/Plugins/SmartStore.Clickatell/packages.config index 362bbd54e5..0470997544 100644 --- a/src/Plugins/SmartStore.Clickatell/packages.config +++ b/src/Plugins/SmartStore.Clickatell/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/web.config b/src/Plugins/SmartStore.Clickatell/web.config index 1ae1189cd0..06bf37d3c2 100644 --- a/src/Plugins/SmartStore.Clickatell/web.config +++ b/src/Plugins/SmartStore.Clickatell/web.config @@ -1,9 +1,9 @@ - + - - + + - - + + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + - + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj b/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj index 752242a26f..b392fe891f 100644 --- a/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj +++ b/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj @@ -93,9 +93,8 @@ ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll False - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - False + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll @@ -105,21 +104,18 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - False + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll - False + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll diff --git a/src/Plugins/SmartStore.OfflinePayment/packages.config b/src/Plugins/SmartStore.OfflinePayment/packages.config index d772d4d4ca..1e0b785b88 100644 --- a/src/Plugins/SmartStore.OfflinePayment/packages.config +++ b/src/Plugins/SmartStore.OfflinePayment/packages.config @@ -5,10 +5,10 @@ - + - - - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/web.config b/src/Plugins/SmartStore.OfflinePayment/web.config index 1ae1189cd0..06bf37d3c2 100644 --- a/src/Plugins/SmartStore.OfflinePayment/web.config +++ b/src/Plugins/SmartStore.OfflinePayment/web.config @@ -1,9 +1,9 @@ - + - - + + - - + + - @if (brand != null) - { -
- - @if (brand.Picture != null && brand.Picture.ImageUrl.HasValue() && brand.Picture.PictureId != 0) + @if (Model.Sku.HasValue()) + { + + } + + @if (Model.Gtin.HasValue()) + { + + } + + @if (Model.ManufacturerPartNumber.HasValue()) + { + + } + + @if (brand != null) + { + + } + + +
+

+ @if (Model.IsAssociatedProduct && Model.VisibleIndividually) { - @brand.Picture.AlternateText + @Model.Name } else { - @brand.Name + @Model.Name } - - - - @if (Model.Sku.HasValue()) - { - - } - - @if (Model.Gtin.HasValue()) - { - - } - - @if (Model.ManufacturerPartNumber.HasValue()) - { - - } +

- } - - -
-

- @if (Model.IsAssociatedProduct && Model.VisibleIndividually) - { - @Model.Name - } - else - { - @Model.Name - } -

-
- - @if (Model.ShortDescription.Value.HasValue()) - { -
- @Html.Raw(Model.ShortDescription) -
- } + + @if (Model.ShortDescription.Value.HasValue()) + { +
+ @Html.Raw(Model.ShortDescription) +
+ }
diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml index 694a1a0788..41d0b05cdb 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Link.cshtml @@ -118,7 +118,7 @@ @if (allowedTypes.Contains(LinkType.File)) {