From b1d8bdd8b6feb79e3dde81662b2d3df561b2ac10 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 23 Oct 2015 12:49:03 +0200 Subject: [PATCH 001/732] #791 Preselected attributes or attribute combinations should always be appended as querystring to product page links (part 3) --- .../Catalog/IProductAttributeParser.cs | 33 +++++++-- .../Catalog/ProductAttributeParser.cs | 70 ++++++++++++------- .../NameValueCollectionExtensions.cs | 13 ++-- .../Controllers/ProductController.cs | 5 +- .../Controllers/CatalogHelper.cs | 9 +-- .../Controllers/CustomerController.cs | 18 ++++- .../Controllers/OrderController.cs | 37 ++++++++-- .../Controllers/ProductController.cs | 11 ++- .../Controllers/ReturnRequestController.cs | 23 ++++-- .../Controllers/ShoppingCartController.cs | 62 +++++++++++----- 10 files changed, 205 insertions(+), 76 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs index bdc8ef7c82..64a412d339 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs @@ -82,22 +82,47 @@ public partial interface IProductAttributeParser List> DeserializeQueryData(string jsonData); /// - /// Serializes attribute data + /// Deserializes attribute data /// + /// List with deserialized data + /// XML formatted attributes /// Product identifier + /// Bundle item identifier + void DeserializeQueryData(List> queryData, string attributesXml, int productId, int bundleItemId = 0); + + /// + /// Serializes attribute data + /// /// XML formatted attributes + /// Product identifier /// Whether to URL encode /// Json string with attribute data - string SerializeQueryData(int productId, string attributesXml, bool urlEncode = true); + string SerializeQueryData(string attributesXml, int productId, bool urlEncode = true); + + /// + /// Serializes attribute data + /// + /// List with deserialized data + /// Whether to URL encode + /// Json string with attribute data + string SerializeQueryData(List> queryData, bool urlEncode = true); /// /// Gets the URL of the product page including attributes query string /// + /// XML formatted attributes /// Product identifier /// Product SEO name - /// XML formatted attributes /// URL of the product page including attributes query string - string GetProductUrlWithAttributes(int productId, string productSeName, string attributesXml); + string GetProductUrlWithAttributes(string attributesXml, int productId, string productSeName); + + /// + /// Gets the URL of the product page including attributes query string + /// + /// Attribute query data + /// Product SEO name + /// URL of the product page including attributes query string + string GetProductUrlWithAttributes(List> queryData, string productSeName); #endregion diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs index 635893d821..23ee794f0b 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs @@ -372,58 +372,68 @@ public virtual ProductVariantAttributeCombination FindProductVariantAttributeCom return result; } - /// - /// Deserializes attribute data from an URL query string - /// - /// Json data query string - /// List items with following structure: Product.Id, ProductAttribute.Id, Product_ProductAttribute_Mapping.Id, ProductVariantAttributeValue.Id public virtual List> DeserializeQueryData(string jsonData) { if (jsonData.HasValue()) { if (jsonData.StartsWith("[")) + { return JsonConvert.DeserializeObject>>(jsonData); + } - return new List>() { JsonConvert.DeserializeObject>(jsonData) }; + return new List> { JsonConvert.DeserializeObject>(jsonData) }; } return new List>(); } - - /// - /// Serializes attribute data - /// - /// Product identifier - /// Attribute XML string - /// Whether to URL encode - /// Json string with attribute data - public virtual string SerializeQueryData(int productId, string attributesXml, bool urlEncode = true) + + public virtual void DeserializeQueryData(List> queryData, string attributesXml, int productId, int bundleItemId = 0) { + Guard.ArgumentNotNull(() => queryData); + if (attributesXml.HasValue() && productId != 0) { - var data = new List>(); var attributeValues = ParseProductVariantAttributeValues(attributesXml).ToList(); foreach (var value in attributeValues) { - data.Add(new List + var lst = new List { productId, value.ProductVariantAttribute.ProductAttributeId, value.ProductVariantAttributeId, value.Id - }); - } + }; - if (data.Count > 0) - { - string result = JsonConvert.SerializeObject(data); - return (urlEncode ? HttpUtility.UrlEncode(result) : result); + if (bundleItemId != 0) + lst.Add(bundleItemId); + + queryData.Add(lst); } } + } + + public virtual string SerializeQueryData(string attributesXml, int productId, bool urlEncode = true) + { + var data = new List>(); + + DeserializeQueryData(data, attributesXml, productId); + + return SerializeQueryData(data, urlEncode); + } + + public virtual string SerializeQueryData(List> queryData, bool urlEncode = true) + { + if (queryData.Count > 0) + { + var result = JsonConvert.SerializeObject(queryData); + + return (urlEncode ? HttpUtility.UrlEncode(result) : result); + } + return ""; } - public virtual string GetProductUrlWithAttributes(int productId, string productSeName, string attributesXml) + private string CreateProductUrl(string queryString, string productSeName) { var url = UrlHelper.GenerateUrl( "Product", @@ -434,8 +444,6 @@ public virtual string GetProductUrlWithAttributes(int productId, string productS HttpContext.Current.Request.RequestContext, false); - var queryString = SerializeQueryData(productId, attributesXml); - if (queryString.HasValue()) { url = string.Concat(url, url.Contains("?") ? "&" : "?", "attributes=", queryString); @@ -444,6 +452,16 @@ public virtual string GetProductUrlWithAttributes(int productId, string productS return url; } + public virtual string GetProductUrlWithAttributes(string attributesXml, int productId, string productSeName) + { + return CreateProductUrl(SerializeQueryData(attributesXml, productId), productSeName); + } + + public virtual string GetProductUrlWithAttributes(List> queryData, string productSeName) + { + return CreateProductUrl(SerializeQueryData(queryData), productSeName); + } + #endregion #region Gift card attributes diff --git a/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs b/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs index 68d8f8dda6..c7fa1aa9dd 100644 --- a/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs +++ b/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs @@ -35,23 +35,24 @@ public static void AddProductAttribute(this NameValueCollection collection, int /// Converts attribute query data /// /// Name value collection - /// Attribute query data items with following structure: Product.Id, ProductAttribute.Id, Product_ProductAttribute_Mapping.Id, ProductVariantAttributeValue.Id + /// Attribute query data items with following structure: + /// Product.Id, ProductAttribute.Id, Product_ProductAttribute_Mapping.Id, ProductVariantAttributeValue.Id, [BundleItem.Id] /// Product identifier to filter public static void ConvertAttributeQueryData(this NameValueCollection collection, List> queryData, int productId = 0) { if (collection == null || queryData == null || queryData.Count <= 0) return; - var enm = queryData.Where(i => i.Count > 3); + var items = queryData.Where(i => i.Count > 3); if (productId != 0) - enm = enm.Where(i => i[0] == productId); + items = items.Where(i => i[0] == productId); - foreach (var itm in enm) + foreach (var item in items) { - string name = AttributeFormatedName(itm[1], itm[2], itm[0]); + var name = AttributeFormatedName(item[1], item[2], item[0], item.Count > 4 ? item[4] : 0); - collection.Add(name, itm[3].ToString()); + collection.Add(name, item[3].ToString()); } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index 3f2466d5e6..3f9fa79477 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -4104,15 +4104,14 @@ public ActionResult ProductVariantAttributeCombinationList(GridCommand command, var allCombinations = _productAttributeService.GetAllProductVariantAttributeCombinations(product.Id, command.Page - 1, command.PageSize); var productUrlTitle = _localizationService.GetResource("Common.OpenInShop"); - var productUrl = Url.RouteUrl("Product", new { SeName = product.GetSeName() }); - productUrl = "{0}{1}attributes=".FormatInvariant(productUrl, productUrl.Contains("?") ? "&" : "?"); + var productSeName = product.GetSeName(); var productVariantAttributesModel = allCombinations.Select(x => { var pvacModel = x.ToModel(); PrepareProductAttributeCombinationModel(pvacModel, x, product, true); - pvacModel.ProductUrl = productUrl + _productAttributeParser.SerializeQueryData(product.Id, x.AttributesXml); + pvacModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(x.AttributesXml, product.Id, productSeName); pvacModel.ProductUrlTitle = productUrlTitle; try diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index 83d17f1efa..ad36b89187 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -31,6 +31,7 @@ using SmartStore.Web.Models.Catalog; using SmartStore.Web.Models.Media; using SmartStore.Core.Data; +using System.Collections.Specialized; namespace SmartStore.Web.Controllers { @@ -147,7 +148,7 @@ public ProductDetailsModel PrepareProductDetailsPageModel( bool isAssociatedProduct = false, ProductBundleItemData productBundleItem = null, IList productBundleItems = null, - FormCollection selectedAttributes = null) + NameValueCollection selectedAttributes = null) { if (product == null) throw new ArgumentNullException("product"); @@ -229,7 +230,7 @@ public ProductDetailsModel PrepareProductDetailsPageModel( foreach (var itemData in bundleItems.Where(x => x.Item.Product.CanBeBundleItem())) { var item = itemData.Item; - var bundledProductModel = PrepareProductDetailsPageModel(item.Product, false, itemData); + var bundledProductModel = PrepareProductDetailsPageModel(item.Product, false, itemData, null, selectedAttributes); bundledProductModel.BundleItem.Id = item.Id; bundledProductModel.BundleItem.Quantity = item.Quantity; @@ -418,7 +419,7 @@ public ProductDetailsModel PrepareProductDetailModel( bool isAssociatedProduct = false, ProductBundleItemData productBundleItem = null, IList productBundleItems = null, - FormCollection selectedAttributes = null, + NameValueCollection selectedAttributes = null, int selectedQuantity = 1) { if (product == null) @@ -428,7 +429,7 @@ public ProductDetailsModel PrepareProductDetailModel( throw new ArgumentNullException("model"); if (selectedAttributes == null) - selectedAttributes = new FormCollection(); + selectedAttributes = new NameValueCollection(); decimal preSelectedPriceAdjustmentBase = decimal.Zero; decimal preSelectedWeightAdjustment = decimal.Zero; diff --git a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs index 62248ddbb4..a643b42759 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SmartStore.Core; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Forums; @@ -1348,6 +1350,7 @@ public ActionResult ReturnRequests() if (orderItem != null) { var product = orderItem.Product; + var attributeQueryData = new List>(); var itemModel = new CustomerReturnRequestsModel.ReturnRequestModel { @@ -1363,7 +1366,18 @@ public ActionResult ReturnRequests() CreatedOn = _dateTimeHelper.ConvertToUserTime(returnRequest.CreatedOnUtc, DateTimeKind.Utc) }; - itemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(orderItem.ProductId, itemModel.ProductSeName, orderItem.AttributesXml); + if (orderItem.Product.ProductType != ProductType.BundledProduct) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, orderItem.AttributesXml, orderItem.ProductId); + } + else if (orderItem.Product.BundlePerItemPricing && orderItem.BundleData.HasValue()) + { + var bundleData = orderItem.GetBundleData(); + + bundleData.ForEach(x => _productAttributeParser.DeserializeQueryData(attributeQueryData, x.AttributesXml, x.ProductId, x.BundleItemId)); + } + + itemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(attributeQueryData, itemModel.ProductSeName); model.Items.Add(itemModel); } @@ -1403,7 +1417,7 @@ public ActionResult DownloadableProducts() ProductId = item.ProductId }; - itemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(item.ProductId, itemModel.ProductSeName, item.AttributesXml); + itemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(item.AttributesXml, item.ProductId, itemModel.ProductSeName); model.Items.Add(itemModel); diff --git a/src/Presentation/SmartStore.Web/Controllers/OrderController.cs b/src/Presentation/SmartStore.Web/Controllers/OrderController.cs index ec9eb8365b..b4b1976c25 100644 --- a/src/Presentation/SmartStore.Web/Controllers/OrderController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/OrderController.cs @@ -389,6 +389,7 @@ protected ShipmentDetailsModel PrepareShipmentDetailsModel(Shipment shipment) //products in this shipment model.ShowSku = catalogSettings.ShowProductSku; + foreach (var shipmentItem in shipment.ShipmentItems) { var orderItem = _orderService.GetOrderItemById(shipmentItem.OrderItemId); @@ -397,6 +398,8 @@ protected ShipmentDetailsModel PrepareShipmentDetailsModel(Shipment shipment) orderItem.Product.MergeWithCombination(orderItem.AttributesXml); + var attributeQueryData = new List>(); + var shipmentItemModel = new ShipmentDetailsModel.ShipmentItemModel { Id = shipmentItem.Id, @@ -409,12 +412,22 @@ protected ShipmentDetailsModel PrepareShipmentDetailsModel(Shipment shipment) QuantityShipped = shipmentItem.Quantity }; - shipmentItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(orderItem.ProductId, shipmentItemModel.ProductSeName, orderItem.AttributesXml); + if (orderItem.Product.ProductType != ProductType.BundledProduct) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, orderItem.AttributesXml, orderItem.ProductId); + } + else if (orderItem.Product.BundlePerItemPricing && orderItem.BundleData.HasValue()) + { + var bundleData = orderItem.GetBundleData(); + + bundleData.ForEach(x => _productAttributeParser.DeserializeQueryData(attributeQueryData, x.AttributesXml, x.ProductId, x.BundleItemId)); + } + + shipmentItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(attributeQueryData, shipmentItemModel.ProductSeName); model.Items.Add(shipmentItemModel); } - //order details model model.Order = PrepareOrderDetailsModel(order); return model; @@ -422,6 +435,8 @@ protected ShipmentDetailsModel PrepareShipmentDetailsModel(Shipment shipment) private OrderDetailsModel.OrderItemModel PrepareOrderItemModel(Order order, OrderItem orderItem) { + var attributeQueryData = new List>(); + orderItem.Product.MergeWithCombination(orderItem.AttributesXml); var model = new OrderDetailsModel.OrderItemModel @@ -436,10 +451,13 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel(Order order, Orde AttributeInfo = orderItem.AttributeDescription }; - model.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(orderItem.ProductId, model.ProductSeName, orderItem.AttributesXml); + if (orderItem.Product.ProductType != ProductType.BundledProduct) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, orderItem.AttributesXml, orderItem.ProductId); + } var quantityUnit = _quantityUnitService.GetQuantityUnitById(orderItem.Product.QuantityUnitId); - model.QuantityUnit = quantityUnit == null ? "" : quantityUnit.GetLocalized(x => x.Name); + model.QuantityUnit = (quantityUnit == null ? "" : quantityUnit.GetLocalized(x => x.Name)); if (orderItem.Product.ProductType == ProductType.BundledProduct && orderItem.BundleData.HasValue()) { @@ -461,7 +479,12 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel(Order order, Orde AttributeInfo = bundleItem.AttributesInfo }; - bundleItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(bundleItem.ProductId, bundleItemModel.ProductSeName, bundleItem.AttributesXml); + bundleItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(bundleItem.AttributesXml, bundleItem.ProductId, bundleItemModel.ProductSeName); + + if (orderItem.Product.BundlePerItemPricing) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, bundleItem.AttributesXml, bundleItem.ProductId, bundleItem.BundleItemId); + } if (model.BundlePerItemShoppingCart) { @@ -485,6 +508,7 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel(Order order, Orde model.SubTotal = _priceFormatter.FormatPrice(priceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, _services.WorkContext.WorkingLanguage, false, false); } break; + case TaxDisplayType.IncludingTax: { var unitPriceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.UnitPriceInclTax, order.CurrencyRate); @@ -495,6 +519,9 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel(Order order, Orde } break; } + + model.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(attributeQueryData, model.ProductSeName); + return model; } diff --git a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs index 048274fc77..3b94189942 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Web.Mvc; using SmartStore.Core.Domain.Catalog; @@ -167,8 +168,11 @@ public ActionResult ProductDetails(int productId, string attributes) } //prepare the model - var selectedAttributes = new FormCollection(); - selectedAttributes.ConvertAttributeQueryData(_productAttributeParser.DeserializeQueryData(attributes), product.Id); + var selectedAttributes = new NameValueCollection(); + + selectedAttributes.ConvertAttributeQueryData( + _productAttributeParser.DeserializeQueryData(attributes), + product.ProductType == ProductType.BundledProduct ? 0 : product.Id); var model = _helper.PrepareProductDetailsPageModel(product, selectedAttributes: selectedAttributes); @@ -624,9 +628,10 @@ public ActionResult UpdateProductDetails(int productId, string itemType, int bun bundleItems = _productService.GetBundleItems(product.Id); if (form.Count > 0) { + // may add elements to form if they are preselected by bundle item filter foreach (var itemData in bundleItems) { - var tempModel = _helper.PrepareProductDetailsPageModel(itemData.Item.Product, false, itemData, null, form); + var unused = _helper.PrepareProductDetailsPageModel(itemData.Item.Product, false, itemData, null, form); } } } diff --git a/src/Presentation/SmartStore.Web/Controllers/ReturnRequestController.cs b/src/Presentation/SmartStore.Web/Controllers/ReturnRequestController.cs index 8fa1e94a3a..d7b5e454d6 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ReturnRequestController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ReturnRequestController.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Web.Mvc; using SmartStore.Core; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Tax; @@ -11,9 +13,9 @@ using SmartStore.Services.Messages; using SmartStore.Services.Orders; using SmartStore.Services.Seo; +using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Security; using SmartStore.Web.Models.Order; -using SmartStore.Web.Framework.Controllers; namespace SmartStore.Web.Controllers { @@ -101,19 +103,30 @@ protected SubmitReturnRequestModel PrepareReturnRequestModel(SubmitReturnRequest foreach (var orderItem in orderItems) { + var attributeQueryData = new List>(); + var orderItemModel = new SubmitReturnRequestModel.OrderItemModel { Id = orderItem.Id, ProductId = orderItem.Product.Id, ProductName = orderItem.Product.GetLocalized(x => x.Name), - ProductSeName = orderItem.Product.GetSeName(), + ProductSeName = orderItem.Product.GetSeName(), AttributeInfo = orderItem.AttributeDescription, Quantity = orderItem.Quantity }; - orderItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(orderItem.ProductId, orderItemModel.ProductSeName, orderItem.AttributesXml); + if (orderItem.Product.ProductType != ProductType.BundledProduct) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, orderItem.AttributesXml, orderItem.ProductId); + } + else if (orderItem.Product.BundlePerItemPricing && orderItem.BundleData.HasValue()) + { + var bundleData = orderItem.GetBundleData(); - model.Items.Add(orderItemModel); + bundleData.ForEach(x => _productAttributeParser.DeserializeQueryData(attributeQueryData, x.AttributesXml, x.ProductId, x.BundleItemId)); + } + + orderItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(attributeQueryData, orderItemModel.ProductSeName); //unit price switch (order.CustomerTaxDisplayType) @@ -131,6 +144,8 @@ protected SubmitReturnRequestModel PrepareReturnRequestModel(SubmitReturnRequest } break; } + + model.Items.Add(orderItemModel); } return model; diff --git a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs index 5c22d74b7a..98ea0b9fcc 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ShoppingCartController.cs @@ -238,7 +238,7 @@ private ShoppingCartModel.ShoppingCartItemModel PrepareShoppingCartItemModel(Org Weight = product.Weight }; - model.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(item.ProductId, model.ProductSeName, item.AttributesXml); + model.ProductUrl = GetProductUrlWithAttributes(sci, model.ProductSeName); if (item.BundleItem != null) { @@ -413,6 +413,7 @@ private ShoppingCartModel.ShoppingCartItemModel PrepareShoppingCartItemModel(Org return model; } + private WishlistModel.ShoppingCartItemModel PrepareWishlistCartItemModel(OrganizedShoppingCartItem sci) { var item = sci.Item; @@ -433,7 +434,7 @@ private WishlistModel.ShoppingCartItemModel PrepareWishlistCartItemModel(Organiz VisibleIndividually = product.VisibleIndividually }; - model.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(item.ProductId, model.ProductSeName, item.AttributesXml); + model.ProductUrl = GetProductUrlWithAttributes(sci, model.ProductSeName); if (item.BundleItem != null) { @@ -472,7 +473,7 @@ private WishlistModel.ShoppingCartItemModel PrepareWishlistCartItemModel(Organiz var allowedQuantities = product.ParseAllowedQuatities(); foreach (var qty in allowedQuantities) { - model.AllowedQuantities.Add(new SelectListItem() + model.AllowedQuantities.Add(new SelectListItem { Text = qty.ToString(), Value = qty.ToString(), @@ -913,7 +914,7 @@ protected void PrepareWishlistModel(WishlistModel model, IList x.Name), - ProductSeName = sci.Item.Product.GetSeName(), - Quantity = sci.Item.Quantity, + Id = item.Id, + ProductId = product.Id, + ProductName = product.GetLocalized(x => x.Name), + ProductSeName = product.GetSeName(), + Quantity = item.Quantity, AttributeInfo = _productAttributeFormatter.FormatAttributes( - sci.Item.Product, - sci.Item.AttributesXml, + product, + item.AttributesXml, null, serapator: ", ", renderPrices: false, @@ -980,11 +984,11 @@ protected MiniShoppingCartModel PrepareMiniShoppingCartModel() allowHyperlinks: false) }; - cartItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(sci.Item.ProductId, cartItemModel.ProductSeName, sci.Item.AttributesXml); + cartItemModel.ProductUrl = GetProductUrlWithAttributes(sci, cartItemModel.ProductSeName); if (sci.ChildItems != null && _shoppingCartSettings.ShowProductBundleImagesOnShoppingCart) { - foreach (var childItem in sci.ChildItems.Where(x => x.Item.Id != sci.Item.Id && x.Item.BundleItem != null && !x.Item.BundleItem.HideThumbnail)) + foreach (var childItem in sci.ChildItems.Where(x => x.Item.Id != item.Id && x.Item.BundleItem != null && !x.Item.BundleItem.HideThumbnail)) { var bundleItemModel = new MiniShoppingCartModel.ShoppingCartItemBundleItem { @@ -993,7 +997,7 @@ protected MiniShoppingCartModel PrepareMiniShoppingCartModel() }; bundleItemModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes( - childItem.Item.ProductId, bundleItemModel.ProductSeName, childItem.Item.AttributesXml); + childItem.Item.AttributesXml, childItem.Item.ProductId, bundleItemModel.ProductSeName); var itemPicture = _pictureService.GetPicturesByProductId(childItem.Item.ProductId, 1).FirstOrDefault(); if (itemPicture != null) @@ -1004,16 +1008,16 @@ protected MiniShoppingCartModel PrepareMiniShoppingCartModel() } //unit prices - if (sci.Item.Product.CallForPrice) + if (product.CallForPrice) { cartItemModel.UnitPrice = _localizationService.GetResource("Products.CallForPrice"); } else { - sci.Item.Product.MergeWithCombination(sci.Item.AttributesXml); + product.MergeWithCombination(item.AttributesXml); decimal taxRate = decimal.Zero; - decimal shoppingCartUnitPriceWithDiscountBase = _taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate); + decimal shoppingCartUnitPriceWithDiscountBase = _taxService.GetProductPrice(product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate); decimal shoppingCartUnitPriceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(shoppingCartUnitPriceWithDiscountBase, _workContext.WorkingCurrency); cartItemModel.UnitPrice = _priceFormatter.FormatPrice(shoppingCartUnitPriceWithDiscount); @@ -1022,8 +1026,7 @@ protected MiniShoppingCartModel PrepareMiniShoppingCartModel() //picture if (_shoppingCartSettings.ShowProductImagesInMiniShoppingCart) { - cartItemModel.Picture = PrepareCartItemPictureModel(sci.Item.Product, - _mediaSettings.MiniCartThumbPictureSize, true, cartItemModel.ProductName, sci.Item.AttributesXml); + cartItemModel.Picture = PrepareCartItemPictureModel(product, _mediaSettings.MiniCartThumbPictureSize, true, cartItemModel.ProductName, item.AttributesXml); } model.Items.Add(cartItemModel); @@ -1148,6 +1151,27 @@ protected void ParseAndSaveCheckoutAttributes(List ca _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.CheckoutAttributes, selectedAttributes); } + private string GetProductUrlWithAttributes(OrganizedShoppingCartItem cartItem, string productSeName) + { + var attributeQueryData = new List>(); + var product = cartItem.Item.Product; + + if (product.ProductType != ProductType.BundledProduct) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, cartItem.Item.AttributesXml, product.Id); + } + else if (cartItem.ChildItems != null && product.BundlePerItemPricing) + { + foreach (var childItem in cartItem.ChildItems.Where(x => x.Item.Id != cartItem.Item.Id)) + { + _productAttributeParser.DeserializeQueryData(attributeQueryData, childItem.Item.AttributesXml, childItem.Item.ProductId, childItem.BundleItemData.Item.Id); + } + } + + var url = _productAttributeParser.GetProductUrlWithAttributes(attributeQueryData, productSeName); + return url; + } + #endregion #region Shopping cart From 592ccbad6cd3a3c879a6cdb6ab3d1eebe1e7011a Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 23 Oct 2015 15:05:13 +0200 Subject: [PATCH 002/732] Resolves #796 Selected specification in product filter mask is displayed with default language (not localized) --- changelog.md | 1 + .../Filter/FilterCriteria.cs | 13 +++++--- .../Filter/FilterExtensions.cs | 32 ++++++++++--------- .../Filter/FilterService.cs | 26 +++++++++++---- .../Controllers/FilterController.cs | 21 ++++++------ 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/changelog.md b/changelog.md index f72b36b86e..e8364c5e2c 100644 --- a/changelog.md +++ b/changelog.md @@ -66,6 +66,7 @@ * #784 Biz-Importer: Name of delivery time must not be imported empty * #776 Preview: Manufacturer and Product in Multi Store * #755 Some methods still loading all products in one go +* #796 Selected specification in product filter mask is displayed with default language (not localized) ## SmartStore.NET 2.2.2 diff --git a/src/Libraries/SmartStore.Services/Filter/FilterCriteria.cs b/src/Libraries/SmartStore.Services/Filter/FilterCriteria.cs index 59de0fa9c1..5470367d49 100644 --- a/src/Libraries/SmartStore.Services/Filter/FilterCriteria.cs +++ b/src/Libraries/SmartStore.Services/Filter/FilterCriteria.cs @@ -33,10 +33,12 @@ public class FilterCriteria : IComparable [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public int? ID { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int? PId { get; set; } + // Metadata public int MatchCount { get; set; } public bool IsInactive { get; set; } - public int ParentId { get; set; } public string NameLocalized { get; set; } public string ValueLocalized { get; set; } @@ -45,10 +47,12 @@ public string SqlName get { if (Entity == "Manufacturer" && !Name.Contains('.')) - return "{0}.{1}".FormatWith(Entity, Name); + return "{0}.{1}".FormatInvariant(Entity, Name); + return Name; } } + public bool IsRange { get @@ -59,9 +63,9 @@ public bool IsRange int IComparable.CompareTo(object obj) { - FilterCriteria filter = (FilterCriteria)obj; + var filter = (FilterCriteria)obj; - int compare = string.Compare(this.Entity, filter.Entity, true); + var compare = string.Compare(this.Entity, filter.Entity, true); if (compare == 0) { @@ -86,6 +90,7 @@ int IComparable.CompareTo(object obj) //return string.Compare(this.Value, filter.Value, true); } + public override string ToString() { try diff --git a/src/Libraries/SmartStore.Services/Filter/FilterExtensions.cs b/src/Libraries/SmartStore.Services/Filter/FilterExtensions.cs index a466c8adeb..cd9996d010 100644 --- a/src/Libraries/SmartStore.Services/Filter/FilterExtensions.cs +++ b/src/Libraries/SmartStore.Services/Filter/FilterExtensions.cs @@ -17,6 +17,7 @@ private static string FormatPrice(string value) if (value.HasValue()) { decimal d = 0; + if (StringToPrice(value, out d)) return EngineContext.Current.Resolve().FormatPrice(d, true, false); } @@ -36,32 +37,32 @@ public static string ToDescription(this FilterCriteria criteria) criteria.Value.SplitToPair(out valueLeft, out valueRight, "~"); if (criteria.Entity == FilterService.ShortcutPrice) - return "{0} - {1}".FormatWith(FormatPrice(valueLeft), FormatPrice(valueRight)); + return "{0} - {1}".FormatInvariant(FormatPrice(valueLeft), FormatPrice(valueRight)); - return "{0} - {1}".FormatWith(valueLeft, valueRight); + return "{0} - {1}".FormatInvariant(valueLeft, valueRight); } - string value = (criteria.ValueLocalized.HasValue() ? criteria.ValueLocalized : criteria.Value); + var value = (criteria.ValueLocalized.HasValue() ? criteria.ValueLocalized : criteria.Value); if (criteria.Entity == FilterService.ShortcutPrice) value = FormatPrice(criteria.Value); if (criteria.Operator == FilterOperator.Unequal) - return "≠ {0}".FormatWith(value); + return "≠ {0}".FormatInvariant(value); if (criteria.Operator == FilterOperator.Greater) - return "> {0}".FormatWith(value); + return "> {0}".FormatInvariant(value); if (criteria.Operator == FilterOperator.GreaterEqual) - return "≥ {0}".FormatWith(value); + return "≥ {0}".FormatInvariant(value); if (criteria.Operator == FilterOperator.Less) - return "< {0}".FormatWith(value); + return "< {0}".FormatInvariant(value); if (criteria.Operator == FilterOperator.LessEqual) - return "≤ {0}".FormatWith(value); + return "≤ {0}".FormatInvariant(value); if (criteria.Operator == FilterOperator.Contains) - return "{0} {1}".FormatWith(localize.GetResource("Products.Filter.Contains"), value); + return "{0} {1}".FormatInvariant(localize.GetResource("Products.Filter.Contains"), value); if (criteria.Operator == FilterOperator.StartsWith) - return "{0} {1}".FormatWith(localize.GetResource("Products.Filter.StartsWith"), value); + return "{0} {1}".FormatInvariant(localize.GetResource("Products.Filter.StartsWith"), value); if (criteria.Operator == FilterOperator.EndsWith) - return "{0} {1}".FormatWith(localize.GetResource("Products.Filter.EndsWith"), value); + return "{0} {1}".FormatInvariant(localize.GetResource("Products.Filter.EndsWith"), value); return value; } @@ -132,7 +133,8 @@ public static bool StringToPrice(this string[] range, int index, out decimal res result = 0; if (range != null && index < range.Length) { - string value = range[index].Trim(); + var value = range[index].Trim(); + return StringToPrice(value, out result); } return false; @@ -140,11 +142,11 @@ public static bool StringToPrice(this string[] range, int index, out decimal res public static string GetUrl(this FilterProductContext context, FilterCriteria criteriaAdd = null, FilterCriteria criteriaRemove = null) { - string url = "{0}?pagesize={1}&viewmode={2}".FormatWith(context.Path, context.PageSize, context.ViewMode); + var url = "{0}?pagesize={1}&viewmode={2}".FormatInvariant(context.Path, context.PageSize, context.ViewMode); if (context.OrderBy.HasValue) { - url = "{0}&orderby={1}".FormatWith(url, context.OrderBy.Value); + url = "{0}&orderby={1}".FormatInvariant(url, context.OrderBy.Value); } try @@ -162,7 +164,7 @@ public static string GetUrl(this FilterProductContext context, FilterCriteria cr criterias.RemoveAll(c => c.Entity == criteriaRemove.Entity && c.Name == criteriaRemove.Name && c.Value == criteriaRemove.Value); if (criterias.Count > 0) - url = "{0}&filter={1}".FormatWith(url, HttpUtility.UrlEncode(JsonConvert.SerializeObject(criterias))); + url = "{0}&filter={1}".FormatInvariant(url, HttpUtility.UrlEncode(JsonConvert.SerializeObject(criterias))); } } catch (Exception exc) diff --git a/src/Libraries/SmartStore.Services/Filter/FilterService.cs b/src/Libraries/SmartStore.Services/Filter/FilterService.cs index fad58fc5c8..93673743aa 100644 --- a/src/Libraries/SmartStore.Services/Filter/FilterService.cs +++ b/src/Libraries/SmartStore.Services/Filter/FilterService.cs @@ -53,6 +53,7 @@ private string ValidateValue(string value, string alternativeValue) { if (value.HasValue() && !value.IsCaseInsensitiveEqual("null")) return value; + return alternativeValue; } @@ -163,7 +164,7 @@ private IQueryable AllProducts(List categoryIds) { if (_products == null) { - var searchContext = new ProductSearchContext() + var searchContext = new ProductSearchContext { Query = _productRepository.TableUntracked, FeaturedProducts = (_catalogSettings.IncludeFeaturedProductsInNormalLists ? null : (bool?)false), @@ -300,7 +301,7 @@ from a in attributes Name = g.FirstOrDefault().SpecificationAttribute.Name, Value = g.FirstOrDefault().Name, ID = g.Key.Id, - ParentId = g.FirstOrDefault().SpecificationAttribute.Id, + PId = g.FirstOrDefault().SpecificationAttribute.Id, MatchCount = g.Count() }; @@ -313,8 +314,8 @@ from a in attributes c.Entity = ShortcutSpecAttribute; c.IsInactive = true; - if (c.ParentId != 0) - c.NameLocalized = _localizedEntityService.GetLocalizedValue(languageId, c.ParentId, "SpecificationAttribute", "Name"); + if (c.PId.HasValue) + c.NameLocalized = _localizedEntityService.GetLocalizedValue(languageId, c.PId.Value, "SpecificationAttribute", "Name"); if (c.ID.HasValue) c.ValueLocalized = _localizedEntityService.GetLocalizedValue(languageId, c.ID.Value, "SpecificationAttributeOption", "Name"); @@ -342,12 +343,13 @@ public virtual string Serialize(List criteria) //criteria.FindAll(c => c.Type.IsNullOrEmpty()).ForEach(c => c.Type = _defaultType); if (criteria != null && criteria.Count > 0) return JsonConvert.SerializeObject(criteria); + return ""; } public virtual FilterProductContext CreateFilterProductContext(string filter, int categoryID, string path, int? pagesize, int? orderby, string viewmode) { - var context = new FilterProductContext() + var context = new FilterProductContext { Filter = filter, ParentCategoryID = categoryID, @@ -366,6 +368,17 @@ public virtual FilterProductContext CreateFilterProductContext(string filter, in ); } + int languageId = _services.WorkContext.WorkingLanguage.Id; + + foreach (var criteria in context.Criteria.Where(x => x.Entity == ShortcutSpecAttribute)) + { + if (criteria.PId.HasValue) + criteria.NameLocalized = _localizedEntityService.GetLocalizedValue(languageId, criteria.PId.Value, "SpecificationAttribute", "Name"); + + if (criteria.ID.HasValue) + criteria.ValueLocalized = _localizedEntityService.GetLocalizedValue(languageId, criteria.ID.Value, "SpecificationAttributeOption", "Name"); + } + return context; } @@ -448,6 +461,7 @@ public virtual bool ToWhereClause(FilterSql context, List findIn context.Criteria.Clear(); // ! context.Criteria = findIn.FindAll(match); + return ToWhereClause(context); } @@ -569,7 +583,7 @@ public virtual void ProductFilterableMultiSelect(FilterProductContext context, s } // filters WITHOUT the multiple selectable filters - string excludedFilter = Serialize(context.Criteria); + var excludedFilter = Serialize(context.Criteria); // filters WITH the multiple selectable filters (required for highlighting selected values) context.Criteria = Deserialize(context.Filter); diff --git a/src/Presentation/SmartStore.Web/Controllers/FilterController.cs b/src/Presentation/SmartStore.Web/Controllers/FilterController.cs index 01a4528ad5..4b069a06f3 100644 --- a/src/Presentation/SmartStore.Web/Controllers/FilterController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/FilterController.cs @@ -18,6 +18,14 @@ public FilterController(IFilterService filterService, CatalogSettings catalogSet _catalogSettings = catalogSettings; } + private bool IsShowAllText(ICollection criteriaGroup) + { + if (criteriaGroup.Any(c => c.Entity == FilterService.ShortcutPrice)) + return false; + + return (criteriaGroup.Count >= _catalogSettings.MaxFilterItemsToDisplay || criteriaGroup.Any(c => !c.IsInactive)); + } + public ActionResult Products(string filter, int categoryID, string path, int? pagesize, int? orderby, string viewmode) { var context = _filterService.CreateFilterProductContext(filter, categoryID, path, pagesize, orderby, viewmode); @@ -39,7 +47,7 @@ public ActionResult Products(string active, string inactive, int categoryID, int // TODO: needed later for ajax based filtering... see example below //System.Threading.Thread.Sleep(3000); - var context = new FilterProductContext() + var context = new FilterProductContext { ParentCategoryID = categoryID, CategoryIds = new List { categoryID }, @@ -74,20 +82,13 @@ public ActionResult ProductsMultiSelect(string filter, int categoryID, string pa _filterService.ProductFilterableMultiSelect(context, filterMultiSelect); - return PartialView(new ProductFilterModel { + return PartialView(new ProductFilterModel + { Context = context, IsShowAllText = IsShowAllText(context.Criteria), MaxFilterItemsToDisplay = _catalogSettings.MaxFilterItemsToDisplay, ExpandAllFilterGroups = _catalogSettings.ExpandAllFilterCriteria }); } - - private bool IsShowAllText(ICollection criteriaGroup) - { - if (criteriaGroup.Any(c => c.Entity == FilterService.ShortcutPrice)) - return false; - - return (criteriaGroup.Count >= _catalogSettings.MaxFilterItemsToDisplay || criteriaGroup.Any(c => !c.IsInactive)); - } } } From 9ca7b8dc4751890d72b9cbfd1dbb87bc45330603 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 25 Oct 2015 13:59:55 +0100 Subject: [PATCH 003/732] Minor changes --- .../Migrations/201508091512101_ExportFramework.cs | 4 ++-- .../Migrations/201509021536425_ExportFramework1.cs | 2 +- .../SmartStore.Web/Administration/Views/Export/Edit.cshtml | 4 ++-- .../Views/Export/_CreateOrUpdate.Deployment.cshtml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs index 795d74921a..65999838ab 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs @@ -493,13 +493,13 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailSubject", "Email subject", "E-Mail Betreff", - "Specifies the subject of the data should be sent.", + "Specifies the subject of the email.", "Legt den Betreff der verschickten Daten fest."); builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailAccountId", "Email account", "E-Mail Konto", - "Specifies the email account through which the data should be sent.", + "Specifies the email account used to sent the data.", "Legt das E-Mail Konto fest, ber welches die Daten verschickt werden sollen."); } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs index 4f7df13d97..d8e9b8ab9d 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs @@ -96,7 +96,7 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.DataExchange.Export.FolderName", "Folder name", "Ordnername", - "Specifies the name of the folder where the data will be exported.", + "Specifies the name of the folder where to export the data.", "Legt den Namen des Ordners fest, in den die Daten exportiert werden."); builder.AddOrUpdate("Admin.DataExchange.Export.FolderAndFileName.Validate", diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index 05d16db1cc..529b85f5d4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -98,12 +98,12 @@ { @Model.Provider.FriendlyName.NaIfEmpty() - (@(Model.Provider.SystemName.NaIfEmpty())) } else { - @Model.Provider.FriendlyName.NaIfEmpty() (@(Model.Provider.SystemName.NaIfEmpty())) +
@Model.Provider.FriendlyName.NaIfEmpty()
} +
@(Model.Provider.SystemName.NaIfEmpty())
@if (Model.Provider.Description.HasValue()) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml index 3b79b6fdeb..c1e5c4abc5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml @@ -165,7 +165,7 @@ // show\hide file system path $('#@(Html.FieldIdFor(x => x.IsPublic))').change(function () { - var val = !$(this).is(':checked') && $('#@(Html.FieldIdFor(x => x.DeploymentType))').val() === @((int)ExportDeploymentType.FileSystem); + var val = !$(this).is(':checked') && $('#@(Html.FieldIdFor(x => x.DeploymentType))').val() == @((int)ExportDeploymentType.FileSystem); $('#FileSystemPathContainer').toggle(val); }).trigger('change'); }); From 04ed9722af36f27a9de0fc606c38b901f403a323 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 25 Oct 2015 14:02:57 +0100 Subject: [PATCH 004/732] Fixed "The object in the 'ProductVariantAttributeCombination_Product_Source' role cannot be automatically added to the context because it was retrieved using the NoTracking merge option" --- .../Catalog/CopyProductService.cs | 73 ++++++++++--------- .../Controllers/ProductController.cs | 2 + 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs index cbf04229fb..71792bc582 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; +using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Media; using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Seo; using SmartStore.Services.Stores; -using SmartStore.Core.Data; namespace SmartStore.Services.Catalog { @@ -103,6 +103,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli Extension = download.Extension, IsNew = download.IsNew, }; + _downloadService.InsertDownload(downloadCopy); downloadId = downloadCopy.Id; } @@ -123,6 +124,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli Extension = sampleDownload.Extension, IsNew = sampleDownload.IsNew }; + _downloadService.InsertDownload(sampleDownloadCopy); sampleDownloadId = sampleDownloadCopy.Id; } @@ -280,6 +282,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli true, false, false); + _productService.InsertProductPicture(new ProductPicture { ProductId = productCopy.Id, @@ -292,7 +295,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // product <-> categories mappings foreach (var productCategory in product.ProductCategories) { - var productCategoryCopy = new ProductCategory() + var productCategoryCopy = new ProductCategory { ProductId = productCopy.Id, CategoryId = productCategory.CategoryId, @@ -306,7 +309,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // product <-> manufacturers mappings foreach (var productManufacturers in product.ProductManufacturers) { - var productManufacturerCopy = new ProductManufacturer() + var productManufacturerCopy = new ProductManufacturer { ProductId = productCopy.Id, ManufacturerId = productManufacturers.ManufacturerId, @@ -320,30 +323,28 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // product <-> releated products mappings foreach (var relatedProduct in _productService.GetRelatedProductsByProductId1(product.Id, true)) { - _productService.InsertRelatedProduct( - new RelatedProduct() - { - ProductId1 = productCopy.Id, - ProductId2 = relatedProduct.ProductId2, - DisplayOrder = relatedProduct.DisplayOrder - }); + _productService.InsertRelatedProduct(new RelatedProduct + { + ProductId1 = productCopy.Id, + ProductId2 = relatedProduct.ProductId2, + DisplayOrder = relatedProduct.DisplayOrder + }); } // product <-> cross sells mappings foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true)) { - _productService.InsertCrossSellProduct( - new CrossSellProduct() - { - ProductId1 = productCopy.Id, - ProductId2 = csProduct.ProductId2, - }); + _productService.InsertCrossSellProduct(new CrossSellProduct + { + ProductId1 = productCopy.Id, + ProductId2 = csProduct.ProductId2, + }); } // product specifications foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes) { - var psaCopy = new ProductSpecificationAttribute() + var psaCopy = new ProductSpecificationAttribute { ProductId = productCopy.Id, SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId, @@ -351,6 +352,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage, DisplayOrder = productSpecificationAttribute.DisplayOrder }; + _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy); } @@ -364,9 +366,10 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // product <-> attributes mappings var associatedAttributes = new Dictionary(); var associatedAttributeValues = new Dictionary(); + foreach (var productVariantAttribute in _productAttributeService.GetProductVariantAttributesByProductId(product.Id)) { - var productVariantAttributeCopy = new ProductVariantAttribute() + var productVariantAttributeCopy = new ProductVariantAttribute { ProductId = productCopy.Id, ProductAttributeId = productVariantAttribute.ProductAttributeId, @@ -375,15 +378,17 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli AttributeControlTypeId = productVariantAttribute.AttributeControlTypeId, DisplayOrder = productVariantAttribute.DisplayOrder }; + _productAttributeService.InsertProductVariantAttribute(productVariantAttributeCopy); //save associated value (used for combinations copying) associatedAttributes.Add(productVariantAttribute.Id, productVariantAttributeCopy.Id); // product variant attribute values var productVariantAttributeValues = _productAttributeService.GetProductVariantAttributeValues(productVariantAttribute.Id); + foreach (var productVariantAttributeValue in productVariantAttributeValues) { - var pvavCopy = new ProductVariantAttributeValue() + var pvavCopy = new ProductVariantAttributeValue { ProductVariantAttributeId = productVariantAttributeCopy.Id, Name = productVariantAttributeValue.Name, @@ -396,6 +401,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli LinkedProductId = productVariantAttributeValue.LinkedProductId, Quantity = productVariantAttributeValue.Quantity, }; + _productAttributeService.InsertProductVariantAttributeValue(pvavCopy); //save associated value (used for combinations copying) @@ -412,7 +418,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli } // attribute combinations - using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: true)) + using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: false)) { scope.LoadCollection(product, (Product p) => p.ProductVariantAttributeCombinations); } @@ -443,22 +449,20 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli var newPvav = _productAttributeService.GetProductVariantAttributeValueById(newPvavId); if (newPvav != null) { - newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, - newPva, newPvav.Id.ToString()); + newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, newPvav.Id.ToString()); } } } else { //just a text - newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, - newPva, oldPvaValueStr); + newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, oldPvaValueStr); } } } } } - var combinationCopy = new ProductVariantAttributeCombination() + var combinationCopy = new ProductVariantAttributeCombination { ProductId = productCopy.Id, AttributesXml = newAttributesXml, @@ -487,15 +491,14 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // tier prices foreach (var tierPrice in product.TierPrices) { - _productService.InsertTierPrice( - new TierPrice() - { - ProductId = productCopy.Id, - StoreId = tierPrice.StoreId, - CustomerRoleId = tierPrice.CustomerRoleId, - Quantity = tierPrice.Quantity, - Price = tierPrice.Price - }); + _productService.InsertTierPrice(new TierPrice + { + ProductId = productCopy.Id, + StoreId = tierPrice.StoreId, + CustomerRoleId = tierPrice.CustomerRoleId, + Quantity = tierPrice.Quantity, + Price = tierPrice.Price + }); } // product <-> discounts mapping @@ -513,7 +516,7 @@ public virtual Product CopyProduct(Product product, string newName, bool isPubli // associated products if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct) { - var searchContext = new ProductSearchContext() + var searchContext = new ProductSearchContext { ParentGroupedProductId = product.Id, PageSize = int.MaxValue, diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index 3f9fa79477..5b79e00770 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -1271,7 +1271,9 @@ public ActionResult CopyProduct(ProductModel model) { var product = _productService.GetProductById(copyModel.Id); var newProduct = _copyProductService.CopyProduct(product, copyModel.Name, copyModel.Published, copyModel.CopyImages); + NotifySuccess("The product is copied"); + return RedirectToAction("Edit", new { id = newProduct.Id }); } catch (Exception exc) From 83b197b9654d9fd5b5e2ffcbeda8f4284bc2c273 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 25 Oct 2015 14:28:32 +0100 Subject: [PATCH 005/732] Resolves #797 Payment method couldn't be loaded --- .../SmartStore.Services/Payments/PaymentService.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs index 7505bcea30..c029fc051b 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs @@ -381,10 +381,14 @@ public virtual ProcessPaymentResult ProcessPayment(ProcessPaymentRequest process /// Payment info required for an order processing public virtual void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) { - var paymentMethod = LoadPaymentMethodBySystemName(postProcessPaymentRequest.Order.PaymentMethodSystemName); - if (paymentMethod == null) - throw new SmartException("Payment method couldn't be loaded"); - paymentMethod.Value.PostProcessPayment(postProcessPaymentRequest); + if (postProcessPaymentRequest.Order.PaymentMethodSystemName.HasValue()) + { + var paymentMethod = LoadPaymentMethodBySystemName(postProcessPaymentRequest.Order.PaymentMethodSystemName); + if (paymentMethod == null) + throw new SmartException("Payment method couldn't be loaded"); + + paymentMethod.Value.PostProcessPayment(postProcessPaymentRequest); + } } /// From 88c296460f97a56bb8819e97cb66c434cc8f6f4c Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 26 Oct 2015 12:24:50 +0100 Subject: [PATCH 006/732] Proper handling of deactivated export providers due to uninstalled plugins --- .../201508091512101_ExportFramework.cs | 4 - .../201509150931528_ExportFramework2.cs | 4 + .../ExportTask/ExportProfileTask.cs | 16 +- .../ExportTask/ExportProfileTaskContext.cs | 2 +- .../Controllers/ExportController.cs | 196 ++++++++---------- .../Models/DataExchange/ExportProfileModel.cs | 9 +- .../Administration/Views/Export/Create.cshtml | 10 +- .../Administration/Views/Export/Edit.cshtml | 166 ++++++++------- .../Administration/Views/Export/List.cshtml | 17 +- 9 files changed, 219 insertions(+), 205 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs index 65999838ab..64bb41f4d8 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs @@ -112,10 +112,6 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Validation.EmailAddress", "Please enter a valid email address", "Bitte geben Sie eine gltige E-Mail Adresse ein"); - builder.AddOrUpdate("Admin.DataExchange.Export.ProviderSystemName.Validate", - "There were no export provider found for system name \"{0}\". A provider is mandatory for an export profile.", - "Es wurde kein Export-Provider mit dem Systemnamen \"{0}\" gefunden. Ein Provider ist fr ein Exportprofil zwingend erforderlich."); - builder.AddOrUpdate("Admin.DataExchange.Export.NoProfiles", "There were no export profiles found.", "Es wurden keine Exportprofile gefunden."); diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 5e3a2a8714..195519dbc5 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -48,6 +48,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "{0} folders were deleted", "{0} Verzeichnisse wurden gelscht"); + builder.AddOrUpdate("Admin.Common.ProviderNotLoaded", + "Cannot load the provider {0}.", + "Der Provider {0} konnte nicht geladen werden."); + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", "Diese Option wird in der Vorschau nicht bercksichtigt."); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 0d4d486c2e..66698ab1a3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -2617,11 +2617,6 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) { try { - if (ctx.Provider == null) - { - throw new SmartException("Export aborted because the export provider cannot be loaded"); - } - if (!ctx.Provider.IsValid()) { throw new SmartException("Export aborted because the export provider is not valid"); @@ -2887,7 +2882,12 @@ public void Execute(TaskExecutionContext taskContext) var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); var selectedEntityIds = HttpRuntime.Cache[selectedIdsCacheKey] as string; - var ctx = new ExportProfileTaskContext(taskContext, profile, _exportService.Value.LoadProvider(profile.ProviderSystemName), selectedEntityIds); + var provider = _exportService.Value.LoadProvider(profile.ProviderSystemName); + + if (provider == null) + throw new SmartException(_services.Localization.GetResource("Admin.Common.ProviderNotLoaded").FormatInvariant(profile.ProviderSystemName.NaIfEmpty())); + + var ctx = new ExportProfileTaskContext(taskContext, profile, provider, selectedEntityIds); HttpRuntime.Cache.Remove(selectedIdsCacheKey); @@ -2925,6 +2925,9 @@ public ExportExecuteResult Execute(string providerSystemName, var provider = _exportService.Value.LoadProvider(providerSystemName); + if (provider == null) + throw new SmartException(_services.Localization.GetResource("Admin.Common.ProviderNotLoaded").FormatInvariant(providerSystemName.NaIfEmpty())); + if (profile == null) profile = _exportService.Value.CreateVolatileProfile(provider); @@ -2982,6 +2985,7 @@ public ExportExecuteResult Execute(string providerSystemName, public void Preview(ExportProfile profile, Provider provider, IComponentContext context, int pageIndex, int totalRecords, Action previewData) { Guard.ArgumentNotNull(() => profile); + Guard.ArgumentNotNull(() => provider); Guard.ArgumentNotNull(() => context); var taskContext = new TaskExecutionContext(context, null); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs index 6e120e1866..2beab3db1b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs @@ -115,7 +115,7 @@ public string LogPath public bool IsFileBasedExport { - get { return Provider.Value.FileExtension.HasValue(); } + get { return Provider == null || Provider.Value == null || Provider.Value.FileExtension.HasValue(); } } public string[] GetDeploymentFiles(ExportDeployment deployment) { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 07952a0a1d..33d35d9fbb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -24,7 +24,6 @@ using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; -using SmartStore.Services.Media; using SmartStore.Services.Messages; using SmartStore.Services.Security; using SmartStore.Web.Framework; @@ -41,7 +40,6 @@ public class ExportController : AdminControllerBase private readonly ICommonServices _services; private readonly IExportService _exportService; private readonly PluginMediator _pluginMediator; - private readonly IPictureService _pictureService; private readonly ICategoryService _categoryService; private readonly IManufacturerService _manufacturerService; private readonly IProductTagService _productTagService; @@ -57,7 +55,6 @@ public ExportController( ICommonServices services, IExportService exportService, PluginMediator pluginMediator, - IPictureService pictureService, ICategoryService categoryService, IManufacturerService manufacturerService, IProductTagService productTagService, @@ -72,7 +69,6 @@ public ExportController( _services = services; _exportService = exportService; _pluginMediator = pluginMediator; - _pictureService = pictureService; _categoryService = categoryService; _manufacturerService = manufacturerService; _productTagService = productTagService; @@ -89,15 +85,15 @@ public ExportController( private string GetThumbnailUrl(Provider provider) { - if (provider == null) - return ""; + string url = null; - var url = _pluginMediator.GetIconUrl(provider.Metadata); + if (provider != null) + url = _pluginMediator.GetIconUrl(provider.Metadata); if (url.IsEmpty()) - url = _pictureService.GetDefaultPictureUrl(48); - else - url = Url.Content(url); + url = _pluginMediator.GetDefaultIconUrl(null); + + url = Url.Content(url); return url; } @@ -106,6 +102,7 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile { model.Id = profile.Id; model.Name = profile.Name; + model.ProviderSystemName = profile.ProviderSystemName; model.FolderName = profile.FolderName; model.FileNamePattern = profile.FileNamePattern; model.Enabled = profile.Enabled; @@ -114,18 +111,15 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile model.IsTaskRunning = profile.ScheduleTask.IsRunning; model.IsTaskEnabled = profile.ScheduleTask.Enabled; model.LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()); + model.HasActiveProvider = (provider != null); - model.Provider = new ExportProfileModel.ProviderModel - { - SystemName = profile.ProviderSystemName - }; + model.Provider = new ExportProfileModel.ProviderModel(); + model.Provider.ThumbnailUrl = GetThumbnailUrl(provider); if (provider != null) { var descriptor = provider.Metadata.PluginDescriptor; - model.Provider.ThumbnailUrl = GetThumbnailUrl(provider); - if (descriptor != null) { model.Provider.Url = descriptor.Url; @@ -172,39 +166,6 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile model.SerializedCompletedEmailAddresses = string.Join(",", profile.CompletedEmailAddresses.SplitSafe(",").Select(x => x.EncodeJsString())); - // get and initialize provider specific configuration data - if (provider != null) - { - model.Provider.Supporting = provider.Metadata.ExportSupport; - - try - { - var configInfo = provider.Value.ConfigurationInfo; - if (configInfo != null) - { - model.Provider.ConfigPartialViewName = configInfo.PartialViewName; - model.Provider.ConfigDataType = configInfo.ModelType; - model.Provider.ConfigData = XmlHelper.Deserialize(profile.ProviderConfigData, configInfo.ModelType); - - if (configInfo.Initialize != null) - { - try - { - configInfo.Initialize(model.Provider.ConfigData); - } - catch (Exception exc) - { - NotifyWarning(exc.ToAllMessages()); - } - } - } - } - catch (Exception exc) - { - NotifyError(exc); - } - } - // projection model.Projection = new ExportProjectionModel { @@ -242,21 +203,6 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile .Select(y => new SelectListItem { Text = y.Name, Value = y.Id.ToString() }) .ToList(); - - if (model.Provider.EntityType == ExportEntityType.Product) - { - model.Projection.AvailableDescriptionMergings = ExportDescriptionMerging.Description.ToSelectList(false); - model.Projection.AvailablePriceTypes = PriceDisplayType.LowestPrice.ToSelectList(false); - model.Projection.AvailableAttributeCombinationValueMerging = ExportAttributeValueMerging.AppendAllValuesToName.ToSelectList(false); - - model.Projection.SerializedAppendDescriptionText = string.Join(",", projection.AppendDescriptionText.SplitSafe(",").Select(x => x.EncodeJsString())); - model.Projection.SerializedCriticalCharacters = string.Join(",", projection.CriticalCharacters.SplitSafe(",").Select(x => x.EncodeJsString())); - } - else if (model.Provider.EntityType == ExportEntityType.Order) - { - model.Projection.AvailableOrderStatusChange = ExportOrderStatusChange.Processing.ToSelectList(false); - } - // filtering model.Filter = new ExportFilterModel { @@ -287,42 +233,6 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile .Select(y => new SelectListItem { Text = y.Name, Value = y.Id.ToString() }) .ToList(); - - if (model.Provider.EntityType == ExportEntityType.Product) - { - var allCategories = _categoryService.GetAllCategories(showHidden: true); - var mappedCategories = allCategories.ToDictionary(x => x.Id); - var allManufacturers = _manufacturerService.GetAllManufacturers(true); - var allProductTags = _productTagService.GetAllProductTags(); - - model.Filter.AvailableProductTypes = ProductType.SimpleProduct.ToSelectList(false).ToList(); - - model.Filter.AvailableCategories = allCategories - .Select(x => new SelectListItem { Text = x.GetCategoryNameWithPrefix(_categoryService, mappedCategories), Value = x.Id.ToString() }) - .ToList(); - - model.Filter.AvailableManufacturers = allManufacturers - .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) - .ToList(); - - model.Filter.AvailableProductTags = allProductTags - .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) - .ToList(); - } - else if (model.Provider.EntityType == ExportEntityType.Order) - { - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - - model.Filter.AvailableOrderStates = OrderStatus.Pending.ToSelectList(false).ToList(); - model.Filter.AvailablePaymentStates = PaymentStatus.Pending.ToSelectList(false).ToList(); - model.Filter.AvailableShippingStates = ShippingStatus.NotYetShipped.ToSelectList(false).ToList(); - - model.Filter.AvailableCustomerRoles = allCustomerRoles - .OrderBy(x => x.Name) - .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) - .ToList(); - } - // deployment model.Deployments = profile.Deployments .Select(x => @@ -364,6 +274,84 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile return deploymentModel; }) .ToList(); + + + if (provider != null) + { + model.Provider.Supporting = provider.Metadata.ExportSupport; + + if (model.Provider.EntityType == ExportEntityType.Product) + { + var allCategories = _categoryService.GetAllCategories(showHidden: true); + var mappedCategories = allCategories.ToDictionary(x => x.Id); + var allManufacturers = _manufacturerService.GetAllManufacturers(true); + var allProductTags = _productTagService.GetAllProductTags(); + + model.Projection.AvailableDescriptionMergings = ExportDescriptionMerging.Description.ToSelectList(false); + model.Projection.AvailablePriceTypes = PriceDisplayType.LowestPrice.ToSelectList(false); + model.Projection.AvailableAttributeCombinationValueMerging = ExportAttributeValueMerging.AppendAllValuesToName.ToSelectList(false); + + model.Projection.SerializedAppendDescriptionText = string.Join(",", projection.AppendDescriptionText.SplitSafe(",").Select(x => x.EncodeJsString())); + model.Projection.SerializedCriticalCharacters = string.Join(",", projection.CriticalCharacters.SplitSafe(",").Select(x => x.EncodeJsString())); + + model.Filter.AvailableProductTypes = ProductType.SimpleProduct.ToSelectList(false).ToList(); + + model.Filter.AvailableCategories = allCategories + .Select(x => new SelectListItem { Text = x.GetCategoryNameWithPrefix(_categoryService, mappedCategories), Value = x.Id.ToString() }) + .ToList(); + + model.Filter.AvailableManufacturers = allManufacturers + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + + model.Filter.AvailableProductTags = allProductTags + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + + } + else if (model.Provider.EntityType == ExportEntityType.Order) + { + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + + model.Projection.AvailableOrderStatusChange = ExportOrderStatusChange.Processing.ToSelectList(false); + + model.Filter.AvailableOrderStates = OrderStatus.Pending.ToSelectList(false).ToList(); + model.Filter.AvailablePaymentStates = PaymentStatus.Pending.ToSelectList(false).ToList(); + model.Filter.AvailableShippingStates = ShippingStatus.NotYetShipped.ToSelectList(false).ToList(); + + model.Filter.AvailableCustomerRoles = allCustomerRoles + .OrderBy(x => x.Name) + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + } + + try + { + var configInfo = provider.Value.ConfigurationInfo; + if (configInfo != null) + { + model.Provider.ConfigPartialViewName = configInfo.PartialViewName; + model.Provider.ConfigDataType = configInfo.ModelType; + model.Provider.ConfigData = XmlHelper.Deserialize(profile.ProviderConfigData, configInfo.ModelType); + + if (configInfo.Initialize != null) + { + try + { + configInfo.Initialize(model.Provider.ConfigData); + } + catch (Exception exc) + { + NotifyWarning(exc.ToAllMessages()); + } + } + } + } + catch (Exception exc) + { + NotifyError(exc); + } + } } private ExportDeploymentModel PrepareDeploymentModel(ExportDeployment deployment, Provider provider, bool forEdit) @@ -492,7 +480,7 @@ public ActionResult Create() model.Provider = new ExportProfileModel.ProviderModel(); - model.Provider.AvailableProviders = allProviders + model.AvailableProviders = allProviders .Select(x => { var item = new ExportProfileModel.ProviderSelectItem @@ -531,9 +519,9 @@ public ActionResult Create(ExportProfileModel model) if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) return AccessDeniedView(); - if (model.Provider.SystemName.HasValue()) + if (model.ProviderSystemName.HasValue()) { - var provider = _exportService.LoadProvider(model.Provider.SystemName); + var provider = _exportService.LoadProvider(model.ProviderSystemName); if (provider != null) { var profile = _exportService.InsertExportProfile(provider, model.CloneProfileId ?? 0); @@ -542,7 +530,7 @@ public ActionResult Create(ExportProfileModel model) } } - NotifyError(T("Admin.DataExchange.Export.ProviderSystemName.Validate", model.Provider.SystemName.NaIfEmpty())); + NotifyError(T("Admin.Common.ProviderNotLoaded", model.ProviderSystemName.NaIfEmpty())); return RedirectToAction("List"); } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index 08f7af67b3..80dce133e9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -18,10 +18,15 @@ public partial class ExportProfileModel : EntityModelBase public string AllString { get; set; } public string UnspecifiedString { get; set; } public bool LogFileExists { get; set; } + public bool HasActiveProvider { get; set; } [SmartResourceDisplayName("Admin.DataExchange.Export.Name")] public string Name { get; set; } + [SmartResourceDisplayName("Admin.DataExchange.Export.ProviderSystemName")] + public string ProviderSystemName { get; set; } + public List AvailableProviders { get; set; } + [SmartResourceDisplayName("Admin.DataExchange.Export.FolderName")] public string FolderName { get; set; } @@ -100,10 +105,6 @@ public class ProviderModel [SmartResourceDisplayName("Admin.Configuration.Plugins.Fields.Configure")] public string ConfigurationUrl { get; set; } - [SmartResourceDisplayName("Admin.DataExchange.Export.ProviderSystemName")] - public string SystemName { get; set; } - public List AvailableProviders { get; set; } - [SmartResourceDisplayName("Common.Provider")] public string FriendlyName { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Create.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Create.cshtml index 3af31d9510..f1f162fedf 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Create.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Create.cshtml @@ -5,17 +5,17 @@ }
- @if(Model.Provider.AvailableProviders.Count > 0) + @if(Model.AvailableProviders.Count > 0) {
+
- @Html.SmartLabelFor(model => model.Provider.SystemName) + @Html.SmartLabelFor(model => model.ProviderSystemName) - + @foreach (var item in Model.AvailableProviders) { } @@ -45,7 +45,7 @@   - @foreach (var item in Model.Provider.AvailableProviders) + @foreach (var item in Model.AvailableProviders) { @item.Description } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index 529b85f5d4..f9174a7b29 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -13,13 +13,16 @@
+ +

 

- -   - - - - @Html.SmartLabelFor(x => x.Provider.FriendlyName) - - - @if (Model.Provider.ConfigurationUrl.HasValue()) - { - - @Model.Provider.FriendlyName.NaIfEmpty() - } - else - { -
@Model.Provider.FriendlyName.NaIfEmpty()
- } -
@(Model.Provider.SystemName.NaIfEmpty())
- - - @if (Model.Provider.Description.HasValue()) - { - - - @Html.SmartLabelFor(x => x.Provider.Description) - - - @Html.Raw(Model.Provider.Description) - - - } - @if (Model.Provider.Url.HasValue()) - { + if (Model.HasActiveProvider) + { + - } - @if (Model.Provider.Author.HasValue()) - { + @if (Model.Provider.Description.HasValue()) + { + + + + + } + @if (Model.Provider.Url.HasValue()) + { + + + + + } + @if (Model.Provider.Author.HasValue()) + { + + + + + } + @if (Model.Provider.Version.HasValue()) + { + + + + + } - } - @if (Model.Provider.Version.HasValue()) - { - } - - - - - - - - -
- @Html.SmartLabelFor(x => x.Provider.Url) + @Html.SmartLabelFor(x => x.Provider.FriendlyName) - @Model.Provider.Url + @if (Model.Provider.ConfigurationUrl.HasValue()) + { + + @Model.Provider.FriendlyName.NaIfEmpty() + + } + else + { +
@Model.Provider.FriendlyName.NaIfEmpty()
+ } +
@(Model.ProviderSystemName.NaIfEmpty())
+ @Html.SmartLabelFor(x => x.Provider.Description) + + @Html.Raw(Model.Provider.Description) +
+ @Html.SmartLabelFor(x => x.Provider.Url) + + @Model.Provider.Url +
+ @Html.SmartLabelFor(x => x.Provider.Author) + + @Html.DisplayFor(x => x.Provider.Author) +
+ @Html.SmartLabelFor(x => x.Provider.Version) + + @Html.DisplayFor(x => x.Provider.Version) +
- @Html.SmartLabelFor(x => x.Provider.Author) + @Html.SmartLabelFor(x => x.Provider.EntityTypeName) - @Html.DisplayFor(x => x.Provider.Author) + @Html.DisplayFor(x => x.Provider.EntityTypeName)
- @Html.SmartLabelFor(x => x.Provider.Version) + @Html.SmartLabelFor(x => x.Provider.FileExtension) - @Html.DisplayFor(x => x.Provider.Version) + @Html.IconForFileExtension(Model.Provider.FileExtension, true)
- @Html.SmartLabelFor(x => x.Provider.EntityTypeName) - - @Html.DisplayFor(x => x.Provider.EntityTypeName) -
- @Html.SmartLabelFor(x => x.Provider.FileExtension) - - @Html.IconForFileExtension(Model.Provider.FileExtension, true) -
+ + } + else + { +
+ @T("Admin.Common.ProviderNotLoaded", Model.ProviderSystemName) +
+ } } @helper TabPartition() @@ -233,7 +251,7 @@ } else { -
@T("Admin.DataExchange.Export.NoProjection")
+
@T("Admin.DataExchange.Export.NoProjection")
} } @@ -257,7 +275,7 @@ else {
- @Html.Raw(T("Admin.DataExchange.Export.Configuration.NotRequired", Model.Provider.FriendlyName.NaIfEmpty())) + @Html.Raw(T("Admin.DataExchange.Export.Configuration.NotRequired", Model.Provider.FriendlyName ?? Model.ProviderSystemName))
} diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml index 0704662b78..689da7f9a4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml @@ -48,14 +48,17 @@ @profile.Name - @profile.Provider.FriendlyName.NaIfEmpty() -
@profile.Provider.SystemName
+ @if (profile.Provider.ConfigurationUrl.HasValue()) + { + @profile.Provider.FriendlyName.NaIfEmpty() + } +
@profile.ProviderSystemName
@Html.SymbolForBool(profile.Enabled) - @profile.Provider.EntityTypeName + @profile.Provider.EntityTypeName.NaIfEmpty() @Html.IconForFileExtension(profile.Provider.FileExtension, true) @@ -144,7 +147,7 @@ else callbackSuccess: function (resp) { dialog.find('.modal-body').html(resp); - $('select[name="Provider.SystemName"]').select2({ + $('select[name="ProviderSystemName"]').select2({ minimumResultsForSearch: 16, formatResult: providerSelectItemFormatting, formatSelection: providerSelectItemFormatting @@ -181,7 +184,7 @@ else }); // selected provider changed - $(document).on('change', 'select[name="Provider.SystemName"]', toggleProviderDescription); + $(document).on('change', 'select[name="ProviderSystemName"]', toggleProviderDescription); function refreshGrid() { setTimeout(function () { @@ -196,7 +199,7 @@ else function toggleProviderDescription() { dialog.find('.export-provider-description').hide(); - var option = dialog.find('select[name="Provider.SystemName"]').find(':selected'); + var option = dialog.find('select[name="ProviderSystemName"]').find(':selected'); $('#ExportProviderDescription' + option.attr('data-id')).fadeIn('fast'); } @@ -206,7 +209,7 @@ else // item.element is undefined for selection formatting if (option.length === 0) { - option = $('select[name="Provider.SystemName"]').find('option[value="' + item.id + '"]'); + option = $('select[name="ProviderSystemName"]').find('option[value="' + item.id + '"]'); } var html = '
', From 3100528a4d9868d149282bbbc594bc23bf705347 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Mon, 26 Oct 2015 19:09:45 +0100 Subject: [PATCH 007/732] Minor fixes --- .../SmartStore.Core/Extensions/MiscExtensions.cs | 12 ++++-------- src/Libraries/SmartStore.Data/SmartObjectContext.cs | 2 +- .../Configuration/SettingService.cs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs index 3e871038fe..865d5f8ed4 100644 --- a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs @@ -117,17 +117,13 @@ public static string ToHexString(this byte[] bytes, int length = 0) public static T GetMergedDataValue(this IMergedData mergedData, string key, T defaultValue) { - try + if (mergedData.MergedDataValues != null && !mergedData.MergedDataIgnore) { - if (mergedData.MergedDataValues != null && !mergedData.MergedDataIgnore) - { - object value; + object value; - if (mergedData.MergedDataValues.TryGetValue(key, out value)) - return (T)value; - } + if (mergedData.MergedDataValues.TryGetValue(key, out value)) + return (T)value; } - catch (Exception) { } return defaultValue; } diff --git a/src/Libraries/SmartStore.Data/SmartObjectContext.cs b/src/Libraries/SmartStore.Data/SmartObjectContext.cs index 97a8982b71..0d5356688c 100644 --- a/src/Libraries/SmartStore.Data/SmartObjectContext.cs +++ b/src/Libraries/SmartStore.Data/SmartObjectContext.cs @@ -53,7 +53,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) // && type.BaseType != null // && type.BaseType.IsGenericType // && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); - + var typesToRegister = from t in Assembly.GetExecutingAssembly().GetTypes() where t.Namespace.HasValue() && t.BaseType != null && diff --git a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs index d6efe2222c..9bdcecbfcb 100644 --- a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs +++ b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs @@ -81,7 +81,7 @@ protected virtual IDictionary> GetAllSettingsCa orderby s.Name, s.StoreId select s; var settings = query.ToList(); - var dictionary = new Dictionary>(); + var dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var s in settings) { var settingName = s.Name.ToLowerInvariant(); From e1615370eb4d5e1eed5f4869f21fdc4a33adc7ba Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 26 Oct 2015 22:06:27 +0100 Subject: [PATCH 008/732] Fixed issue with selected attributes at bundles with per item pricing --- .../Catalog/PriceCalculationService.cs | 2 +- .../Extensions/NameValueCollectionExtensions.cs | 4 ++-- .../SmartStore.Web/Controllers/CatalogHelper.cs | 14 +++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs index 28eebe4491..1ad7adb761 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs @@ -241,7 +241,7 @@ protected virtual decimal GetPreselectedPrice(Product product, PriceCalculationC // 2. find attribute combination for selected attributes and merge it if (!isBundle && selectedAttributes.Count > 0) { - string attributeXml = selectedAttributes.CreateSelectedAttributesXml(product.Id, attributes, _productAttributeParser, _services.Localization, + var attributeXml = selectedAttributes.CreateSelectedAttributesXml(product.Id, attributes, _productAttributeParser, _services.Localization, _downloadService, _catalogSettings, _httpRequestBase, new List(), true, bundleItemId); var combinations = context.AttributeCombinations.Load(product.Id); diff --git a/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs b/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs index c7fa1aa9dd..a2e53aa8da 100644 --- a/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs +++ b/src/Libraries/SmartStore.Services/Extensions/NameValueCollectionExtensions.cs @@ -16,9 +16,9 @@ public static class NameValueCollectionExtensions private static string AttributeFormatedName(int productAttributeId, int attributeId, int productId = 0, int bundleItemId = 0) { if (productId == 0) - return "product_attribute_{0}_{1}".FormatWith(productAttributeId, attributeId); + return "product_attribute_{0}_{1}".FormatInvariant(productAttributeId, attributeId); else - return "product_attribute_{0}_{1}_{2}_{3}".FormatWith(productId, bundleItemId, productAttributeId, attributeId); + return "product_attribute_{0}_{1}_{2}_{3}".FormatInvariant(productId, bundleItemId, productAttributeId, attributeId); } public static void AddProductAttribute(this NameValueCollection collection, int productAttributeId, int attributeId, int valueId, int productId = 0, int bundleItemId = 0) diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index ad36b89187..4f20eefd3c 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -230,7 +230,15 @@ public ProductDetailsModel PrepareProductDetailsPageModel( foreach (var itemData in bundleItems.Where(x => x.Item.Product.CanBeBundleItem())) { var item = itemData.Item; - var bundledProductModel = PrepareProductDetailsPageModel(item.Product, false, itemData, null, selectedAttributes); + var bundleItemAttributes = new NameValueCollection(); + var keyPrefix = "product_attribute_{0}_".FormatInvariant(itemData.Item.ProductId); + + foreach (var key in selectedAttributes.AllKeys.Where(x => x.HasValue() && x.StartsWith(keyPrefix))) + { + bundleItemAttributes.Add(key, selectedAttributes[key]); + } + + var bundledProductModel = PrepareProductDetailsPageModel(item.Product, false, itemData, null, bundleItemAttributes); bundledProductModel.BundleItem.Id = item.Id; bundledProductModel.BundleItem.Quantity = item.Quantity; @@ -238,11 +246,11 @@ public ProductDetailsModel PrepareProductDetailsPageModel( bundledProductModel.BundleItem.Visible = item.Visible; bundledProductModel.BundleItem.IsBundleItemPricing = item.BundleProduct.BundlePerItemPricing; - string bundleItemName = item.GetLocalized(x => x.Name); + var bundleItemName = item.GetLocalized(x => x.Name); if (bundleItemName.HasValue()) bundledProductModel.Name = bundleItemName; - string bundleItemShortDescription = item.GetLocalized(x => x.ShortDescription); + var bundleItemShortDescription = item.GetLocalized(x => x.ShortDescription); if (bundleItemShortDescription.HasValue()) bundledProductModel.ShortDescription = bundleItemShortDescription; From 7567fb4c77d300fbe8ae07fe26d655bf6f899e5d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 27 Oct 2015 12:02:52 +0100 Subject: [PATCH 009/732] Some changes to last commit --- src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs | 2 +- .../SmartStore.Web/Controllers/ProductController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs index 4f20eefd3c..beb33343b0 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogHelper.cs @@ -231,7 +231,7 @@ public ProductDetailsModel PrepareProductDetailsPageModel( { var item = itemData.Item; var bundleItemAttributes = new NameValueCollection(); - var keyPrefix = "product_attribute_{0}_".FormatInvariant(itemData.Item.ProductId); + var keyPrefix = "product_attribute_{0}_{1}".FormatInvariant(item.ProductId, item.Id); foreach (var key in selectedAttributes.AllKeys.Where(x => x.HasValue() && x.StartsWith(keyPrefix))) { diff --git a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs index 3b94189942..74c3d06c58 100644 --- a/src/Presentation/SmartStore.Web/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/ProductController.cs @@ -172,7 +172,7 @@ public ActionResult ProductDetails(int productId, string attributes) selectedAttributes.ConvertAttributeQueryData( _productAttributeParser.DeserializeQueryData(attributes), - product.ProductType == ProductType.BundledProduct ? 0 : product.Id); + product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing ? 0 : product.Id); var model = _helper.PrepareProductDetailsPageModel(product, selectedAttributes: selectedAttributes); From 9e1791f2042b44b831d9e3003e42a5e4c29ceafb Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 27 Oct 2015 14:34:30 +0100 Subject: [PATCH 010/732] Amazon Payment: Message tokens for billing address are not resolved in order placed email because the billing address is unavailable at this time (shipping address is used instead). Updated registration link. --- .../Messages/IMessageTokenProvider.cs | 2 +- .../Messages/MessageTokenProvider.cs | 26 +++++++++---------- .../Messages/WorkflowMessageService.cs | 5 +++- .../Localization/resources.de-de.xml | 3 +-- .../Localization/resources.en-us.xml | 3 +-- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs b/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs index add0f4e57a..d5456194c7 100644 --- a/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs @@ -15,7 +15,7 @@ public partial interface IMessageTokenProvider { void AddStoreTokens(IList tokens, Store store); - void AddOrderTokens(IList tokens, Order order, int languageId); + void AddOrderTokens(IList tokens, Order order, int languageId, bool skipBillingAddress = false); void AddShipmentTokens(IList tokens, Shipment shipment, int languageId); diff --git a/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs index 0d294a4c54..7591020c72 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs @@ -722,7 +722,7 @@ public virtual void AddContactDataTokens(IList tokens) tokens.Add(new Token("Contact.ContactEmailAddress", _contactDataSettings.ContactEmailAddress)); } - public virtual void AddOrderTokens(IList tokens, Order order, int languageId) + public virtual void AddOrderTokens(IList tokens, Order order, int languageId, bool skipBillingAddress = false) { tokens.Add(new Token("Order.ID", order.Id.ToString())); tokens.Add(new Token("Order.OrderNumber", order.GetOrderNumber())); @@ -730,18 +730,18 @@ public virtual void AddOrderTokens(IList tokens, Order order, int languag tokens.Add(new Token("Order.CustomerFullName", string.Format("{0} {1}", order.BillingAddress.FirstName, order.BillingAddress.LastName))); tokens.Add(new Token("Order.CustomerEmail", order.BillingAddress.Email)); - tokens.Add(new Token("Order.BillingFirstName", order.BillingAddress.FirstName)); - tokens.Add(new Token("Order.BillingLastName", order.BillingAddress.LastName)); - tokens.Add(new Token("Order.BillingPhoneNumber", order.BillingAddress.PhoneNumber)); - tokens.Add(new Token("Order.BillingEmail", order.BillingAddress.Email)); - tokens.Add(new Token("Order.BillingFaxNumber", order.BillingAddress.FaxNumber)); - tokens.Add(new Token("Order.BillingCompany", order.BillingAddress.Company)); - tokens.Add(new Token("Order.BillingAddress1", order.BillingAddress.Address1)); - tokens.Add(new Token("Order.BillingAddress2", order.BillingAddress.Address2)); - tokens.Add(new Token("Order.BillingCity", order.BillingAddress.City)); - tokens.Add(new Token("Order.BillingStateProvince", order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name) : "")); - tokens.Add(new Token("Order.BillingZipPostalCode", order.BillingAddress.ZipPostalCode)); - tokens.Add(new Token("Order.BillingCountry", order.BillingAddress.Country != null ? order.BillingAddress.Country.GetLocalized(x => x.Name) : "")); + tokens.Add(new Token("Order.BillingFirstName", skipBillingAddress ? "".NaIfEmpty() : order.BillingAddress.FirstName)); + tokens.Add(new Token("Order.BillingLastName", skipBillingAddress ? "" : order.BillingAddress.LastName)); + tokens.Add(new Token("Order.BillingPhoneNumber", skipBillingAddress ? "" : order.BillingAddress.PhoneNumber)); + tokens.Add(new Token("Order.BillingEmail", skipBillingAddress ? "" : order.BillingAddress.Email)); + tokens.Add(new Token("Order.BillingFaxNumber", skipBillingAddress ? "" : order.BillingAddress.FaxNumber)); + tokens.Add(new Token("Order.BillingCompany", skipBillingAddress ? "" : order.BillingAddress.Company)); + tokens.Add(new Token("Order.BillingAddress1", skipBillingAddress ? "" : order.BillingAddress.Address1)); + tokens.Add(new Token("Order.BillingAddress2", order.BillingAddress.Address2)); + tokens.Add(new Token("Order.BillingCity", skipBillingAddress ? "" : order.BillingAddress.City)); + tokens.Add(new Token("Order.BillingStateProvince", !skipBillingAddress && order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name) : "")); + tokens.Add(new Token("Order.BillingZipPostalCode", skipBillingAddress ? "" : order.BillingAddress.ZipPostalCode)); + tokens.Add(new Token("Order.BillingCountry", !skipBillingAddress && order.BillingAddress.Country != null ? order.BillingAddress.Country.GetLocalized(x => x.Name) : "")); tokens.Add(new Token("Order.ShippingMethod", order.ShippingMethod)); tokens.Add(new Token("Order.ShippingFirstName", order.ShippingAddress != null ? order.ShippingAddress.FirstName : "")); diff --git a/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs b/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs index 64adb4aa26..075b2f653b 100644 --- a/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs +++ b/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs @@ -467,10 +467,13 @@ public virtual int SendOrderPlacedCustomerNotification(Order order, int language if (messageTemplate == null) return 0; + // unavailable at this time. shipping address is used as billing address here, so do not resolve tokens. + var skipBillingAddress = order.PaymentMethodSystemName.IsCaseInsensitiveEqual("SmartStore.AmazonPay"); + //tokens var tokens = new List(); _messageTokenProvider.AddStoreTokens(tokens, store); - _messageTokenProvider.AddOrderTokens(tokens, order, languageId); + _messageTokenProvider.AddOrderTokens(tokens, order, languageId, skipBillingAddress); _messageTokenProvider.AddCustomerTokens(tokens, order.Customer); _messageTokenProvider.AddCompanyTokens(tokens); diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml index da4712b22d..3bdbb45a9b 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml @@ -8,14 +8,13 @@ - Registrieren Sie sich bei Amazon Payments.

+ Registrieren Sie sich zunächst bei Amazon Payments.

So richten Sie "Bezahlen mit Amazon" ein:

  • Tragen Sie Ihre Amazon-Zugangsdaten unten in die dafür vorgesehenen Felder ein. Sie finden diese Daten in Ihrem Amazon Seller Central Konto.
  • Ihre Händlernummer finden Sie dort rechts oben unter Einstellungen - Integrationseinstellungen.
  • Die beiden Zugangsschlüssel finden Sie dort links oben unter Integration - MWS Access Key. In diesem Dokument finden Sie Bilder, wie Sie diese erstellen.
  • Falls Sie Sofortbenachrichtigungen (IPN) erhalten möchten (SSL zwingend erforderlich!), so tragen Sie die unten aufgeführte IPN URL unter Einstellungen - Integrationseinstellungen - Sofortbenachrichtigungs-Einstellungen - Händler-URL ein.
-

Hinweis! Die Rechnungsadresse des Kunden erhalten Sie erst nach Bestätigung der Zahlungsautorisierung von Amazon, also i.d.R. erst kurz nach Versand der Bestellbestätigungs-Email. Um Missverständnisse zu vermeiden ist es ratsam, den Platzhalter für die Rechnungsadresse aus der Nachrichtenvorlage für die Bestellbestätigung zu entfernen.

Bitte fügen Sie Informationen zu "Bezahlen mit Amazon" auf Ihrer Seite der Zahlungsarten ein (siehe CMS - Seiten). Bildmaterial finden Sie hier. Textvorschläge:

  • Option 1: Bezahlen mit Amazon: Zahlen Sie jetzt mit den Zahl- und Lieferinformationen aus Ihrem Amazon-Konto.
  • diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml index 99d505ab8e..49fa863b0f 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml @@ -8,14 +8,13 @@ - Register now at Amazon Payments.

    + Register now at Amazon Payments.

    How to set up "Pay with Amazon":

    • Enter your Amazon credentials in the fields provided below. You can find these credentials in your Amazon Seller Central account.
    • You can find the Merchant ID in Seller Central at Settings - Integration Settings.
    • You can find both access keys in Seller Central at Integration - MWS Access Key.
    • If you would like to receive instant payment notifications (SSL required!) enter the IPN URL listed bewlow under Settings - Integration Settings - Instant Notification Settings - Merchant URL.
    -

    Note! You get the billing address of the customer after the payment has been authorised by Amazon, so usually shortly after the order confirmation E-mail has been sent to the customer. To avoid misunderstandings, it is therefore advisable to remove the placeholder for the billing address from the message template for order confirmations.

    Please add information about "Pay with Amazon" on your payment page (see CMS - Topics). You will find picture material here. Text suggestions:

    • Option 1: Pay with Amazon: Pay now with the payment and shipping information from your Amazon account.
    • From 5b92f50323c00ec6836e8aa921d9671eec31a49b Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 27 Oct 2015 18:22:20 +0100 Subject: [PATCH 011/732] Reverted last commit and added message token for billing address note in order placed customer notification --- .../Messages/IMessageTokenProvider.cs | 2 +- .../Messages/MessageTokenProvider.cs | 24 +++++----- .../Messages/WorkflowMessageService.cs | 5 +- .../SmartStore.AmazonPay/Description.txt | 2 +- .../Events/OrderEventConsumer.cs | 47 +++++++++++++++++++ .../Localization/resources.de-de.xml | 3 ++ .../Localization/resources.en-us.xml | 3 ++ .../SmartStore.AmazonPay.csproj | 1 + src/Plugins/SmartStore.AmazonPay/changelog.md | 44 +++++++++-------- 9 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs diff --git a/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs b/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs index d5456194c7..7265b4faa4 100644 --- a/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/IMessageTokenProvider.cs @@ -15,7 +15,7 @@ public partial interface IMessageTokenProvider { void AddStoreTokens(IList tokens, Store store); - void AddOrderTokens(IList tokens, Order order, int languageId, bool skipBillingAddress = false); + void AddOrderTokens(IList tokens, Order order, int languageId); void AddShipmentTokens(IList tokens, Shipment shipment, int languageId); diff --git a/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs index 7591020c72..b1e50412da 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageTokenProvider.cs @@ -722,7 +722,7 @@ public virtual void AddContactDataTokens(IList tokens) tokens.Add(new Token("Contact.ContactEmailAddress", _contactDataSettings.ContactEmailAddress)); } - public virtual void AddOrderTokens(IList tokens, Order order, int languageId, bool skipBillingAddress = false) + public virtual void AddOrderTokens(IList tokens, Order order, int languageId) { tokens.Add(new Token("Order.ID", order.Id.ToString())); tokens.Add(new Token("Order.OrderNumber", order.GetOrderNumber())); @@ -730,18 +730,18 @@ public virtual void AddOrderTokens(IList tokens, Order order, int languag tokens.Add(new Token("Order.CustomerFullName", string.Format("{0} {1}", order.BillingAddress.FirstName, order.BillingAddress.LastName))); tokens.Add(new Token("Order.CustomerEmail", order.BillingAddress.Email)); - tokens.Add(new Token("Order.BillingFirstName", skipBillingAddress ? "".NaIfEmpty() : order.BillingAddress.FirstName)); - tokens.Add(new Token("Order.BillingLastName", skipBillingAddress ? "" : order.BillingAddress.LastName)); - tokens.Add(new Token("Order.BillingPhoneNumber", skipBillingAddress ? "" : order.BillingAddress.PhoneNumber)); - tokens.Add(new Token("Order.BillingEmail", skipBillingAddress ? "" : order.BillingAddress.Email)); - tokens.Add(new Token("Order.BillingFaxNumber", skipBillingAddress ? "" : order.BillingAddress.FaxNumber)); - tokens.Add(new Token("Order.BillingCompany", skipBillingAddress ? "" : order.BillingAddress.Company)); - tokens.Add(new Token("Order.BillingAddress1", skipBillingAddress ? "" : order.BillingAddress.Address1)); + tokens.Add(new Token("Order.BillingFirstName", order.BillingAddress.FirstName)); + tokens.Add(new Token("Order.BillingLastName", order.BillingAddress.LastName)); + tokens.Add(new Token("Order.BillingPhoneNumber", order.BillingAddress.PhoneNumber)); + tokens.Add(new Token("Order.BillingEmail", order.BillingAddress.Email)); + tokens.Add(new Token("Order.BillingFaxNumber", order.BillingAddress.FaxNumber)); + tokens.Add(new Token("Order.BillingCompany", order.BillingAddress.Company)); + tokens.Add(new Token("Order.BillingAddress1", order.BillingAddress.Address1)); tokens.Add(new Token("Order.BillingAddress2", order.BillingAddress.Address2)); - tokens.Add(new Token("Order.BillingCity", skipBillingAddress ? "" : order.BillingAddress.City)); - tokens.Add(new Token("Order.BillingStateProvince", !skipBillingAddress && order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name) : "")); - tokens.Add(new Token("Order.BillingZipPostalCode", skipBillingAddress ? "" : order.BillingAddress.ZipPostalCode)); - tokens.Add(new Token("Order.BillingCountry", !skipBillingAddress && order.BillingAddress.Country != null ? order.BillingAddress.Country.GetLocalized(x => x.Name) : "")); + tokens.Add(new Token("Order.BillingCity", order.BillingAddress.City)); + tokens.Add(new Token("Order.BillingStateProvince", order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name) : "")); + tokens.Add(new Token("Order.BillingZipPostalCode", order.BillingAddress.ZipPostalCode)); + tokens.Add(new Token("Order.BillingCountry", order.BillingAddress.Country != null ? order.BillingAddress.Country.GetLocalized(x => x.Name) : "")); tokens.Add(new Token("Order.ShippingMethod", order.ShippingMethod)); tokens.Add(new Token("Order.ShippingFirstName", order.ShippingAddress != null ? order.ShippingAddress.FirstName : "")); diff --git a/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs b/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs index 075b2f653b..64adb4aa26 100644 --- a/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs +++ b/src/Libraries/SmartStore.Services/Messages/WorkflowMessageService.cs @@ -467,13 +467,10 @@ public virtual int SendOrderPlacedCustomerNotification(Order order, int language if (messageTemplate == null) return 0; - // unavailable at this time. shipping address is used as billing address here, so do not resolve tokens. - var skipBillingAddress = order.PaymentMethodSystemName.IsCaseInsensitiveEqual("SmartStore.AmazonPay"); - //tokens var tokens = new List(); _messageTokenProvider.AddStoreTokens(tokens, store); - _messageTokenProvider.AddOrderTokens(tokens, order, languageId, skipBillingAddress); + _messageTokenProvider.AddOrderTokens(tokens, order, languageId); _messageTokenProvider.AddCustomerTokens(tokens, order.Customer); _messageTokenProvider.AddCompanyTokens(tokens); diff --git a/src/Plugins/SmartStore.AmazonPay/Description.txt b/src/Plugins/SmartStore.AmazonPay/Description.txt index 92acfd1fab..383de5f866 100644 --- a/src/Plugins/SmartStore.AmazonPay/Description.txt +++ b/src/Plugins/SmartStore.AmazonPay/Description.txt @@ -1,6 +1,6 @@ FriendlyName: Pay with Amazon SystemName: SmartStore.AmazonPay -Version: 2.2.0.2 +Version: 2.2.0.3 Group: Payment MinAppVersion: 2.2.0 Author: SmartStore AG diff --git a/src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs b/src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs new file mode 100644 index 0000000000..16ff49247a --- /dev/null +++ b/src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs @@ -0,0 +1,47 @@ +using System.Linq; +using SmartStore.AmazonPay.Services; +using SmartStore.Core.Domain.Messages; +using SmartStore.Core.Events; +using SmartStore.Core.Plugins; +using SmartStore.Services; +using SmartStore.Services.Messages; +using SmartStore.Services.Orders; +using SmartStore.Web.Framework; + +namespace SmartStore.AmazonPay.Events +{ + public class OrderEventConsumer : IConsumer> + { + private readonly IPluginFinder _pluginFinder; + private readonly ICommonServices _services; + private readonly IOrderService _orderService; + + public OrderEventConsumer( + IPluginFinder pluginFinder, + ICommonServices services, + IOrderService orderService) + { + _pluginFinder = pluginFinder; + _services = services; + _orderService = orderService; + } + + public void HandleEvent(MessageTokensAddedEvent messageTokenEvent) + { + if (!messageTokenEvent.Message.Name.IsCaseInsensitiveEqual("OrderPlaced.CustomerNotification")) + return; + + var storeId = _services.StoreContext.CurrentStore.Id; + + if (!_pluginFinder.IsPluginReady(_services.Settings, AmazonPayCore.SystemName, storeId)) + return; + + var order = _orderService.SearchOrders(storeId, _services.WorkContext.CurrentCustomer.Id, null, null, null, null, null, null, null, null, 0, 1).FirstOrDefault(); + + var isAmazonPayment = (order != null && order.PaymentMethodSystemName.IsCaseInsensitiveEqual(AmazonPayCore.SystemName)); + var tokenValue = (isAmazonPayment ? _services.Localization.GetResource("Plugins.Payments.AmazonPay.BillingAddressMessageNote") : ""); + + messageTokenEvent.Tokens.Add(new Token("SmartStore.AmazonPay.BillingAddressMessageNote", tokenValue)); + } + } +} \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml index 3bdbb45a9b..63b8f6b811 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml @@ -23,6 +23,9 @@ Textvorschläge:

      ]]> + + Bitte beachten Sie, dass es sich bei dieser Rechnungsadresse unter Umständen nicht um die für diese Bestellung gültige handelt! + Es wurde keine Auftrags-Referenz-ID durch Amazon übermittelt! diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml index 49fa863b0f..d21b5bc3cb 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml @@ -23,6 +23,9 @@ Text suggestions:

      ]]> + + Please note that this billing address is possibly not the valid billing address for this order! + There was no order reference ID transmitted by Amazon! diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index ab7c222b62..399f6faa95 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -139,6 +139,7 @@ + diff --git a/src/Plugins/SmartStore.AmazonPay/changelog.md b/src/Plugins/SmartStore.AmazonPay/changelog.md index 416414c398..c22eca67c8 100644 --- a/src/Plugins/SmartStore.AmazonPay/changelog.md +++ b/src/Plugins/SmartStore.AmazonPay/changelog.md @@ -1,48 +1,52 @@ -#Release Notes# +#Release Notes + +##Pay with Amazon 2.2.0.3 +###Improvements +* Added message token %SmartStore.AmazonPay.BillingAddressMessageNote% for billing address note in order placed customer notification ##Pay with Amazon 2.2.0.2 -###Bugfixes### +###Bugfixes * Send currency code of primary store currency (not of working currency) to payment gateway ##Pay with Amazon 2.2.0.1 ### New Features * Supports order list label for new incoming IPNs -##Pay with Amazon 1.20## -###Bugfixes### +##Pay with Amazon 1.20 +###Bugfixes * PlatformID must be .NET ID not merchant ID. PlatformID needs to be identical for all orders -##Pay with Amazon 1.19## -###Bugfixes### +##Pay with Amazon 1.19 +###Bugfixes * Declined authorization IPN did not void the payment status -##Pay with Amazon 1.18## -###Bugfixes### +##Pay with Amazon 1.18 +###Bugfixes * Order wasn't found if the capturing\refunding took place at Amazon Seller Central and the notification came through IPN -##Pay with Amazon 1.17## -###Improvements### +##Pay with Amazon 1.17 +###Improvements * Amazon payments review -##Pay with Amazon 1.16## -###Bugfixes### +##Pay with Amazon 1.16 +###Bugfixes * Reflect refunds made at amazon seller central when using data polling -##Pay with Amazon 1.15## -###Bugfixes### +##Pay with Amazon 1.15 +###Bugfixes * Multistore configuration might be lost if "All stores" are left empty -##Pay with Amazon 1.14## -###Bugfixes### +##Pay with Amazon 1.14 +###Bugfixes * Data polling did not reflect the transaction status correctly if the action took place at amazon seller central -##Pay with Amazon 1.13## -###Bugfixes### +##Pay with Amazon 1.13 +###Bugfixes * Sometimes shipping method restrictions were not applied * Shipping tab in checkout may show "Shipping address is not set" even if set -##Pay with Amazon 1.12## -###Improvements### +##Pay with Amazon 1.12 +###Improvements * Created this changelog * Changed string „Bezahlen über Amazon“ into „Bezahlen mit Amazon“ From ff715fbba3042be3090adc736a2d3fee5649a668 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 27 Oct 2015 20:27:41 +0100 Subject: [PATCH 012/732] Minor change --- .../{OrderEventConsumer.cs => MessageTokenEventConsumer.cs} | 4 ++-- src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Plugins/SmartStore.AmazonPay/Events/{OrderEventConsumer.cs => MessageTokenEventConsumer.cs} (92%) diff --git a/src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs b/src/Plugins/SmartStore.AmazonPay/Events/MessageTokenEventConsumer.cs similarity index 92% rename from src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs rename to src/Plugins/SmartStore.AmazonPay/Events/MessageTokenEventConsumer.cs index 16ff49247a..78f10c09e2 100644 --- a/src/Plugins/SmartStore.AmazonPay/Events/OrderEventConsumer.cs +++ b/src/Plugins/SmartStore.AmazonPay/Events/MessageTokenEventConsumer.cs @@ -10,13 +10,13 @@ namespace SmartStore.AmazonPay.Events { - public class OrderEventConsumer : IConsumer> + public class MessageTokenEventConsumer : IConsumer> { private readonly IPluginFinder _pluginFinder; private readonly ICommonServices _services; private readonly IOrderService _orderService; - public OrderEventConsumer( + public MessageTokenEventConsumer( IPluginFinder pluginFinder, ICommonServices services, IOrderService orderService) diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index 399f6faa95..f436a67f06 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -139,7 +139,7 @@ - + From 7806ca91b91d43ba8e28124755e0c34616bab6dd Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 28 Oct 2015 12:12:40 +0100 Subject: [PATCH 013/732] PayPal Standard: Closes #758. Option to add order note when order total validation fails. --- .../Controllers/PayPalStandardController.cs | 70 +++++++++++++------ src/Plugins/SmartStore.PayPal/Description.txt | 2 +- .../Localization/resources.de-de.xml | 30 ++++---- .../Localization/resources.en-us.xml | 30 ++++---- .../PayPalStandardConfigurationModel.cs | 6 +- .../Settings/PayPalSettings.cs | 2 +- .../Views/PayPalStandard/Configure.cshtml | 19 ++++- src/Plugins/SmartStore.PayPal/changelog.md | 4 ++ 8 files changed, 112 insertions(+), 51 deletions(-) diff --git a/src/Plugins/SmartStore.PayPal/Controllers/PayPalStandardController.cs b/src/Plugins/SmartStore.PayPal/Controllers/PayPalStandardController.cs index 10aa50046e..0256173046 100644 --- a/src/Plugins/SmartStore.PayPal/Controllers/PayPalStandardController.cs +++ b/src/Plugins/SmartStore.PayPal/Controllers/PayPalStandardController.cs @@ -119,8 +119,12 @@ public override ProcessPaymentRequest GetPaymentInfo(FormCollection form) [ValidateInput(false)] public ActionResult PDTHandler(FormCollection form) { - string tx = _webHelper.QueryString("tx"); Dictionary values; + var tx = _webHelper.QueryString("tx"); + var utcNow = DateTime.UtcNow; + var orderNumberGuid = Guid.Empty; + var orderNumber = string.Empty; + var total = decimal.Zero; string response; var provider = _paymentService.LoadPaymentMethodBySystemName("Payments.PayPalStandard", true); @@ -132,18 +136,18 @@ public ActionResult PDTHandler(FormCollection form) if (processor.GetPDTDetails(tx, settings, out values, out response)) { - string orderNumber = string.Empty; values.TryGetValue("custom", out orderNumber); - Guid orderNumberGuid = Guid.Empty; + try { orderNumberGuid = new Guid(orderNumber); } catch { } - Order order = _orderService.GetOrderByGuid(orderNumberGuid); + + var order = _orderService.GetOrderByGuid(orderNumberGuid); + if (order != null) { - decimal total = decimal.Zero; try { total = decimal.Parse(values["mc_gross"], new CultureInfo("en-US")); @@ -174,26 +178,48 @@ public ActionResult PDTHandler(FormCollection form) string payment_fee = string.Empty; values.TryGetValue("payment_fee", out payment_fee); - string paymentNote = _localizationService.GetResource("Plugins.Payments.PayPalStandard.PaymentNote").FormatWith( + var paymentNote = _localizationService.GetResource("Plugins.Payments.PayPalStandard.PaymentNote").FormatInvariant( total, mc_currency, payer_status, payment_status, pending_reason, txn_id, payment_type, payer_id, receiver_id, invoice, payment_fee); - //order note - order.OrderNotes.Add(new OrderNote() + order.OrderNotes.Add(new OrderNote { Note = paymentNote, DisplayToCustomer = false, - CreatedOnUtc = DateTime.UtcNow + CreatedOnUtc = utcNow }); - _orderService.UpdateOrder(order); - //validate order total - if (settings.PdtValidateOrderTotal && !Math.Round(total, 2).Equals(Math.Round(order.OrderTotal, 2))) + //validate order total... you may get differences if settings.PassProductNamesAndTotals is true + if (settings.PdtValidateOrderTotal) { - Logger.Error(_localizationService.GetResource("Plugins.Payments.PayPalStandard.UnequalTotalOrder").FormatWith(total, order.OrderTotal)); + var roundedTotal = Math.Round(total, 2); + var roundedOrderTotal = Math.Round(order.OrderTotal, 2); + var roundedDifference = Math.Abs(roundedTotal - roundedOrderTotal); + + if (!roundedTotal.Equals(roundedOrderTotal)) + { + var message = _localizationService.GetResource("Plugins.Payments.PayPalStandard.UnequalTotalOrder").FormatInvariant( + total, roundedOrderTotal.FormatInvariant(), order.OrderTotal, roundedDifference.FormatInvariant()); + + if (settings.PdtValidateOnlyWarn) + { + order.OrderNotes.Add(new OrderNote + { + Note = message, + DisplayToCustomer = false, + CreatedOnUtc = utcNow + }); + } + else + { + Logger.Error(message); - return RedirectToAction("Index", "Home", new { area = "" }); + return RedirectToAction("Index", "Home", new { area = "" }); + } + } } + _orderService.UpdateOrder(order); + //mark order as paid if (_orderProcessingService.CanMarkOrderAsPaid(order)) { @@ -208,26 +234,27 @@ public ActionResult PDTHandler(FormCollection form) } else { - string orderNumber = string.Empty; values.TryGetValue("custom", out orderNumber); - Guid orderNumberGuid = Guid.Empty; + try { orderNumberGuid = new Guid(orderNumber); } catch { } - Order order = _orderService.GetOrderByGuid(orderNumberGuid); + + var order = _orderService.GetOrderByGuid(orderNumberGuid); + if (order != null) { - //order note - order.OrderNotes.Add(new OrderNote() + order.OrderNotes.Add(new OrderNote { Note = "{0} {1}".FormatWith(_localizationService.GetResource("Plugins.Payments.PayPalStandard.PdtFailed"), response), DisplayToCustomer = false, - CreatedOnUtc = DateTime.UtcNow + CreatedOnUtc = utcNow }); _orderService.UpdateOrder(order); } + return RedirectToAction("Index", "Home", new { area = "" }); } } @@ -235,7 +262,7 @@ public ActionResult PDTHandler(FormCollection form) [ValidateInput(false)] public ActionResult IPNHandler() { - Debug.WriteLine("PayPal Standard IPN: {0}".FormatWith(Request.ContentLength)); + Debug.WriteLine("PayPal Standard IPN: {0}".FormatInvariant(Request.ContentLength)); byte[] param = Request.BinaryRead(Request.ContentLength); string strRequest = Encoding.ASCII.GetString(param); @@ -249,6 +276,7 @@ public ActionResult IPNHandler() if (processor.VerifyIPN(strRequest, out values)) { #region values + decimal total = decimal.Zero; try { diff --git a/src/Plugins/SmartStore.PayPal/Description.txt b/src/Plugins/SmartStore.PayPal/Description.txt index ecf91d9d38..854a27fa9f 100644 --- a/src/Plugins/SmartStore.PayPal/Description.txt +++ b/src/Plugins/SmartStore.PayPal/Description.txt @@ -2,7 +2,7 @@ Description: Provides the PayPal payment methods PayPal Express, PayPal Standard and PayPal Direct. SystemName: SmartStore.PayPal Group: Payment -Version: 2.2.0.2 +Version: 2.2.0.3 MinAppVersion: 2.2.0 DisplayOrder: 1 FileName: SmartStore.PayPal.dll diff --git a/src/Plugins/SmartStore.PayPal/Localization/resources.de-de.xml b/src/Plugins/SmartStore.PayPal/Localization/resources.de-de.xml index d9f8e940a1..ba2d95cd8d 100644 --- a/src/Plugins/SmartStore.PayPal/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.PayPal/Localization/resources.de-de.xml @@ -271,6 +271,12 @@ Aktivieren Sie diese Option, falls der durch PayPal übermittelte Gesamtbetrag überprüft werden soll. + + Nur warnen + + + Legt fest, ob im Falle einer Nichtübereinstimmung des Gesamtbetrags lediglich eine Auftragsnotiz angelegt werden soll. Ansonsten wird ein Fehler erzeugt und der Zahlungsstatus wird nicht aktualisiert. + Zusätzliche Gebühren @@ -311,7 +317,7 @@ PayPal Standard PDT. Fehler beim Abruf von mc_gross. - PayPal Standard PDT. Der übermittelte Gesamtbetrag {0} entspricht nicht dem des Shops {1}. + PayPal Standard PDT. Der übermittelte Gesamtbetrag {0} entspricht nicht dem des Shops {1} (ungerundet {2}). Die Differenz beträgt {3}. PayPal Standard PDT ist fehlgeschlagen. @@ -343,17 +349,17 @@ +Total: {0} +Währung: {1} +Zahlenderstatus: {2} +Zahlungsstatus: {3} +Ausstehend (Grund): {4} +Transaktions-ID: {5} +Zahlungstyp: {6} +Zahlender-ID: {7} +Empfänger-ID: {8} +Rechnung: {9} +Zahlungsgebühr: {10}]]> diff --git a/src/Plugins/SmartStore.PayPal/Localization/resources.en-us.xml b/src/Plugins/SmartStore.PayPal/Localization/resources.en-us.xml index 3aebd55408..a0143309c7 100644 --- a/src/Plugins/SmartStore.PayPal/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.PayPal/Localization/resources.en-us.xml @@ -310,6 +310,12 @@ Check the box if the PDT handler should validate PayPal's order totals. + + Only warn + + + Specifies whether to add an order note if the order total is unequal. Otherwise an error will be logged and the payment status is not updated. + Additional fee @@ -350,7 +356,7 @@ PayPal Standard PDT. Error getting mc_gross. - PayPal Standard PDT. Returned order total {0} doesn't equal order total {1}. + PayPal Standard PDT. Returned order total {0} doesn't equal order total {1} (not rounded {2}). The difference is {3}. PayPal Standard PDT failed. @@ -382,17 +388,17 @@ +Total: {0} +Currency: {1} +Payer status: {2} +Payment status: {3} +Pending reason: {4} +Transaction ID: {5} +Payment type: {6} +Payer ID: {7} +Receiver ID: {8} +Invoice: {9} +Payment fee: {10}]]> diff --git a/src/Plugins/SmartStore.PayPal/Models/PayPalStandardConfigurationModel.cs b/src/Plugins/SmartStore.PayPal/Models/PayPalStandardConfigurationModel.cs index 76f329c55c..d928e57927 100644 --- a/src/Plugins/SmartStore.PayPal/Models/PayPalStandardConfigurationModel.cs +++ b/src/Plugins/SmartStore.PayPal/Models/PayPalStandardConfigurationModel.cs @@ -18,6 +18,9 @@ public class PayPalStandardConfigurationModel : ModelBase [SmartResourceDisplayName("Plugins.Payments.PayPalStandard.Fields.PDTValidateOrderTotal")] public bool PdtValidateOrderTotal { get; set; } + [SmartResourceDisplayName("Plugins.Payments.PayPalStandard.Fields.PdtValidateOnlyWarn")] + public bool PdtValidateOnlyWarn { get; set; } + [SmartResourceDisplayName("Plugins.Payments.PayPalStandard.Fields.AdditionalFee")] public decimal AdditionalFee { get; set; } @@ -41,6 +44,7 @@ public void Copy(PayPalStandardPaymentSettings settings, bool fromSettings) BusinessEmail = settings.BusinessEmail; PdtToken = settings.PdtToken; PdtValidateOrderTotal = settings.PdtValidateOrderTotal; + PdtValidateOnlyWarn = settings.PdtValidateOnlyWarn; AdditionalFee = settings.AdditionalFee; AdditionalFeePercentage = settings.AdditionalFeePercentage; PassProductNamesAndTotals = settings.PassProductNamesAndTotals; @@ -53,13 +57,13 @@ public void Copy(PayPalStandardPaymentSettings settings, bool fromSettings) settings.BusinessEmail = BusinessEmail; settings.PdtToken = PdtToken; settings.PdtValidateOrderTotal = PdtValidateOrderTotal; + settings.PdtValidateOnlyWarn = PdtValidateOnlyWarn; settings.AdditionalFee = AdditionalFee; settings.AdditionalFeePercentage = AdditionalFeePercentage; settings.PassProductNamesAndTotals = PassProductNamesAndTotals; settings.EnableIpn = EnableIpn; settings.IpnUrl = IpnUrl; } - } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.PayPal/Settings/PayPalSettings.cs b/src/Plugins/SmartStore.PayPal/Settings/PayPalSettings.cs index 9e3adea8da..863197acc0 100644 --- a/src/Plugins/SmartStore.PayPal/Settings/PayPalSettings.cs +++ b/src/Plugins/SmartStore.PayPal/Settings/PayPalSettings.cs @@ -72,7 +72,6 @@ public class PayPalStandardPaymentSettings : PayPalSettingsBase, ISettings public PayPalStandardPaymentSettings() { UseSandbox = true; - PdtValidateOrderTotal = true; EnableIpn = true; } @@ -80,6 +79,7 @@ public PayPalStandardPaymentSettings() public string PdtToken { get; set; } public bool PassProductNamesAndTotals { get; set; } public bool PdtValidateOrderTotal { get; set; } + public bool PdtValidateOnlyWarn { get; set; } public bool EnableIpn { get; set; } public string IpnUrl { get; set; } } diff --git a/src/Plugins/SmartStore.PayPal/Views/PayPalStandard/Configure.cshtml b/src/Plugins/SmartStore.PayPal/Views/PayPalStandard/Configure.cshtml index c4b1c4b0eb..70868da9e5 100644 --- a/src/Plugins/SmartStore.PayPal/Views/PayPalStandard/Configure.cshtml +++ b/src/Plugins/SmartStore.PayPal/Views/PayPalStandard/Configure.cshtml @@ -61,6 +61,15 @@ @Html.ValidationMessageFor(model => model.PdtValidateOrderTotal) + + + @Html.SmartLabelFor(model => model.PdtValidateOnlyWarn) + + + @Html.SettingEditorFor(model => model.PdtValidateOnlyWarn) + @Html.ValidationMessageFor(model => model.PdtValidateOnlyWarn) + + @Html.SmartLabelFor(model => model.AdditionalFee) @@ -97,7 +106,7 @@ @Html.ValidationMessageFor(model => model.EnableIpn) - + @Html.SmartLabelFor(model => model.IpnUrl) @@ -106,7 +115,7 @@ @Html.ValidationMessageFor(model => model.IpnUrl) - +
      @T("Plugins.Payments.PayPalStandard.Fields.EnableIpn.Hint2") @@ -130,7 +139,11 @@ $(document).ready(function () { $("#@Html.FieldIdFor(model => model.EnableIpn)").change(function () { - $('.ipn-url').toggle($(this).is(':checked')); + $('.ipn-handling').toggle($(this).is(':checked')); + }).trigger('change'); + + $("#@Html.FieldIdFor(model => model.PdtValidateOrderTotal)").change(function () { + $('#PdtValidateOnlyWarnContainer').toggle($(this).is(':checked')); }).trigger('change'); }); diff --git a/src/Plugins/SmartStore.PayPal/changelog.md b/src/Plugins/SmartStore.PayPal/changelog.md index 62451fa42c..80afe49fdd 100644 --- a/src/Plugins/SmartStore.PayPal/changelog.md +++ b/src/Plugins/SmartStore.PayPal/changelog.md @@ -1,5 +1,9 @@ #Release Notes# +##Paypal 2.2.0.3 +### New Features +* Option to add order note when order total validation fails + ##PayPal 2.2.0.2 ###Improvements * Redirecting to payment provider performed by core instead of plugin From 1c98019fe07ed0d69695adb57e5f9c980f86dc15 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 29 Oct 2015 19:21:35 +0100 Subject: [PATCH 014/732] New interface to filter out payment methods --- ...06211043073_PaymentShippingRestrictions.cs | 8 ++-- .../201509150931528_ExportFramework2.cs | 4 ++ .../Payments/IPaymentFilter.cs | 48 +++++++++++++++++++ .../Payments/IPaymentService.cs | 5 ++ .../Payments/PaymentService.cs | 32 ++++++++++++- .../SmartStore.Services.csproj | 1 + .../Controllers/PaymentController.cs | 21 +++++++- .../Models/Payments/PaymentMethodEditModel.cs | 4 +- .../Administration/Views/Payment/Edit.cshtml | 22 ++++++++- .../Payments/PaymentServiceTests.cs | 5 +- 10 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/Libraries/SmartStore.Services/Payments/IPaymentFilter.cs diff --git a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs index 1f5f6223e8..5cd8878827 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs @@ -80,12 +80,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Configuration.Payment.Methods.RestrictionNote", - "Select customer roles, shipping methods, countries and order amounts for which you do not want to offer this payment method.", - "Whlen Sie Kundengruppen, Versandarten, Lnder und Bestellwerte, bei denen Sie diese Zahlungsmethode nicht anbieten mchten."); + "Select features for which you do not want to offer this payment method.", + "Whlen Sie Merkmale, bei denen Sie diese Zahlungsmethode nicht anbieten mchten."); builder.AddOrUpdate("Admin.Configuration.Shipping.Methods.RestrictionNote", - "Select customer roles and countries for which you do not want to offer this shipping method.", - "Whlen Sie Kundengruppen und Lnder, bei denen Sie diese Versandart nicht anbieten mchten."); + "Select features for which you do not want to offer this shipping method.", + "Whlen Sie Merkmale, bei denen Sie diese Versandart nicht anbieten mchten."); builder.AddOrUpdate("Admin.Configuration.Payment.Methods.ExcludedCustomerRole", diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 195519dbc5..afbc9f0d63 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -52,6 +52,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Cannot load the provider {0}.", "Der Provider {0} konnte nicht geladen werden."); + builder.AddOrUpdate("ActivityLog.EditPaymentMethod", + "Edited payment method '{0}' ({1})", + "Zahlungsart '{0}' ({1}) bearbeitet"); + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", "Diese Option wird in der Vorschau nicht bercksichtigt."); diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentFilter.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentFilter.cs new file mode 100644 index 0000000000..53962b2a8a --- /dev/null +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentFilter.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Plugins; + +namespace SmartStore.Services.Payments +{ + public partial interface IPaymentFilter + { + /// + /// Gets a value indicating whether a payment method should be filtered out + /// + /// Payment filter request + /// true filter out method, false do not filter out method + bool FilterOutPaymentMethod(PaymentFilterRequest request); + + /// + /// Get URL for filter configuration + /// + /// Payment provider system name + /// URL for filter configuration + string GetConfigurationUrl(string systemName); + } + + + public partial class PaymentFilterRequest + { + /// + /// The payment method to be checked + /// + public Provider PaymentMethod { get; set; } + + /// + /// The context shopping cart + /// + public IList Cart { get; set; } + + /// + /// The context store identifier + /// + public int StoreId { get; set; } + + /// + /// The context customer + /// + public Customer Customer { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs index 6e45d3d0e2..a03fddee54 100644 --- a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs @@ -209,5 +209,10 @@ IEnumerable> LoadActivePaymentMethods( /// Masked credit card number string GetMaskedCreditCardNumber(string creditCardNumber); + /// + /// Gets all payment filters + /// + /// List of payment filters + IList GetAllPaymentFilters(); } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs index c029fc051b..9d78091d1c 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs @@ -9,6 +9,7 @@ using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Events; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Plugins; using SmartStore.Services.Common; using SmartStore.Services.Directory; @@ -37,6 +38,7 @@ public partial class PaymentService : IPaymentService private readonly ICurrencyService _currencyService; private readonly ICommonServices _services; private readonly IOrderTotalCalculationService _orderTotalCalculationService; + private readonly ITypeFinder _typeFinder; #endregion @@ -57,7 +59,8 @@ public PaymentService( IProviderManager providerManager, ICurrencyService currencyService, ICommonServices services, - IOrderTotalCalculationService orderTotalCalculationService) + IOrderTotalCalculationService orderTotalCalculationService, + ITypeFinder typeFinder) { this._paymentMethodRepository = paymentMethodRepository; this._paymentSettings = paymentSettings; @@ -67,6 +70,7 @@ public PaymentService( this._currencyService = currencyService; this._services = services; this._orderTotalCalculationService = orderTotalCalculationService; + this._typeFinder = typeFinder; } #endregion @@ -94,8 +98,16 @@ public virtual IEnumerable> LoadActivePaymentMethods( decimal? orderSubTotal = null; decimal? orderTotal = null; IList allMethods = null; + IList allFilters = null; IEnumerable> allProviders = null; + var filterRequest = new PaymentFilterRequest + { + Cart = cart, + StoreId = storeId, + Customer = customer + }; + if (types != null && types.Any()) allProviders = LoadAllPaymentMethods(storeId).Where(x => types.Contains(x.Value.PaymentMethodType)); else @@ -107,6 +119,16 @@ public virtual IEnumerable> LoadActivePaymentMethods( if (!p.Value.IsActive || !_paymentSettings.ActivePaymentMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)) return false; + // payment method filtering + if (allFilters == null) + allFilters = GetAllPaymentFilters(); + + filterRequest.PaymentMethod = p; + + if (allFilters.Any(x => x.FilterOutPaymentMethod(filterRequest))) + return false; + + // payment method core restrictions if (customer != null) { if (allMethods == null) @@ -196,6 +218,7 @@ public virtual IEnumerable> LoadActivePaymentMethods( } } } + return true; }); @@ -697,6 +720,13 @@ public virtual string GetMaskedCreditCardNumber(string creditCardNumber) return maskedChars + last4; } + public virtual IList GetAllPaymentFilters() + { + return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentFilter) + .ToList(); + } + #endregion } } diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 708211c879..0ae2169db8 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -207,6 +207,7 @@ + diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs index e0abb761b8..53a020c313 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs @@ -4,6 +4,7 @@ using SmartStore.Admin.Models.Payments; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Payments; +using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services; using SmartStore.Services.Customers; @@ -34,6 +35,7 @@ public partial class PaymentController : AdminControllerBase private readonly IShippingService _shippingService; private readonly ICountryService _countryService; private readonly ILocalizedEntityService _localizedEntityService; + private readonly ICustomerActivityService _customerActivityService; #endregion @@ -49,7 +51,8 @@ public PaymentController( ICustomerService customerService, IShippingService shippingService, ICountryService countryService, - ILocalizedEntityService localizedEntityService) + ILocalizedEntityService localizedEntityService, + ICustomerActivityService customerActivityService) { this._services = services; this._paymentService = paymentService; @@ -61,6 +64,7 @@ public PaymentController( this._shippingService = shippingService; this._countryService = countryService; this._localizedEntityService = localizedEntityService; + this._customerActivityService = customerActivityService; } #endregion @@ -72,6 +76,7 @@ private void PreparePaymentMethodEditModel(PaymentMethodEditModel model, Payment var customerRoles = _customerService.GetAllCustomerRoles(true); var shippingMethods = _shippingService.GetAllShippingMethods(); var countries = _countryService.GetAllCountries(true); + var allFilters = _paymentService.GetAllPaymentFilters(); model.AvailableCustomerRoles = new List(); model.AvailableShippingMethods = new List(); @@ -95,8 +100,15 @@ private void PreparePaymentMethodEditModel(PaymentMethodEditModel model, Payment model.AvailableCountries.Add(new SelectListItem { Text = country.GetLocalized(x => x.Name), Value = country.Id.ToString() }); } + + model.FilterConfigurationUrls = allFilters + .Select(x => "'" + x.GetConfigurationUrl(model.SystemName) + "'") + .OrderBy(x => x) + .ToList(); + if (paymentMethod != null) { + model.Id = paymentMethod.Id; model.ExcludedCustomerRoleIds = paymentMethod.ExcludedCustomerRoleIds.SplitSafe(","); model.ExcludedShippingMethodIds = paymentMethod.ExcludedShippingMethodIds.SplitSafe(","); model.ExcludedCountryIds = paymentMethod.ExcludedCountryIds.SplitSafe(","); @@ -196,7 +208,7 @@ public ActionResult Edit(string systemName) } [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")] - public ActionResult Edit(string systemName, bool continueEditing, PaymentMethodEditModel model) + public ActionResult Edit(string systemName, bool continueEditing, PaymentMethodEditModel model, FormCollection form) { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManagePaymentMethods)) return AccessDeniedView(); @@ -238,6 +250,11 @@ public ActionResult Edit(string systemName, bool continueEditing, PaymentMethodE _localizedEntityService.SaveLocalizedValue(paymentMethod, x => x.FullDescription, localized.FullDescription, localized.LanguageId); } + _services.EventPublisher.Publish(new ModelBoundEvent(model, paymentMethod, form)); + + _customerActivityService.InsertActivity("EditPaymentMethod", _services.Localization.GetResource("ActivityLog.EditPaymentMethod"), + model.FriendlyName.NaIfEmpty(), model.SystemName); + NotifySuccess(_services.Localization.GetResource("Admin.Common.DataEditSuccess")); return (continueEditing ? diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Payments/PaymentMethodEditModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Payments/PaymentMethodEditModel.cs index 1a84067969..28e7cfabe5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Payments/PaymentMethodEditModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Payments/PaymentMethodEditModel.cs @@ -7,15 +7,17 @@ namespace SmartStore.Admin.Models.Payments { - public class PaymentMethodEditModel : EntityModelBase, ILocalizedModel + public class PaymentMethodEditModel : TabbableModel, ILocalizedModel { public PaymentMethodEditModel() { Locales = new List(); + FilterConfigurationUrls = new List(); } public IList Locales { get; set; } public string IconUrl { get; set; } + public IList FilterConfigurationUrls { get; set; } [SmartResourceDisplayName("Common.SystemName")] public string SystemName { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Payment/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Payment/Edit.cshtml index 6151ad6e0d..6a0b4582a0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Payment/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Payment/Edit.cshtml @@ -23,7 +23,6 @@ @Html.ValidationSummary(false) @Html.HiddenFor(model => model.SystemName) - @Html.SmartStore().TabStrip().Name("payment-method-edit").Items(x => { x.Add().Text(T("Admin.Common.General").Text).Content(TabGeneral()).Selected(true); @@ -181,5 +180,26 @@ + +
      } + + \ No newline at end of file diff --git a/src/Tests/SmartStore.Services.Tests/Payments/PaymentServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Payments/PaymentServiceTests.cs index 2a4a4499af..b7148173fb 100644 --- a/src/Tests/SmartStore.Services.Tests/Payments/PaymentServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Payments/PaymentServiceTests.cs @@ -5,6 +5,7 @@ using SmartStore.Core.Data; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Plugins; using SmartStore.Services.Directory; using SmartStore.Services.Localization; @@ -24,6 +25,7 @@ public class PaymentServiceTests : ServiceTest ICurrencyService _currencyService; ICommonServices _services; IOrderTotalCalculationService _orderTotalCalculationService; + ITypeFinder _typeFinder; [SetUp] public new void SetUp() @@ -39,12 +41,13 @@ public class PaymentServiceTests : ServiceTest _currencyService = MockRepository.GenerateMock(); _services = MockRepository.GenerateMock(); _orderTotalCalculationService = MockRepository.GenerateMock(); + _typeFinder = MockRepository.GenerateMock(); var localizationService = MockRepository.GenerateMock(); localizationService.Expect(ls => ls.GetResource(null)).IgnoreArguments().Return("NotSupported").Repeat.Any(); _paymentService = new PaymentService(_paymentMethodRepository, _paymentSettings, pluginFinder, _shoppingCartSettings, - this.ProviderManager, _currencyService, _services, _orderTotalCalculationService); + this.ProviderManager, _currencyService, _services, _orderTotalCalculationService, _typeFinder); } [Test] From 1e4a1b0fa3c7ba593fa3ca0e9c5d240b17d7ac36 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 2 Nov 2015 13:11:15 +0100 Subject: [PATCH 015/732] New interface to filter out shipping methods --- .../Orders/OrderTotalCalculationService.cs | 2 +- ...ymentFilter.cs => IPaymentMethodFilter.cs} | 2 +- .../Payments/IPaymentService.cs | 2 +- .../Payments/PaymentService.cs | 10 +- .../Shipping/GetShippingOptionRequest.cs | 5 + .../Shipping/IShippingMethodFilter.cs | 35 +++++++ .../Shipping/IShippingService.cs | 13 ++- .../Shipping/ShippingService.cs | 91 ++++++++++++------- .../SmartStore.Services.csproj | 3 +- .../Providers/ByTotalProvider.cs | 2 +- .../Providers/FixedRateProvider.cs | 4 +- .../ByWeightShippingComputationMethod.cs | 2 +- .../Controllers/PaymentController.cs | 2 +- .../Controllers/ShippingController.cs | 6 ++ .../Infrastructure/AutoMapperStartupTask.cs | 3 +- .../Models/Shipping/ShippingMethodModel.cs | 3 + .../Shipping/_CreateOrUpdateMethod.cshtml | 21 +++++ .../Orders/OrderProcessingServiceTests.cs | 9 +- .../OrderTotalCalculationServiceTests.cs | 7 +- .../Shipping/ShippingServiceTests.cs | 6 +- 20 files changed, 173 insertions(+), 55 deletions(-) rename src/Libraries/SmartStore.Services/Payments/{IPaymentFilter.cs => IPaymentMethodFilter.cs} (96%) create mode 100644 src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs diff --git a/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs b/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs index 4aef87d01f..3e48061380 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs @@ -617,7 +617,7 @@ public virtual decimal AdjustShippingRate(decimal shippingRate, IList /// Gets a value indicating whether a payment method should be filtered out diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs index a03fddee54..ced73b9b10 100644 --- a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs @@ -213,6 +213,6 @@ IEnumerable> LoadActivePaymentMethods( /// Gets all payment filters ///
/// List of payment filters - IList GetAllPaymentFilters(); + IList GetAllPaymentMethodFilters(); } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs index 9d78091d1c..d7bd0f6176 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs @@ -98,7 +98,7 @@ public virtual IEnumerable> LoadActivePaymentMethods( decimal? orderSubTotal = null; decimal? orderTotal = null; IList allMethods = null; - IList allFilters = null; + IList allFilters = null; IEnumerable> allProviders = null; var filterRequest = new PaymentFilterRequest @@ -121,7 +121,7 @@ public virtual IEnumerable> LoadActivePaymentMethods( // payment method filtering if (allFilters == null) - allFilters = GetAllPaymentFilters(); + allFilters = GetAllPaymentMethodFilters(); filterRequest.PaymentMethod = p; @@ -720,10 +720,10 @@ public virtual string GetMaskedCreditCardNumber(string creditCardNumber) return maskedChars + last4; } - public virtual IList GetAllPaymentFilters() + public virtual IList GetAllPaymentMethodFilters() { - return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) - .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentFilter) + return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentMethodFilter) .ToList(); } diff --git a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs index 1962f97177..3439b6fd4f 100644 --- a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs +++ b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs @@ -16,6 +16,11 @@ public GetShippingOptionRequest() this.Items = new List(); } + /// + /// The context store identifier + /// + public int StoreId { get; set; } + /// /// Gets or sets a customer /// diff --git a/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs b/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs new file mode 100644 index 0000000000..5718dc56ff --- /dev/null +++ b/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs @@ -0,0 +1,35 @@ +using SmartStore.Core.Domain.Shipping; + +namespace SmartStore.Services.Shipping +{ + public partial interface IShippingMethodFilter + { + /// + /// Gets a value indicating whether a shipping method should be filtered out + /// + /// Shipping filter request + /// true filter out method, false do not filter out method + bool FilterOutShippingMethod(ShippingFilterRequest request); + + /// + /// Get URL for filter configuration + /// + /// Shipping method identifier + /// URL for filter configuration + string GetConfigurationUrl(int shippingMethodId); + } + + + public partial class ShippingFilterRequest + { + /// + /// The shipping method to be checked + /// + public ShippingMethod ShippingMethod { get; set; } + + /// + /// Shipping method request + /// + public GetShippingOptionRequest Request { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs b/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs index 8ec9c19c40..9018fab4b6 100644 --- a/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs @@ -53,9 +53,9 @@ public partial interface IShippingService /// /// Gets all shipping methods /// - /// Filter shipping methods by customer and apply payment method restrictions; null to load all records + /// Shipping option request to filter out shipping methods. null to load all shipping methods. /// Shipping method collection - IList GetAllShippingMethods(Customer customer = null); + IList GetAllShippingMethods(GetShippingOptionRequest request = null); /// /// Inserts a shipping method @@ -97,8 +97,9 @@ public partial interface IShippingService /// /// Shopping cart /// Shipping address + /// Store identifier /// Shipment package - GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress); + GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId); /// /// Gets available shipping options @@ -110,5 +111,11 @@ public partial interface IShippingService /// Shipping options GetShippingOptionResponse GetShippingOptions(IList cart, Address shippingAddress, string allowedShippingRateComputationMethodSystemName = "", int storeId = 0); + + /// + /// Gets all shipping method filters + /// + /// List of shipping method filters + IList GetAllShippingMethodFilters(); } } diff --git a/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs b/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs index 1959d39d27..ec8bd410ad 100644 --- a/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs @@ -9,6 +9,7 @@ using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Events; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services.Catalog; @@ -37,6 +38,7 @@ public partial class ShippingService : IShippingService private readonly ShoppingCartSettings _shoppingCartSettings; private readonly ISettingService _settingService; private readonly IProviderManager _providerManager; + private readonly ITypeFinder _typeFinder; #endregion @@ -71,7 +73,8 @@ public ShippingService(ICacheManager cacheManager, IEventPublisher eventPublisher, ShoppingCartSettings shoppingCartSettings, ISettingService settingService, - IProviderManager providerManager) + IProviderManager providerManager, + ITypeFinder typeFinder) { this._cacheManager = cacheManager; this._shippingMethodRepository = shippingMethodRepository; @@ -87,6 +90,7 @@ public ShippingService(ICacheManager cacheManager, this._shoppingCartSettings = shoppingCartSettings; this._settingService = settingService; this._providerManager = providerManager; + this._typeFinder = typeFinder; } #endregion @@ -184,13 +188,8 @@ public virtual ShippingMethod GetShippingMethodById(int shippingMethodId) return _shippingMethodRepository.GetById(shippingMethodId); } - - /// - /// Gets all shipping methods - /// - /// Filter shipping methods by customer and apply payment method restrictions; null to load all records - /// Shipping method collection - public virtual IList GetAllShippingMethods(Customer customer = null) + + public virtual IList GetAllShippingMethods(GetShippingOptionRequest request = null) { List customerRoleIds = null; @@ -201,31 +200,44 @@ orderby sm.DisplayOrder var allMethods = query.ToList(); - if (customer == null) + if (request == null) return allMethods; - var activeShippingMethods = allMethods.Where(x => + var allFilters = GetAllShippingMethodFilters(); + var filterRequest = new ShippingFilterRequest { Request = request }; + + var activeShippingMethods = allMethods.Where(s => { - // method restricted by customer role id? - var excludedRoleIds = x.ExcludedCustomerRoleIds.ToIntArray(); - if (excludedRoleIds.Any()) + // shipping method filtering + filterRequest.ShippingMethod = s; + + if (allFilters.Any(x => x.FilterOutShippingMethod(filterRequest))) + return false; + + // shipping method core restrictions + if (request.Customer != null) { - if (customerRoleIds == null) - customerRoleIds = customer.CustomerRoles.Where(r => r.Active).Select(r => r.Id).ToList(); + // method restricted by customer role id? + var excludedRoleIds = s.ExcludedCustomerRoleIds.ToIntArray(); + if (excludedRoleIds.Any()) + { + if (customerRoleIds == null) + customerRoleIds = request.Customer.CustomerRoles.Where(r => r.Active).Select(r => r.Id).ToList(); - if (customerRoleIds != null && !customerRoleIds.Except(excludedRoleIds).Any()) - return false; - } + if (customerRoleIds != null && !customerRoleIds.Except(excludedRoleIds).Any()) + return false; + } - // method restricted by country of selected billing or shipping address? - int countryId = 0; - if (x.CountryExclusionContext == CountryRestrictionContextType.ShippingAddress) - countryId = (customer.ShippingAddress != null ? (customer.ShippingAddress.CountryId ?? 0) : 0); - else - countryId = (customer.BillingAddress != null ? (customer.BillingAddress.CountryId ?? 0) : 0); + // method restricted by country of selected billing or shipping address? + int countryId = 0; + if (s.CountryExclusionContext == CountryRestrictionContextType.ShippingAddress) + countryId = (request.Customer.ShippingAddress != null ? (request.Customer.ShippingAddress.CountryId ?? 0) : 0); + else + countryId = (request.Customer.BillingAddress != null ? (request.Customer.BillingAddress.CountryId ?? 0) : 0); - if (countryId != 0 && x.CountryRestrictionExists(countryId)) - return false; + if (countryId != 0 && s.CountryRestrictionExists(countryId)) + return false; + } return true; }); @@ -354,20 +366,26 @@ public virtual decimal GetShoppingCartTotalWeight(IList /// Shopping cart /// Shipping address + /// Store identifier /// Shipment package - public virtual GetShippingOptionRequest CreateShippingOptionRequest(IList cart, - Address shippingAddress) + public virtual GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId) { var request = new GetShippingOptionRequest(); + request.StoreId = storeId; request.Customer = cart.GetCustomer(); - request.Items = new List(); - foreach (var sc in cart) - if (sc.Item.IsShipEnabled) - request.Items.Add(sc); request.ShippingAddress = shippingAddress; request.CountryFrom = null; request.StateProvinceFrom = null; request.ZipPostalCodeFrom = string.Empty; + + request.Items = new List(); + + foreach (var sc in cart) + { + if (sc.Item.IsShipEnabled) + request.Items.Add(sc); + } + return request; } @@ -389,7 +407,7 @@ public virtual GetShippingOptionResponse GetShippingOptions(IList @@ -441,6 +459,13 @@ public virtual GetShippingOptionResponse GetShippingOptions(IList GetAllShippingMethodFilters() + { + return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IShippingMethodFilter) + .ToList(); + } + #endregion #endregion diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 0ae2169db8..2de19dd74f 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -207,7 +207,7 @@ - + @@ -462,6 +462,7 @@ + diff --git a/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs b/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs index 9ca91fdca8..882a576f15 100644 --- a/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs +++ b/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs @@ -187,7 +187,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get decimal sqThreshold = _shippingByTotalSettings.SmallQuantityThreshold; decimal sqSurcharge = _shippingByTotalSettings.SmallQuantitySurcharge; - var shippingMethods = _shippingService.GetAllShippingMethods(getShippingOptionRequest.Customer); + var shippingMethods = _shippingService.GetAllShippingMethods(getShippingOptionRequest); foreach (var shippingMethod in shippingMethods) { decimal? rate = GetRate(subTotal, shippingMethod.Id, storeId, countryId, stateProvinceId, zip); diff --git a/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs b/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs index 3f51635f18..a4ae31c94e 100644 --- a/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs +++ b/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs @@ -55,7 +55,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get return response; } - var shippingMethods = this._shippingService.GetAllShippingMethods(getShippingOptionRequest.Customer); + var shippingMethods = this._shippingService.GetAllShippingMethods(getShippingOptionRequest); foreach (var shippingMethod in shippingMethods) { var shippingOption = new ShippingOption(); @@ -79,7 +79,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get if (getShippingOptionRequest == null) throw new ArgumentNullException("getShippingOptionRequest"); - var shippingMethods = this._shippingService.GetAllShippingMethods(getShippingOptionRequest.Customer); + var shippingMethods = this._shippingService.GetAllShippingMethods(getShippingOptionRequest); var rates = new List(); foreach (var shippingMethod in shippingMethods) diff --git a/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs b/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs index 01042c2d5d..0c59828d2a 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs +++ b/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs @@ -143,7 +143,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get } decimal weight = _shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items); - var shippingMethods = _shippingService.GetAllShippingMethods(getShippingOptionRequest.Customer); + var shippingMethods = _shippingService.GetAllShippingMethods(getShippingOptionRequest); foreach (var shippingMethod in shippingMethods) { var record = _shippingByWeightService.FindRecord(shippingMethod.Id, storeId, countryId, weight, zip); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs index 53a020c313..e2708847b0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs @@ -76,7 +76,7 @@ private void PreparePaymentMethodEditModel(PaymentMethodEditModel model, Payment var customerRoles = _customerService.GetAllCustomerRoles(true); var shippingMethods = _shippingService.GetAllShippingMethods(); var countries = _countryService.GetAllCountries(true); - var allFilters = _paymentService.GetAllPaymentFilters(); + var allFilters = _paymentService.GetAllPaymentMethodFilters(); model.AvailableCustomerRoles = new List(); model.AvailableShippingMethods = new List(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs index 05ee6e911d..e3a001ce12 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs @@ -79,6 +79,7 @@ private void PrepareShippingMethodModel(ShippingMethodModel model, ShippingMetho { var customerRoles = _customerService.GetAllCustomerRoles(true); var countries = _countryService.GetAllCountries(true); + var allFilters = _shippingService.GetAllShippingMethodFilters(); model.AvailableCustomerRoles = new List(); model.AvailableCountries = new List(); @@ -95,6 +96,11 @@ private void PrepareShippingMethodModel(ShippingMethodModel model, ShippingMetho model.AvailableCountries.Add(new SelectListItem { Text = country.GetLocalized(x => x.Name), Value = country.Id.ToString() }); } + model.FilterConfigurationUrls = allFilters + .Select(x => "'" + x.GetConfigurationUrl(shippingMethod.Id) + "'") + .OrderBy(x => x) + .ToList(); + if (shippingMethod != null) { model.ExcludedCustomerRoleIds = shippingMethod.ExcludedCustomerRoleIds.SplitSafe(","); diff --git a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs index b8b44b146e..42a7fcb454 100644 --- a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs +++ b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs @@ -396,7 +396,8 @@ public void Execute() .ForMember(dest => dest.ExcludedCountryIds, mo => mo.Ignore()) .ForMember(dest => dest.AvailableCustomerRoles, mo => mo.Ignore()) .ForMember(dest => dest.AvailableCountries, mo => mo.Ignore()) - .ForMember(dest => dest.AvailableCountryExclusionContextTypes, mo => mo.Ignore()); + .ForMember(dest => dest.AvailableCountryExclusionContextTypes, mo => mo.Ignore()) + .ForMember(dest => dest.FilterConfigurationUrls, mo => mo.Ignore()); Mapper.CreateMap() .ForMember(dest => dest.RestrictedCountries, mo => mo.Ignore()) .ForMember(dest => dest.ExcludedCustomerRoleIds, mo => mo.Ignore()) diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Shipping/ShippingMethodModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Shipping/ShippingMethodModel.cs index 4a8e380e73..d51b1aef24 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Shipping/ShippingMethodModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Shipping/ShippingMethodModel.cs @@ -15,8 +15,11 @@ public class ShippingMethodModel : EntityModelBase, ILocalizedModel(); + FilterConfigurationUrls = new List(); } + public IList FilterConfigurationUrls { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Shipping.Methods.Fields.Name")] [AllowHtml] public string Name { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Shipping/_CreateOrUpdateMethod.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Shipping/_CreateOrUpdateMethod.cshtml index 3639eef59c..fa6e0fa7e8 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Shipping/_CreateOrUpdateMethod.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Shipping/_CreateOrUpdateMethod.cshtml @@ -125,5 +125,26 @@ + +
} + + \ No newline at end of file diff --git a/src/Tests/SmartStore.Services.Tests/Orders/OrderProcessingServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Orders/OrderProcessingServiceTests.cs index ba2ee83ac7..afab03b6fc 100644 --- a/src/Tests/SmartStore.Services.Tests/Orders/OrderProcessingServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Orders/OrderProcessingServiceTests.cs @@ -17,6 +17,7 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Domain.Tax; using SmartStore.Core.Events; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services.Affiliates; @@ -91,8 +92,8 @@ public class OrderProcessingServiceTests : ServiceTest ICommonServices _services; HttpRequestBase _httpRequestBase; IGeoCountryLookup _geoCountryLookup; - Store _store; + ITypeFinder _typeFinder; [SetUp] public new void SetUp() @@ -120,6 +121,7 @@ public class OrderProcessingServiceTests : ServiceTest _localizationService = MockRepository.GenerateMock(); _settingService = MockRepository.GenerateMock(); + _typeFinder = MockRepository.GenerateMock(); //shipping _shippingSettings = new ShippingSettings(); @@ -127,6 +129,7 @@ public class OrderProcessingServiceTests : ServiceTest _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Add("FixedRateTestShippingRateComputationMethod"); _shippingMethodRepository = MockRepository.GenerateMock>(); _logger = new NullLogger(); + _shippingService = new ShippingService(cacheManager, _shippingMethodRepository, _logger, @@ -138,7 +141,9 @@ public class OrderProcessingServiceTests : ServiceTest _shippingSettings, pluginFinder, _eventPublisher, _shoppingCartSettings, _settingService, - this.ProviderManager); + this.ProviderManager, + _typeFinder); + _shipmentService = MockRepository.GenerateMock(); _paymentService = MockRepository.GenerateMock(); diff --git a/src/Tests/SmartStore.Services.Tests/Orders/OrderTotalCalculationServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Orders/OrderTotalCalculationServiceTests.cs index ffab0f5e54..77eb79f431 100644 --- a/src/Tests/SmartStore.Services.Tests/Orders/OrderTotalCalculationServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Orders/OrderTotalCalculationServiceTests.cs @@ -15,6 +15,7 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Domain.Tax; using SmartStore.Core.Events; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services.Catalog; @@ -66,6 +67,7 @@ public class OrderTotalCalculationServiceTests : ServiceTest HttpRequestBase _httpRequestBase; IGeoCountryLookup _geoCountryLookup; Store _store; + ITypeFinder _typeFinder; [SetUp] public new void SetUp() @@ -94,6 +96,7 @@ public class OrderTotalCalculationServiceTests : ServiceTest _localizationService = MockRepository.GenerateMock(); _settingService = MockRepository.GenerateMock(); + _typeFinder = MockRepository.GenerateMock(); //shipping _shippingSettings = new ShippingSettings(); @@ -101,6 +104,7 @@ public class OrderTotalCalculationServiceTests : ServiceTest _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Add("FixedRateTestShippingRateComputationMethod"); _shippingMethodRepository = MockRepository.GenerateMock>(); _logger = new NullLogger(); + _shippingService = new ShippingService(cacheManager, _shippingMethodRepository, _logger, @@ -112,7 +116,8 @@ public class OrderTotalCalculationServiceTests : ServiceTest _shippingSettings, pluginFinder, _eventPublisher, _shoppingCartSettings, _settingService, - this.ProviderManager); + this.ProviderManager, + _typeFinder); _providerManager = MockRepository.GenerateMock(); _checkoutAttributeParser = MockRepository.GenerateMock(); diff --git a/src/Tests/SmartStore.Services.Tests/Shipping/ShippingServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Shipping/ShippingServiceTests.cs index 7ffc605216..6f817238ec 100644 --- a/src/Tests/SmartStore.Services.Tests/Shipping/ShippingServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Shipping/ShippingServiceTests.cs @@ -18,6 +18,7 @@ using Rhino.Mocks; using SmartStore.Services.Common; using SmartStore.Services.Configuration; +using SmartStore.Core.Infrastructure; namespace SmartStore.Services.Tests.Shipping { @@ -36,6 +37,7 @@ public class ShippingServiceTests : ServiceTest IShippingService _shippingService; ShoppingCartSettings _shoppingCartSettings; ISettingService _settingService; + ITypeFinder _typeFinder; [SetUp] public new void SetUp() @@ -60,6 +62,7 @@ public class ShippingServiceTests : ServiceTest _localizationService = MockRepository.GenerateMock(); _genericAttributeService = MockRepository.GenerateMock(); _settingService = MockRepository.GenerateMock(); + _typeFinder = MockRepository.GenerateMock(); _shoppingCartSettings = new ShoppingCartSettings(); _shippingService = new ShippingService(cacheManager, @@ -73,7 +76,8 @@ public class ShippingServiceTests : ServiceTest _shippingSettings, pluginFinder, _eventPublisher, _shoppingCartSettings, _settingService, - this.ProviderManager); + this.ProviderManager, + _typeFinder); } [Test] From 9306e66eef12976d5386ef503a0018f7c935cde6 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 2 Nov 2015 13:16:04 +0100 Subject: [PATCH 016/732] Updated changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index e8364c5e2c..0244d80d1c 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ * #141 Payment and shipping methods by customer roles * #67 Restrict payment methods to countries * #94 Restrict payment methods to shipping methods +* #747 Restrict payment methods by old versus new customer (plugin) * #584 Email attachment support for message templates * Attach order invoice PDF automatically to order notification emails * #526 Min/Max amount option for which the payment method should be offered during checkout From 94fab62cffeb62a6c7fe7841318280bb04d42e13 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 2 Nov 2015 15:33:36 +0100 Subject: [PATCH 017/732] Resolves #802 Saving customer looses Customer Roles when validation fails --- .../Controllers/CustomerController.cs | 124 +++++++++++------- .../Views/Customer/_CreateOrUpdate.cshtml | 2 +- 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs index a609ee76e8..96de99a9d5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs @@ -250,11 +250,10 @@ private void PrepareCustomerModelForCreate(CustomerModel model) foreach (var tzi in _dateTimeHelper.GetSystemTimeZones()) { - model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == timeZoneId) }); + model.AvailableTimeZones.Add(new SelectListItem { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == timeZoneId) }); } model.DisplayVatNumber = false; - //customer roles model.AvailableCustomerRoles = _customerService .GetAllCustomerRoles(true) .Select(cr => cr.ToModel()) @@ -282,11 +281,11 @@ private void PrepareCustomerModelForCreate(CustomerModel model) if (_customerSettings.CountryEnabled) { - model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" }); + model.AvailableCountries.Add(new SelectListItem { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" }); foreach (var c in _countryService.GetAllCountries()) { - model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == model.CountryId) }); + model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == model.CountryId) }); } if (_customerSettings.StateProvinceEnabled) @@ -297,12 +296,12 @@ private void PrepareCustomerModelForCreate(CustomerModel model) { foreach (var s in states) { - model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); + model.AvailableStates.Add(new SelectListItem { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); } } else { - model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" }); + model.AvailableStates.Add(new SelectListItem { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" }); } } } @@ -585,7 +584,6 @@ public ActionResult Edit(int id) var customer = _customerService.GetCustomerById(id); if (customer == null || customer.Deleted) - //No customer found with the specified id return RedirectToAction("List"); var model = new CustomerModel(); @@ -599,12 +597,18 @@ public ActionResult Edit(int id) model.UsernamesEnabled = _customerSettings.UsernamesEnabled; model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames; model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone; - foreach (var tzi in _dateTimeHelper.GetSystemTimeZones()) - model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) }); + + foreach (var tzi in _dateTimeHelper.GetSystemTimeZones()) + { + model.AvailableTimeZones.Add(new SelectListItem { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) }); + } + model.DisplayVatNumber = _taxSettings.EuVatEnabled; model.VatNumber = customer.GetAttribute(SystemCustomerAttributeNames.VatNumber); + model.VatNumberStatusNote = ((VatNumberStatus)customer.GetAttribute(SystemCustomerAttributeNames.VatNumberStatusId)) .GetLocalizedEnum(_localizationService, _workContext); + model.CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc); model.LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc); model.LastIpAddress = customer.LastIpAddress; @@ -650,10 +654,10 @@ public ActionResult Edit(int id) //countries and states if (_customerSettings.CountryEnabled) { - model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" }); + model.AvailableCountries.Add(new SelectListItem { Text = T("Admin.Address.SelectCountry"), Value = "0" }); foreach (var c in _countryService.GetAllCountries()) { - model.AvailableCountries.Add(new SelectListItem() + model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString(), @@ -665,14 +669,17 @@ public ActionResult Edit(int id) { //states var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList(); - if (states.Count > 0) - { - foreach (var s in states) - model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); - } - else - model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" }); - + if (states.Count > 0) + { + foreach (var s in states) + { + model.AvailableStates.Add(new SelectListItem { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); + } + } + else + { + model.AvailableStates.Add(new SelectListItem { Text = T("Admin.Address.OtherNonUS"), Value = "0" }); + } } } @@ -681,12 +688,14 @@ public ActionResult Edit(int id) .GetAllCustomerRoles(true) .Select(cr => cr.ToModel()) .ToList(); + model.SelectedCustomerRoleIds = customer.CustomerRoles.Select(cr => cr.Id).ToArray(); model.AllowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles); + //reward points gistory model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled; model.AddRewardPointsValue = 0; - model.AddRewardPointsMessage = "Some comment here..."; + //external authentication records model.AssociatedExternalAuthRecords = GetAssociatedExternalAuthRecords(customer); @@ -703,19 +712,26 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti var customer = _customerService.GetCustomerById(model.Id); if (customer == null || customer.Deleted) - //No customer found with the specified id return RedirectToAction("List"); //validate customer roles - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - var newCustomerRoles = new List(); - foreach (var customerRole in allCustomerRoles) - if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id)) - newCustomerRoles.Add(customerRole); - var customerRolesError = ValidateCustomerRoles(newCustomerRoles); - if (!String.IsNullOrEmpty(customerRolesError)) - ModelState.AddModelError("", customerRolesError); - bool allowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles); + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + var allowManagingCustomerRoles = _permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles); + + if (allowManagingCustomerRoles) + { + var newCustomerRoles = new List(); + + foreach (var customerRole in allCustomerRoles) + { + if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id)) + newCustomerRoles.Add(customerRole); + } + + var customerRolesError = ValidateCustomerRoles(newCustomerRoles); + if (customerRolesError.HasValue()) + ModelState.AddModelError("", customerRolesError); + } if (ModelState.IsValid) { @@ -724,6 +740,7 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti customer.AdminComment = model.AdminComment; customer.IsTaxExempt = model.IsTaxExempt; customer.Active = model.Active; + //email if (!String.IsNullOrWhiteSpace(model.Email)) { @@ -778,8 +795,10 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.TimeZoneId, model.TimeZoneId); if (_customerSettings.GenderEnabled) _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Gender, model.Gender); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.FirstName, model.FirstName); _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.LastName, model.LastName); + if (_customerSettings.DateOfBirthEnabled) _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.DateOfBirth, model.DateOfBirth); if (_customerSettings.CompanyEnabled) @@ -844,6 +863,7 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti _customerActivityService.InsertActivity("EditCustomer", _localizationService.GetResource("ActivityLog.EditCustomer"), customer.Id); NotifySuccess(_localizationService.GetResource("Admin.Customers.Customers.Updated")); + return continueEditing ? RedirectToAction("Edit", customer.Id) : RedirectToAction("List"); } catch (Exception exc) @@ -857,15 +877,21 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti model.UsernamesEnabled = _customerSettings.UsernamesEnabled; model.AllowUsersToChangeUsernames = _customerSettings.AllowUsersToChangeUsernames; model.AllowCustomersToSetTimeZone = _dateTimeSettings.AllowCustomersToSetTimeZone; - foreach (var tzi in _dateTimeHelper.GetSystemTimeZones()) - model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) }); + + foreach (var tzi in _dateTimeHelper.GetSystemTimeZones()) + { + model.AvailableTimeZones.Add(new SelectListItem { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == model.TimeZoneId) }); + } + model.DisplayVatNumber = _taxSettings.EuVatEnabled; model.VatNumberStatusNote = ((VatNumberStatus)customer.GetAttribute(SystemCustomerAttributeNames.VatNumberStatusId)) .GetLocalizedEnum(_localizationService, _workContext); + model.CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc); model.LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc); model.LastIpAddress = model.LastIpAddress; model.LastVisitedPage = customer.GetAttribute(SystemCustomerAttributeNames.LastVisitedPage); + //form fields model.GenderEnabled = _customerSettings.GenderEnabled; model.DateOfBirthEnabled = _customerSettings.DateOfBirthEnabled; @@ -879,13 +905,14 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti model.StateProvinceEnabled = _customerSettings.StateProvinceEnabled; model.PhoneEnabled = _customerSettings.PhoneEnabled; model.FaxEnabled = _customerSettings.FaxEnabled; + //countries and states if (_customerSettings.CountryEnabled) { - model.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" }); + model.AvailableCountries.Add(new SelectListItem { Text = T("Admin.Address.SelectCountry"), Value = "0" }); foreach (var c in _countryService.GetAllCountries()) { - model.AvailableCountries.Add(new SelectListItem() + model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString(), @@ -897,28 +924,35 @@ public ActionResult Edit(CustomerModel model, bool continueEditing, FormCollecti { //states var states = _stateProvinceService.GetStateProvincesByCountryId(model.CountryId).ToList(); - if (states.Count > 0) - { - foreach (var s in states) - model.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); - } - else - model.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" }); - + if (states.Count > 0) + { + foreach (var s in states) + { + model.AvailableStates.Add(new SelectListItem { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); + } + } + else + { + model.AvailableStates.Add(new SelectListItem { Text = T("Admin.Address.OtherNonUS"), Value = "0" }); + } } } + //customer roles model.AvailableCustomerRoles = _customerService .GetAllCustomerRoles(true) .Select(cr => cr.ToModel()) .ToList(); + model.AllowManagingCustomerRoles = allowManagingCustomerRoles; - //reward points gistory + + //reward points gistory model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled; model.AddRewardPointsValue = 0; - model.AddRewardPointsMessage = "Some comment here..."; - //external authentication records + + //external authentication records model.AssociatedExternalAuthRecords = GetAssociatedExternalAuthRecords(customer); + return View(model); } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml index ab629aa3a4..b77a71517c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml @@ -516,7 +516,7 @@ @Html.SmartLabelFor(model => model.AddRewardPointsMessage) - @Html.EditorFor(model => model.AddRewardPointsMessage) + @Html.TextAreaFor(model => model.AddRewardPointsMessage) @Html.ValidationMessageFor(model => model.AddRewardPointsMessage) From 2833c586bd199c3a11f9781b1cea355bddb97242 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 2 Nov 2015 17:08:19 +0100 Subject: [PATCH 018/732] Minor change --- .../Controllers/DevToolsController.cs | 50 ++++++------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs b/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs index 20e212472d..d83af505bb 100644 --- a/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs +++ b/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using SmartStore.Core; +using System.Web.Mvc; using SmartStore.DevTools.Models; using SmartStore.Services; -using SmartStore.Services.Configuration; -using SmartStore.Services.Stores; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Settings; @@ -16,40 +9,27 @@ namespace SmartStore.DevTools.Controllers public class DevToolsController : SmartController { - private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; - private readonly IStoreService _storeService; - private readonly ISettingService _settingService; - - public DevToolsController( - IWorkContext workContext, - IStoreContext storeContext, - IStoreService storeService, - ISettingService settingService) + private readonly ICommonServices _services; + + public DevToolsController(ICommonServices services) { - _workContext = workContext; - _storeContext = storeContext; - _storeService = storeService; - _settingService = settingService; + _services = services; } - [AdminAuthorize] - [ChildActionOnly] + [AdminAuthorize, ChildActionOnly] public ActionResult Configure() { // load settings for a chosen store scope - var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext); - var settings = _settingService.LoadSetting(storeScope); + var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); + var settings = _services.Settings.LoadSetting(storeScope); var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); - storeDependingSettingHelper.GetOverrideKeys(settings, settings, storeScope, _settingService); + storeDependingSettingHelper.GetOverrideKeys(settings, settings, storeScope, _services.Settings); return View(settings); } - [AdminAuthorize] - [HttpPost] - [ChildActionOnly] + [HttpPost, AdminAuthorize, ChildActionOnly] public ActionResult Configure(ProfilerSettings model, FormCollection form) { if (!ModelState.IsValid) @@ -59,10 +39,10 @@ public ActionResult Configure(ProfilerSettings model, FormCollection form) // load settings for a chosen store scope var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); - var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext); + var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); - storeDependingSettingHelper.UpdateSettings(model /*settings*/, form, storeScope, _settingService); - _settingService.ClearCache(); + storeDependingSettingHelper.UpdateSettings(model /*settings*/, form, storeScope, _services.Settings); + _services.Settings.ClearCache(); return Configure(); } @@ -74,8 +54,8 @@ public ActionResult MiniProfiler() public ActionResult WidgetZone(string widgetZone) { - var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext); - var settings = _settingService.LoadSetting(storeScope); + var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); + var settings = _services.Settings.LoadSetting(storeScope); if (settings.DisplayWidgetZones) { From 652661481a51d22ed83f49a3ef491b9a51dbd919 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 3 Nov 2015 11:31:23 +0100 Subject: [PATCH 019/732] Minor changes --- .../Catalog/PriceCalculationService.cs | 2 +- .../Views/Common/SeNames.cshtml | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs index 1ad7adb761..dcb4b35d5f 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs @@ -744,7 +744,7 @@ public virtual decimal GetUnitPrice(OrganizedShoppingCartItem shoppingCartItem, { foreach (var bundleItem in shoppingCartItem.ChildItems) { - bundleItem.Item.Product.MergeWithCombination(bundleItem.Item.AttributesXml); + bundleItem.Item.Product.MergeWithCombination(bundleItem.Item.AttributesXml, _productAttributeParser); } var bundleItems = shoppingCartItem.ChildItems.Where(x => x.BundleItemData.IsValid()).Select(x => x.BundleItemData).ToList(); diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml index d703ee96ac..9d4219f6dd 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml @@ -3,7 +3,6 @@ @{ var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - //page title ViewBag.Title = T("Admin.System.SeNames").Text; } @using (Html.BeginForm()) @@ -17,6 +16,7 @@ +
@@ -37,6 +37,7 @@
+ @foreach (var cr in Model.AvailableCustomerRoles) @@ -66,5 +73,4 @@
@@ -51,16 +52,22 @@ .Width(50) .HtmlAttributes(new { style = "text-align:center" }) .HeaderHtmlAttributes(new { style = "text-align:center" }); - columns.Bound(x => x.Id).Width(50); + columns.Bound(x => x.Id) + .Width(100) + .Centered(); columns.Bound(x => x.Name); - columns.Bound(x => x.EntityId).Width(50); - columns.Bound(x => x.EntityName).Width(200); + columns.Bound(x => x.EntityId) + .Width(140) + .Centered(); + columns.Bound(x => x.EntityName) + .Width(200); columns.Bound(x => x.IsActive) - .Template(item => @Html.SymbolForBool(item.IsActive)) - .ClientTemplate(@Html.SymbolForBool("IsActive")) - .Centered() - .Width(100); - columns.Bound(x => x.Language).Width(200); + .Template(item => @Html.SymbolForBool(item.IsActive)) + .ClientTemplate(@Html.SymbolForBool("IsActive")) + .Width(100) + .Centered(); + columns.Bound(x => x.Language) + .Width(200); }) .Pageable(settings => settings.PageSize(gridPageSize).Position(GridPagerPosition.Both)) .DataBinding(dataBinding => dataBinding.Ajax().Select("SeNames", "Common")) From 436460c32ffd307d42976283d24d26aa7ed75936 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 3 Nov 2015 14:50:45 +0100 Subject: [PATCH 020/732] Resolves #722 System > SEO Names: Implement editing of an UrlRecord --- changelog.md | 1 + .../201509150931528_ExportFramework2.cs | 37 +++ .../Seo/IUrlRecordService.cs | 13 +- .../Seo/UrlRecordService.cs | 32 ++- .../Controllers/CommonController.cs | 79 ------- .../Controllers/UrlRecordController.cs | 207 +++++++++++++++++ .../UrlRecordListModel.cs | 10 +- .../{Common => UrlRecord}/UrlRecordModel.cs | 11 +- .../Administration/SmartStore.Admin.csproj | 11 +- .../Views/Common/SeNames.cshtml | 185 --------------- .../Views/UrlRecord/Edit.cshtml | 21 ++ .../Views/UrlRecord/List.cshtml | 216 ++++++++++++++++++ .../Views/UrlRecord/_CreateOrUpdate.cshtml | 61 +++++ .../Administration/sitemap.config | 2 +- 14 files changed, 604 insertions(+), 282 deletions(-) create mode 100644 src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs rename src/Presentation/SmartStore.Web/Administration/Models/{Common => UrlRecord}/UrlRecordListModel.cs (52%) rename src/Presentation/SmartStore.Web/Administration/Models/{Common => UrlRecord}/UrlRecordModel.cs (68%) delete mode 100644 src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml diff --git a/changelog.md b/changelog.md index 0244d80d1c..fb272219ce 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,7 @@ * New payment provider for Offline Payment Plugin: Purchase Order Number * #202 Implement option for product list 'default sort order' * #360 Import & export product variant combinations +* #722 System > SEO Names: Implement editing of an UrlRecord ### Improvements * (Perf) Implemented static caches for URL aliases and localized properties. Increases app startup and request speed by up to 30%. diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index afbc9f0d63..311a491a0e 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -39,6 +39,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) { builder.AddOrUpdate("Common.Example", "Example", "Beispiel"); builder.AddOrUpdate("Admin.Common.Selected", "Selected", "Ausgewhlte"); + builder.AddOrUpdate("Admin.Common.Entity", "Entity", "Entitt"); + builder.AddOrUpdate("Admin.Common.FilesDeleted", "{0} files were deleted", @@ -56,6 +58,41 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Edited payment method '{0}' ({1})", "Zahlungsart '{0}' ({1}) bearbeitet"); + + builder.AddOrUpdate("Admin.System.SeNames", "SEO Names", "SEO Namen"); + builder.Delete("Admin.System.SeNames.DeleteSelected"); + + builder.AddOrUpdate("Admin.System.SeNames.Name", + "SEO Name", + "SEO Name", + "Specifies the SEO name.", + "Legt den SEO Namen fest."); + + builder.AddOrUpdate("Admin.System.SeNames.EntityId", + "Entity ID", + "ID der Entitt", + "Specifies the ID of the associated entity.", + "Legt die ID der zugehrigen Entitt fest."); + + builder.AddOrUpdate("Admin.System.SeNames.EntityName", + "Entity name", + "Name der Entitt", + "Specifies the name of the associated entity.", + "Legt den Namen der zugehrigen Entitt fest."); + + builder.AddOrUpdate("Admin.System.SeNames.IsActive", + "Is active", + "Ist aktiv", + "Specifies whether the SEO name is active or inactive.", + "Legt fest, ob der SEO Name aktiv oder inaktiv ist."); + + builder.AddOrUpdate("Admin.System.SeNames.Language", + "Language", + "Sprache", + "Specifies the language of the SEO name.", + "Legt die Sprache des SEO Namens fest."); + + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", "Diese Option wird in der Vorschau nicht bercksichtigt."); diff --git a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs index 3f3451c12d..0981e33384 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -24,6 +24,13 @@ public partial interface IUrlRecordService /// URL record UrlRecord GetUrlRecordById(int urlRecordId); + /// + /// Gets URL records by identifiers + /// + /// + /// List of URL records + IList GetUrlRecordsByIds(int[] urlRecordIds); + /// /// Inserts an URL record /// @@ -46,11 +53,13 @@ public partial interface IUrlRecordService /// /// Gets all URL records /// - /// Slug /// Page index /// Page size + /// Slug + /// Entity name + /// Whether to load only active records /// Customer collection - IPagedList GetAllUrlRecords(string slug, int pageIndex, int pageSize); + IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, bool? isActive); /// /// Gets all URL records for the specified entity diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index dc5950affa..9c360bb699 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -63,6 +63,18 @@ public virtual UrlRecord GetUrlRecordById(int urlRecordId) return urlRecord; } + public virtual IList GetUrlRecordsByIds(int[] urlRecordIds) + { + if (urlRecordIds == null || urlRecordIds.Length == 0) + return new List(); + + var urlRecords = _urlRecordRepository + .Where(x => urlRecordIds.Contains(x.Id)) + .ToList(); + + return urlRecords; + } + public virtual void InsertUrlRecord(UrlRecord urlRecord) { if (urlRecord == null) @@ -85,15 +97,23 @@ public virtual void UpdateUrlRecord(UrlRecord urlRecord) _cacheManager.RemoveByPattern(URLRECORD_PATTERN_KEY); } - public virtual IPagedList GetAllUrlRecords(string slug, int pageIndex, int pageSize) + public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, bool? isActive) { var query = _urlRecordRepository.Table; - if (!String.IsNullOrWhiteSpace(slug)) - query = query.Where(ur => ur.Slug.Contains(slug)); - query = query.OrderBy(ur => ur.Slug); - var urlRecords = new PagedList(query, pageIndex, pageSize); - return urlRecords; + if (slug.HasValue()) + query = query.Where(x => x.Slug.Contains(slug)); + + if (entityName.HasValue()) + query = query.Where(x => x.EntityName == entityName); + + if (isActive.HasValue) + query = query.Where(x => x.IsActive == isActive.Value); + + query = query.OrderBy(x => x.Slug); + + var urlRecords = new PagedList(query, pageIndex, pageSize); + return urlRecords; } public virtual IList GetUrlRecordsFor(string entityName, int entityId, bool activeOnly = false) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index 9c4c7b4424..5c9c8e233b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -21,7 +21,6 @@ using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Security; -using SmartStore.Core.Domain.Seo; using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; using SmartStore.Core.Packaging; @@ -35,7 +34,6 @@ using SmartStore.Services.Media; using SmartStore.Services.Payments; using SmartStore.Services.Security; -using SmartStore.Services.Seo; using SmartStore.Services.Shipping; using SmartStore.Utilities; using SmartStore.Web.Framework; @@ -56,7 +54,6 @@ public class CommonController : AdminControllerBase private readonly Lazy _currencyService; private readonly Lazy _measureService; private readonly ICustomerService _customerService; - private readonly IUrlRecordService _urlRecordService; private readonly Lazy _commonSettings; private readonly Lazy _currencySettings; private readonly Lazy _measureSettings; @@ -83,7 +80,6 @@ public CommonController( Lazy currencyService, Lazy measureService, ICustomerService customerService, - IUrlRecordService urlRecordService, Lazy commonSettings, Lazy currencySettings, Lazy measureSettings, @@ -103,7 +99,6 @@ public CommonController( this._currencyService = currencyService; this._measureService = measureService; this._customerService = customerService; - this._urlRecordService = urlRecordService; this._commonSettings = commonSettings; this._currencySettings = currencySettings; this._measureSettings = measureSettings; @@ -981,80 +976,6 @@ public ActionResult RestartApplication(string previousUrl) #endregion - #region Search engine friendly names - - public ActionResult SeNames() - { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) - return AccessDeniedView(); - - var model = new UrlRecordListModel(); - return View(model); - } - - [HttpPost, GridAction(EnableCustomBinding = true)] - public ActionResult SeNames(GridCommand command, UrlRecordListModel model) - { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) - return AccessDeniedView(); - - var urlRecords = _urlRecordService.GetAllUrlRecords(model.SeName, command.Page - 1, command.PageSize); - var gridModel = new GridModel - { - Data = urlRecords.Select(x => - { - string languageName; - if (x.LanguageId == 0) - { - languageName = _localizationService.GetResource("Admin.System.SeNames.Language.Standard"); - } - else - { - var language = _languageService.GetLanguageById(x.LanguageId); - languageName = language != null ? language.Name : "Unknown"; - } - return new UrlRecordModel() - { - Id = x.Id, - Name = x.Slug, - EntityId = x.EntityId, - EntityName = x.EntityName, - IsActive = x.IsActive, - Language = languageName, - }; - }), - Total = urlRecords.TotalCount - }; - return new JsonResult - { - Data = gridModel - }; - } - - [HttpPost] - public ActionResult DeleteSelectedSeNames(ICollection selectedIds) - { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) - return AccessDeniedView(); - - if (selectedIds != null) - { - var urlRecords = new List(); - foreach (var id in selectedIds) - { - var urlRecord = _urlRecordService.GetUrlRecordById(id); - if (urlRecord != null) - urlRecords.Add(urlRecord); - } - foreach (var urlRecord in urlRecords) - _urlRecordService.DeleteUrlRecord(urlRecord); - } - - return Json(new { Result = true }); - } - - #endregion - #region Generic Attributes [ChildActionOnly] diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs new file mode 100644 index 0000000000..de577163fd --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using SmartStore.Admin.Models.UrlRecord; +using SmartStore.Core.Domain.Common; +using SmartStore.Core.Domain.Seo; +using SmartStore.Services; +using SmartStore.Services.Localization; +using SmartStore.Services.Security; +using SmartStore.Services.Seo; +using SmartStore.Web.Framework.Controllers; +using Telerik.Web.Mvc; + +namespace SmartStore.Admin.Controllers +{ + // TODO: add new permisssion record "ManageUrlRecords" + [AdminAuthorize] + public class UrlRecordController : AdminControllerBase + { + private readonly IUrlRecordService _urlRecordService; + private readonly AdminAreaSettings _adminAreaSettings; + private readonly ICommonServices _services; + private readonly ILanguageService _languageService; + + public UrlRecordController( + IUrlRecordService urlRecordService, + AdminAreaSettings adminAreaSettings, + ICommonServices services, + ILanguageService languageService) + { + _urlRecordService = urlRecordService; + _adminAreaSettings = adminAreaSettings; + _services = services; + _languageService = languageService; + } + + private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord) + { + var allLanguages = _languageService.GetAllLanguages(true); + + model.AvailableLanguages = allLanguages + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + + model.AvailableLanguages.Insert(0, new SelectListItem { Text = T("Admin.System.SeNames.Language.Standard"), Value = "0" }); + + if (urlRecord != null) + { + model.Id = urlRecord.Id; + model.Slug = urlRecord.Slug; + model.EntityId = urlRecord.EntityId; + model.EntityName = urlRecord.EntityName; + model.IsActive = urlRecord.IsActive; + model.LanguageId = urlRecord.LanguageId; + } + } + + public ActionResult List() + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + var model = new UrlRecordListModel + { + GridPageSize = _adminAreaSettings.GridPageSize + }; + + return View(model); + } + + [HttpPost, GridAction(EnableCustomBinding = true)] + public ActionResult List(GridCommand command, UrlRecordListModel model) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + var allLanguages = _languageService.GetAllLanguages(true); + var defaultLanguageName = T("Admin.System.SeNames.Language.Standard"); + + var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, model.SeName, model.EntityName, model.IsActive); + + var gridModel = new GridModel + { + Data = urlRecords.Select(x => + { + string languageName; + + if (x.LanguageId == 0) + { + languageName = defaultLanguageName; + } + else + { + var language = allLanguages.FirstOrDefault(y => y.Id == x.LanguageId); + languageName = (language != null ? language.Name : "".NaIfEmpty()); + } + + return new UrlRecordModel + { + Id = x.Id, + Slug = x.Slug, + EntityId = x.EntityId, + EntityName = x.EntityName, + IsActive = x.IsActive, + Language = languageName, + }; + }), + Total = urlRecords.TotalCount + }; + + return new JsonResult + { + Data = gridModel + }; + } + + public ActionResult Edit(int id) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + var urlRecord = _urlRecordService.GetUrlRecordById(id); + if (urlRecord == null) + return RedirectToAction("List"); + + var model = new UrlRecordModel(); + PrepareUrlRecordModel(model, urlRecord); + + return View(model); + } + + [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")] + public ActionResult Edit(UrlRecordModel model, bool continueEditing) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + var urlRecord = _urlRecordService.GetUrlRecordById(model.Id); + if (urlRecord == null) + return RedirectToAction("List"); + + if (ModelState.IsValid) + { + urlRecord.Slug = model.Slug; + urlRecord.EntityId = model.EntityId; + urlRecord.EntityName = model.EntityName; + urlRecord.IsActive = model.IsActive; + urlRecord.LanguageId = model.LanguageId; + + _urlRecordService.UpdateUrlRecord(urlRecord); + + NotifySuccess(T("Admin.Common.DataEditSuccess")); + + return continueEditing ? RedirectToAction("Edit", new { id = urlRecord.Id }) : RedirectToAction("List"); + } + + PrepareUrlRecordModel(model, null); + + return View(model); + } + + [HttpPost, ActionName("Delete")] + public ActionResult DeleteConfirmed(int id) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + var urlRecord = _urlRecordService.GetUrlRecordById(id); + if (urlRecord == null) + return RedirectToAction("List"); + + try + { + _urlRecordService.DeleteUrlRecord(urlRecord); + + NotifySuccess(T("Admin.Common.TaskSuccessfullyProcessed")); + + return RedirectToAction("List"); + } + catch (Exception exc) + { + NotifyError(exc); + return RedirectToAction("Edit", new { id = urlRecord.Id }); + } + } + + [HttpPost] + public ActionResult DeleteSelected(ICollection selectedIds) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + return AccessDeniedView(); + + if (selectedIds != null) + { + var urlRecords = _urlRecordService.GetUrlRecordsByIds(selectedIds.ToArray()); + + foreach (var urlRecord in urlRecords) + { + _urlRecordService.DeleteUrlRecord(urlRecord); + } + } + + return Json(new { Result = true }); + } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs similarity index 52% rename from src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordListModel.cs rename to src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs index 0a26749535..3b07866768 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordListModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs @@ -2,12 +2,20 @@ using SmartStore.Web.Framework; using SmartStore.Web.Framework.Mvc; -namespace SmartStore.Admin.Models.Common +namespace SmartStore.Admin.Models.UrlRecord { public partial class UrlRecordListModel : ModelBase { + public int GridPageSize { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.Name")] [AllowHtml] public string SeName { get; set; } + + [SmartResourceDisplayName("Admin.Common.Entity")] + public string EntityName { get; set; } + + [SmartResourceDisplayName("Common.IsActive")] + public bool? IsActive { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs similarity index 68% rename from src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordModel.cs rename to src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs index 6a5a5fb235..b3db7e2089 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Common/UrlRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs @@ -1,14 +1,15 @@ -using System.Web.Mvc; +using System.Collections.Generic; +using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Mvc; -namespace SmartStore.Admin.Models.Common +namespace SmartStore.Admin.Models.UrlRecord { public partial class UrlRecordModel : EntityModelBase { [SmartResourceDisplayName("Admin.System.SeNames.Name")] [AllowHtml] - public string Name { get; set; } + public string Slug { get; set; } [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] public int EntityId { get; set; } @@ -19,6 +20,10 @@ public partial class UrlRecordModel : EntityModelBase [SmartResourceDisplayName("Admin.System.SeNames.IsActive")] public bool IsActive { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.Language")] + public int LanguageId { get; set; } + public List AvailableLanguages { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.Language")] public string Language { get; set; } } diff --git a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj index 0c37de1799..564a1aa36f 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -218,6 +218,7 @@ + @@ -254,8 +255,6 @@ - - @@ -271,6 +270,8 @@ + + @@ -534,6 +535,9 @@ + + + @@ -1161,9 +1165,6 @@ - - - diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml deleted file mode 100644 index 9d4219f6dd..0000000000 --- a/src/Presentation/SmartStore.Web/Administration/Views/Common/SeNames.cshtml +++ /dev/null @@ -1,185 +0,0 @@ -@model UrlRecordListModel -@using Telerik.Web.Mvc.UI -@{ - var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - - ViewBag.Title = T("Admin.System.SeNames").Text; -} -@using (Html.BeginForm()) -{ -
-
- - @T("Admin.System.SeNames") -
-
- -
-
- - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SeName) - - @Html.EditorFor(model => Model.SeName) -
-   - - -
- - - - - -
- @(Html.Telerik().Grid() - .Name("sename-grid") - .ClientEvents(events => events.OnDataBinding("onDataBinding").OnDataBound("onDataBound")) - .Columns(columns => - { - columns.Bound(x => x.Id) - .ClientTemplate("") - .Title("") - .Width(50) - .HtmlAttributes(new { style = "text-align:center" }) - .HeaderHtmlAttributes(new { style = "text-align:center" }); - columns.Bound(x => x.Id) - .Width(100) - .Centered(); - columns.Bound(x => x.Name); - columns.Bound(x => x.EntityId) - .Width(140) - .Centered(); - columns.Bound(x => x.EntityName) - .Width(200); - columns.Bound(x => x.IsActive) - .Template(item => @Html.SymbolForBool(item.IsActive)) - .ClientTemplate(@Html.SymbolForBool("IsActive")) - .Width(100) - .Centered(); - columns.Bound(x => x.Language) - .Width(200); - }) - .Pageable(settings => settings.PageSize(gridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("SeNames", "Common")) - .EnableCustomBinding(true)) -
- - -} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml new file mode 100644 index 0000000000..ea6dfc9cdf --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml @@ -0,0 +1,21 @@ +@model UrlRecordModel +@using SmartStore.Admin.Models.UrlRecord; +@{ + var title = string.Concat(T("Admin.Common.Edit"), " ", T("Admin.System.SeNames.Name")); + ViewBag.Title = title; +} +@using (Html.BeginForm()) +{ +
+
+ @title - @Model.Slug @Html.ActionLink("(" + T("Admin.Common.BackToList") + ")", "List") +
+
+ + + +
+
+ @Html.Partial("_CreateOrUpdate", Model) +} +@Html.DeleteConfirmation("urlrecord-delete") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml new file mode 100644 index 0000000000..8df5062147 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml @@ -0,0 +1,216 @@ +@model UrlRecordListModel +@using SmartStore.Admin.Models.UrlRecord; +@using Telerik.Web.Mvc.UI +@{ + ViewBag.Title = T("Admin.System.SeNames").Text; +} + +@using (Html.BeginForm()) +{ +
+
+ + @T("Admin.System.SeNames") +
+
+ +
+
+ + + + + + + + + + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SeName) + + @Html.EditorFor(model => Model.SeName) +
+ @Html.SmartLabelFor(model => model.EntityName) + + @Html.EditorFor(model => Model.EntityName) +
+ @Html.SmartLabelFor(model => model.IsActive) + + @Html.EditorFor(model => Model.IsActive) +
+   + + +
+ +

+ + + + + +
+ @(Html.Telerik().Grid() + .Name("urlrecord-grid") + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(100) + .Centered(); + columns.Bound(x => x.Slug) + .ClientTemplate("<#= Slug #>"); + columns.Bound(x => x.EntityId) + .Width(140) + .Centered(); + columns.Bound(x => x.EntityName) + .Width(200); + columns.Bound(x => x.IsActive) + .Template(item => @Html.SymbolForBool(item.IsActive)) + .ClientTemplate(@Html.SymbolForBool("IsActive")) + .Width(100) + .Centered(); + columns.Bound(x => x.Language) + .Width(200); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "UrlRecord")) + .ClientEvents(events => events.OnDataBinding("onDataBinding").OnDataBound("onDataBound").OnRowDataBound("onRowDataBound")) + .EnableCustomBinding(true)) +
+} + + \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml new file mode 100644 index 0000000000..58798845b4 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml @@ -0,0 +1,61 @@ +@model UrlRecordModel +@using SmartStore.Admin.Models.UrlRecord; +@using Telerik.Web.Mvc.UI; + +@Html.HiddenFor(model => model.Id) + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.Id) + + @Html.DisplayFor(model => model.Id) +
+ @Html.SmartLabelFor(model => model.IsActive) + + @Html.EditorFor(model => model.IsActive) + @Html.ValidationMessageFor(model => model.IsActive) +
+ @Html.SmartLabelFor(model => model.Slug) + + @Html.EditorFor(model => model.Slug) + @Html.ValidationMessageFor(model => model.Slug) +
+ @Html.SmartLabelFor(model => model.EntityId) + + @Html.EditorFor(model => model.EntityId) + @Html.ValidationMessageFor(model => model.EntityId) +
+ @Html.SmartLabelFor(model => model.EntityName) + + @Html.EditorFor(model => model.EntityName) + @Html.ValidationMessageFor(model => model.EntityName) +
+ @Html.SmartLabelFor(x => x.LanguageId) + + @Html.DropDownListFor(x => x.LanguageId, Model.AvailableLanguages) + @Html.ValidationMessageFor(x => x.LanguageId) +
diff --git a/src/Presentation/SmartStore.Web/Administration/sitemap.config b/src/Presentation/SmartStore.Web/Administration/sitemap.config index 6a43d810d8..573d1227b0 100644 --- a/src/Presentation/SmartStore.Web/Administration/sitemap.config +++ b/src/Presentation/SmartStore.Web/Administration/sitemap.config @@ -138,7 +138,7 @@ - + From ecdf5ee3cb4c1b00b4b40dc93ee5c0efdd8ddece Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 3 Nov 2015 17:57:44 +0100 Subject: [PATCH 021/732] Minor changes --- .../Views/Order/AddShipment.cshtml | 124 ++++++++---------- .../Administration/Views/Order/Edit.cshtml | 14 +- .../Views/Order/ShipmentDetails.cshtml | 95 +++++++------- 3 files changed, 109 insertions(+), 124 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/AddShipment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/AddShipment.cshtml index 9003cade34..0deedb6ebd 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/AddShipment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/AddShipment.cshtml @@ -2,7 +2,6 @@ @using SmartStore.Core.Domain.Tax; @using SmartStore.Core.Domain.Catalog; @{ - //page title ViewBag.Title = string.Format(T("Admin.Orders.Shipments.AddNew.Title").Text, Model.OrderId); } @using (Html.BeginForm()) @@ -15,8 +14,9 @@ - + @Html.ValidationSummary(false) +
@@ -27,78 +27,70 @@
+ - +
- @T("Admin.Orders.Shipments.Products") +

@T("Admin.Orders.Shipments.Products")

+ -
+ + + + + + + + + + + + @foreach (var item in Model.Items) + { + + + + + + + + } + +
+ @T("Admin.Orders.Shipments.Products.ProductName") + + @T("Admin.Orders.Shipments.Products.SKU") + + @T("Admin.Orders.Shipments.Products.QtyOrdered") + + @T("Admin.Orders.Shipments.Products.QtyShipped") + + @T("Admin.Orders.Shipments.Products.QtyToShip") +
+
+ @Html.LabeledProductName(item.ProductId, item.ProductName, item.ProductTypeName, item.ProductTypeLabelHint) + @if (!String.IsNullOrEmpty(item.AttributeInfo)) + { +
+ @Html.Raw(item.AttributeInfo) + } +
+
+
+ @item.Sku +
+
+ @item.QuantityOrdered + + @item.QuantityInAllShipments + + +
- - - - - - - - - - - - - - - - - - @foreach (var item in Model.Items) - { - - - - - - - - } - -
- @T("Admin.Orders.Shipments.Products.ProductName") - - @T("Admin.Orders.Shipments.Products.SKU") - - @T("Admin.Orders.Shipments.Products.QtyOrdered") - - @T("Admin.Orders.Shipments.Products.QtyShipped") - - @T("Admin.Orders.Shipments.Products.QtyToShip") -
-
- @Html.LabeledProductName(item.ProductId, item.ProductName, item.ProductTypeName, item.ProductTypeLabelHint) - @if (!String.IsNullOrEmpty(item.AttributeInfo)) - { -
- @Html.Raw(item.AttributeInfo) - } -
-
-
- @item.Sku -
-
- @item.QuantityOrdered - - @item.QuantityInAllShipments - - -
- -
-
} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/Edit.cshtml index 020e9eaccc..305c73e02f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/Edit.cshtml @@ -1221,17 +1221,13 @@ }) .Columns(columns => { - columns.Bound(x => x.Id).Width(50); + columns.Bound(x => x.Id) + .Template(x => Html.ActionLink(x.Id.ToString(), "ShipmentDetails", "Order", new { id = x.Id }, new { })) + .ClientTemplate("\"><#= Id #>"); columns.Bound(x => x.TrackingNumber); columns.Bound(x => x.TotalWeight); columns.Bound(x => x.ShippedDate); columns.Bound(x => x.DeliveryDate); - columns.Bound(x => x.Id) - .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "ShipmentDetails", "Order", new { id = x.Id }, new { })) - .ClientTemplate("\">" + T("Admin.Common.View").Text + "") - .Width(50) - .Centered() - .HeaderTemplate("{0}".FormatWith(T("Admin.Common.View").Text)); }) .EnableCustomBinding(true)) @@ -1375,7 +1371,7 @@ - - - + + - + - - + @@ -176,10 +175,10 @@ - - + + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Tax.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Tax.cshtml index 150886779f..7f2e872994 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Tax.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Tax.cshtml @@ -1,7 +1,6 @@ @model TaxSettingsModel @using Telerik.Web.Mvc.UI; @{ - //page title ViewBag.Title = T("Admin.Configuration.Settings.Tax").Text; } @using (Html.BeginForm()) @@ -150,11 +149,6 @@ @Html.ValidationMessageFor(model => model.HideTaxInOrderSummary) - - - - - + @@ -197,21 +191,21 @@ @Html.ValidationMessageFor(model => model.TaxBasedOn) - + - - - + @@ -243,8 +237,8 @@ @Html.ValidationMessageFor(model => model.ShippingTaxClassId) - - + @@ -276,8 +270,8 @@ @Html.ValidationMessageFor(model => model.PaymentMethodAdditionalFeeTaxClassId) - - + @@ -336,6 +330,5 @@ @Html.ValidationMessageFor(model => model.VatRequired) -
+ @@ -1410,7 +1406,7 @@ @foreach (var item in Model.Items) { - From cd8fc023bc7596c1c44523607fa249dc101832f4 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 30 Nov 2015 11:28:46 +0100 Subject: [PATCH 130/732] FastProperty fails for getting plugin descriptor localized value --- .../SmartStore.Services/Localization/LocalizationExtentions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs index 3fb8b75d82..72f1524895 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs @@ -321,7 +321,7 @@ public static string GetLocalizedValue(this PluginDescriptor descriptor, ILocali if (String.IsNullOrEmpty(result) && returnDefaultValue) { - var fastProp = FastProperty.GetProperty(descriptor.GetType(), propertyName); + var fastProp = FastProperty.GetProperty(descriptor.GetType(), propertyName, PropertyCachingStrategy.EagerCached); if (fastProp != null) { result = fastProp.GetValue(descriptor) as string; From f2c05ea4fd88b9a1ec41032bae71045a6263c864 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 30 Nov 2015 11:35:48 +0100 Subject: [PATCH 131/732] FastProperty fails for getting unmapped properties through Web API --- .../SmartStore.Web.Framework/WebApi/WebApiEntityController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs index 3f48b374d2..fa41b10058 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs @@ -89,7 +89,7 @@ protected virtual internal HttpResponseMessage UnmappedGetProperty(ODataPath oda propertyName = propertySegment.PropertyName; if (propertyName.HasValue()) - prop = FastProperty.GetProperty(entity.GetType(), propertyName); + prop = FastProperty.GetProperty(entity.GetType(), propertyName, PropertyCachingStrategy.EagerCached); if (prop == null) return UnmappedGetProperty(entity, propertyName ?? ""); From 41dd9648560ff87db683363c91f78eeb98f9dddc Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 30 Nov 2015 12:19:13 +0100 Subject: [PATCH 132/732] FastProperty fails for property fulfillment through Web API Also fixes System.InvalidOperationException "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." --- .../DependencyRegistrar.cs | 32 ++++++++++++++++++- .../WebApi/WebApiEntityController.cs | 15 ++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 6b287477a3..e8cd2fdfc7 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -657,7 +657,13 @@ protected override void AttachToComponentRegistration(IComponentRegistry compone var repoProperty = FindRepositoryProperty(type, implementingType.GetGenericArguments()[0]); var serviceProperty = FindServiceProperty(type, implementingType.GetGenericArguments()[1]); - if (repoProperty != null || serviceProperty != null) + var countryServiceProperty = type.GetProperty("CountryService", typeof(ICountryService)); + var stateProvinceServiceProperty = type.GetProperty("StateProvinceService", typeof(IStateProvinceService)); + var languageServiceProperty = type.GetProperty("LanguageService", typeof(ILanguageService)); + var currencyServiceProperty = type.GetProperty("CurrencyService", typeof(ICurrencyService)); + + if (repoProperty != null || serviceProperty != null || + countryServiceProperty != null || stateProvinceServiceProperty != null || languageServiceProperty != null || currencyServiceProperty != null) { registration.Activated += (sender, e) => { @@ -672,6 +678,30 @@ protected override void AttachToComponentRegistration(IComponentRegistry compone var service = e.Context.Resolve(serviceProperty.PropertyType); serviceProperty.SetValue(e.Instance, service, null); } + + if (countryServiceProperty != null) + { + var service = e.Context.Resolve(typeof(ICountryService)); + countryServiceProperty.SetValue(e.Instance, service, null); + } + + if (stateProvinceServiceProperty != null) + { + var service = e.Context.Resolve(typeof(IStateProvinceService)); + stateProvinceServiceProperty.SetValue(e.Instance, service, null); + } + + if (languageServiceProperty != null) + { + var service = e.Context.Resolve(typeof(ILanguageService)); + languageServiceProperty.SetValue(e.Instance, service, null); + } + + if (currencyServiceProperty != null) + { + var service = e.Context.Resolve(typeof(ICurrencyService)); + currencyServiceProperty.SetValue(e.Instance, service, null); + } }; } } diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs index fa41b10058..b789c1785c 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs @@ -165,6 +165,11 @@ public virtual TService Service set; } + public virtual ICountryService CountryService { get; set; } + public virtual IStateProvinceService StateProvinceService { get; set; } + public virtual ILanguageService LanguageService { get; set; } + public virtual ICurrencyService CurrencyService { get; set; } + public override IQueryable Get() { if (!ModelState.IsValid) @@ -430,19 +435,19 @@ protected internal virtual object FulfillPropertyOn(TEntity entity, string prope { if (propertyName.IsCaseInsensitiveEqual("Country")) { - return EngineContext.Current.Resolve().GetCountryByTwoOrThreeLetterIsoCode(queryValue); + return CountryService.GetCountryByTwoOrThreeLetterIsoCode(queryValue); } else if (propertyName.IsCaseInsensitiveEqual("StateProvince")) { - return EngineContext.Current.Resolve().GetStateProvinceByAbbreviation(queryValue); + return StateProvinceService.GetStateProvinceByAbbreviation(queryValue); } else if (propertyName.IsCaseInsensitiveEqual("Language")) { - return EngineContext.Current.Resolve().GetLanguageByCulture(queryValue); + return LanguageService.GetLanguageByCulture(queryValue); } else if (propertyName.IsCaseInsensitiveEqual("Currency")) { - return EngineContext.Current.Resolve().GetCurrencyByCode(queryValue); + return CurrencyService.GetCurrencyByCode(queryValue); } return null; } @@ -466,7 +471,7 @@ protected internal virtual TEntity FulfillPropertiesOn(TEntity entity) if (propertyName.HasValue() && queryValue.HasValue()) { - var prop = FastProperty.GetProperty(entity.GetType(), propertyName); + var prop = FastProperty.GetProperty(entity.GetType(), propertyName, PropertyCachingStrategy.EagerCached); if (prop != null) { var propertyValue = prop.GetValue(entity); From e3ea5d735b6db16ddd87f0c60f34bb08fe01ddc5 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 30 Nov 2015 16:24:03 +0100 Subject: [PATCH 133/732] Hide link to a topic page if it is limited to stores (part 1) --- changelog.md | 1 + .../201509150931528_ExportFramework2.cs | 5 + .../Topics/TopicService.cs | 112 +++++++++--------- .../Models/Topics/TopicModel.cs | 7 +- .../Administration/Views/Topic/List.cshtml | 69 ++++++----- .../Controllers/CommonController.cs | 83 +++++++------ .../Models/Common/FooterModel.cs | 10 +- .../SmartStore.Web/Models/Common/MenuModel.cs | 4 +- .../SmartStore.Web/Views/Common/Footer.cshtml | 49 +++++--- .../SmartStore.Web/Views/Common/Menu.cshtml | 10 +- 10 files changed, 189 insertions(+), 161 deletions(-) diff --git a/changelog.md b/changelog.md index 30d578f594..0d463568f9 100644 --- a/changelog.md +++ b/changelog.md @@ -74,6 +74,7 @@ * #755 Some methods still loading all products in one go * #796 Selected specification in product filter mask is displayed with default language (not localized) * #805 Product filter is reset if 'product sorting' or 'view mode' or 'amount of displayed products per page' is changed +* Hide link to a topic page if it is limited to stores ## SmartStore.NET 2.2.2 diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index d3fe755327..8c5d34109f 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -253,6 +253,11 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Configuration.Themes.NoConfigurationRequired", "Theme requires no configuration", "Theme bentigt keine Konfiguration"); + + + builder.AddOrUpdate("Tax.LegalInfoFooter2", + "* All prices {0}, plus shipping", + "* Alle Preise {0}, zzgl. Versandkosten"); } } } diff --git a/src/Libraries/SmartStore.Services/Topics/TopicService.cs b/src/Libraries/SmartStore.Services/Topics/TopicService.cs index daaa424a3c..d9ffb1a6af 100644 --- a/src/Libraries/SmartStore.Services/Topics/TopicService.cs +++ b/src/Libraries/SmartStore.Services/Topics/TopicService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Domain.Topics; @@ -13,23 +14,29 @@ namespace SmartStore.Services.Topics /// public partial class TopicService : ITopicService { - #region Fields + private const string TOPICS_ALL_KEY = "SmartStore.topics.all-{0}"; + private const string TOPICS_PATTERN_KEY = "SmartStore.topics."; - private readonly IRepository _topicRepository; + #region Fields + + private readonly IRepository _topicRepository; private readonly IRepository _storeMappingRepository; private readonly IEventPublisher _eventPublisher; + private readonly ICacheManager _cacheManager; - #endregion + #endregion - #region Ctor + #region Ctor - public TopicService(IRepository topicRepository, + public TopicService(IRepository topicRepository, IRepository storeMappingRepository, - IEventPublisher eventPublisher) + IEventPublisher eventPublisher, + ICacheManager cacheManager) { _topicRepository = topicRepository; _storeMappingRepository = storeMappingRepository; _eventPublisher = eventPublisher; + _cacheManager = cacheManager; this.QuerySettings = DbQuerySettings.Default; } @@ -51,8 +58,10 @@ public virtual void DeleteTopic(Topic topic) _topicRepository.Delete(topic); - //event notification - _eventPublisher.EntityDeleted(topic); + _cacheManager.RemoveByPattern(TOPICS_PATTERN_KEY); + + //event notification + _eventPublisher.EntityDeleted(topic); } /// @@ -79,29 +88,13 @@ public virtual Topic GetTopicBySystemName(string systemName, int storeId) if (String.IsNullOrEmpty(systemName)) return null; - var query = _topicRepository.Table; - query = query.Where(t => t.SystemName == systemName); - query = query.OrderBy(t => t.Id); + var allTopics = GetAllTopics(storeId); - //Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = from t in query - join sm in _storeMappingRepository.Table - on new { c1 = t.Id, c2 = "Topic" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into t_sm - from sm in t_sm.DefaultIfEmpty() - where !t.LimitedToStores || storeId == sm.StoreId - select t; - - //only distinct items (group by ID) - query = from t in query - group t by t.Id into tGroup - orderby tGroup.Key - select tGroup.FirstOrDefault(); - query = query.OrderBy(t => t.Id); - } - - return query.FirstOrDefault(); + var topic = allTopics + .OrderBy(x => x.Id) + .FirstOrDefault(x => x.SystemName.IsCaseInsensitiveEqual(systemName)); + + return topic; } /// @@ -111,28 +104,33 @@ orderby tGroup.Key /// Topics public virtual IList GetAllTopics(int storeId) { - var query = _topicRepository.Table; - query = query.OrderBy(t => t.Priority).ThenBy(t => t.SystemName); - - //Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + var result = _cacheManager.Get(TOPICS_ALL_KEY.FormatInvariant(storeId), () => { - query = from t in query - join sm in _storeMappingRepository.Table - on new { c1 = t.Id, c2 = "Topic" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into t_sm - from sm in t_sm.DefaultIfEmpty() - where !t.LimitedToStores || storeId == sm.StoreId - select t; - - //only distinct items (group by ID) - query = from t in query - group t by t.Id into tGroup - orderby tGroup.Key - select tGroup.FirstOrDefault(); - query = query.OrderBy(t => t.SystemName); - } - - return query.ToList(); + var query = _topicRepository.Table; + + //Store mapping + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from t in query + join sm in _storeMappingRepository.Table + on new { c1 = t.Id, c2 = "Topic" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into t_sm + from sm in t_sm.DefaultIfEmpty() + where !t.LimitedToStores || storeId == sm.StoreId + select t; + + //only distinct items (group by ID) + query = from t in query + group t by t.Id into tGroup + orderby tGroup.Key + select tGroup.FirstOrDefault(); + } + + query = query.OrderBy(t => t.Priority).ThenBy(t => t.SystemName); + + return query.ToList(); + }); + + return result; } /// @@ -146,8 +144,10 @@ public virtual void InsertTopic(Topic topic) _topicRepository.Insert(topic); - //event notification - _eventPublisher.EntityInserted(topic); + _cacheManager.RemoveByPattern(TOPICS_PATTERN_KEY); + + //event notification + _eventPublisher.EntityInserted(topic); } /// @@ -161,8 +161,10 @@ public virtual void UpdateTopic(Topic topic) _topicRepository.Update(topic); - //event notification - _eventPublisher.EntityUpdated(topic); + _cacheManager.RemoveByPattern(TOPICS_PATTERN_KEY); + + //event notification + _eventPublisher.EntityUpdated(topic); } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Topics/TopicModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Topics/TopicModel.cs index 43de02db29..516f7d402f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Topics/TopicModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Topics/TopicModel.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using FluentValidation.Attributes; @@ -11,7 +10,7 @@ namespace SmartStore.Admin.Models.Topics { - [Validator(typeof(TopicValidator))] + [Validator(typeof(TopicValidator))] public class TopicModel : TabbableModel, ILocalizedModel { #region widget zone names @@ -47,9 +46,9 @@ public TopicModel() AvailableTitleTags.Add(new SelectListItem { Text = "span", Value = "span" }); } - //Store mapping [SmartResourceDisplayName("Admin.Common.Store.LimitedTo")] public bool LimitedToStores { get; set; } + [SmartResourceDisplayName("Admin.Common.Store.AvailableFor")] public List AvailableStores { get; set; } public int[] SelectedStoreIds { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml index 00f7b28cee..08de2f50ab 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml @@ -1,7 +1,6 @@ @model TopicListModel @using Telerik.Web.Mvc.UI @{ - //page title ViewBag.Title = T("Admin.ContentManagement.Topics").Text; }
@@ -16,7 +15,8 @@
-
+
@Html.LabeledProductName(item.ProductId, item.ProductName, item.ProductTypeName, item.ProductTypeLabelHint) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml index e8a3fea84a..800e47eee5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/ShipmentDetails.cshtml @@ -3,12 +3,10 @@ @using SmartStore.Core.Domain.Tax; @using SmartStore.Core.Domain.Catalog; @{ - //page title ViewBag.Title = T("Admin.Orders.Shipments.ViewDetails").Text; } @using (Html.BeginForm()) { -
@T("Admin.Orders.Shipments.ViewDetails") - @Model.Id @Html.ActionLink("(" + T("Admin.Orders.Shipments.BackToOrder") + ")", "Edit", new { id = Model.OrderId }) @@ -23,8 +21,10 @@ }
-
+
+ @Html.ValidationSummary(false) + + + + + + + + + + + + + + - + @@ -40,7 +41,7 @@ @foreach (var profile in Model) { - + @@ -51,6 +52,9 @@
@profile.SystemName
} + -
@@ -82,54 +82,51 @@ -
+ -
- - - - - - - - - - - @foreach (var item in Model.Items) - { - - + + @if (!String.IsNullOrEmpty(item.AttributeInfo)) + { +
+ @Html.Raw(item.AttributeInfo) + } + + + + + + } + +
- @T("Admin.Orders.Shipments.Products.ProductName") - - @T("Admin.Orders.Shipments.Products.SKU") - - @T("Admin.Orders.Shipments.Products.QtyShipped") -
-
+ + + + + + + + + + @foreach (var item in Model.Items) + { + + - - - - } - -
+ @T("Admin.Orders.Shipments.Products.ProductName") + + @T("Admin.Orders.Shipments.Products.SKU") + + @T("Admin.Orders.Shipments.Products.QtyShipped") +
+
@Html.LabeledProductName(item.ProductId, item.ProductName, item.ProductTypeName, item.ProductTypeLabelHint) - - @if (!String.IsNullOrEmpty(item.AttributeInfo)) - { -
- @Html.Raw(item.AttributeInfo) - } -
-
-
- @item.Sku -
-
- @item.QuantityInThisShipment -
- -
-
+
+ @item.Sku +
+
+ @item.QuantityInThisShipment +
+ +
} From 00cc912960badb9db6ac7be9ff5a5aaa9079eb92 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 3 Nov 2015 21:12:52 +0100 Subject: [PATCH 022/732] SEO Names: New column with the number of slugs per entity (click on the value filters them) --- .../201509150931528_ExportFramework2.cs | 10 +++++-- .../Seo/IUrlRecordService.cs | 10 ++++++- .../Seo/UrlRecordService.cs | 26 +++++++++++++++- .../Controllers/UrlRecordController.cs | 30 +++++++++++++++---- .../Models/UrlRecord/UrlRecordListModel.cs | 7 +++-- .../Models/UrlRecord/UrlRecordModel.cs | 9 ++++-- .../Views/UrlRecord/List.cshtml | 29 +++++++++++++++--- .../Views/UrlRecord/_CreateOrUpdate.cshtml | 8 +++++ 8 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 311a491a0e..6de44115cc 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -75,8 +75,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Legt die ID der zugehrigen Entitt fest."); builder.AddOrUpdate("Admin.System.SeNames.EntityName", - "Entity name", - "Name der Entitt", + "Entity", + "Entitt", "Specifies the name of the associated entity.", "Legt den Namen der zugehrigen Entitt fest."); @@ -92,6 +92,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Specifies the language of the SEO name.", "Legt die Sprache des SEO Namens fest."); + builder.AddOrUpdate("Admin.System.SeNames.SlugsPerEntity", + "Names per entity", + "Namen pro Entitt", + "The number of SEO names per entity.", + "Die Anzahl der SEO Namen pro Entitt."); + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", diff --git a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs index 0981e33384..c2e14366ff 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -57,9 +57,10 @@ public partial interface IUrlRecordService /// Page size /// Slug /// Entity name + /// Entity identifier /// Whether to load only active records /// Customer collection - IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, bool? isActive); + IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, bool? isActive); /// /// Gets all URL records for the specified entity @@ -99,5 +100,12 @@ public partial interface IUrlRecordService /// Name of a property /// Url record UrlRecord SaveSlug(T entity, Expression> nameProperty) where T : BaseEntity, ISlugSupported; + + /// + /// Get number of slugs per entity + /// + /// URL record identifier + /// Dictionary of slugs per entity count + Dictionary CountSlugsPerEntity(int[] urlRecordIds); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index 9c360bb699..78ec3ed5c4 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -97,7 +97,7 @@ public virtual void UpdateUrlRecord(UrlRecord urlRecord) _cacheManager.RemoveByPattern(URLRECORD_PATTERN_KEY); } - public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, bool? isActive) + public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, bool? isActive) { var query = _urlRecordRepository.Table; @@ -107,6 +107,9 @@ public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSiz if (entityName.HasValue()) query = query.Where(x => x.EntityName == entityName); + if (entityId.HasValue) + query = query.Where(x => x.EntityId == entityId.Value); + if (isActive.HasValue) query = query.Where(x => x.IsActive == isActive.Value); @@ -324,6 +327,27 @@ private string GenerateKey(int entityId, string entityName, int languageId) return "{0}.{1}.{2}".FormatInvariant(entityId, entityName, languageId); } + public virtual Dictionary CountSlugsPerEntity(int[] urlRecordIds) + { + if (urlRecordIds == null || urlRecordIds.Length == 0) + return new Dictionary(); + + var query = + from x in _urlRecordRepository.TableUntracked + where urlRecordIds.Contains(x.Id) + select new + { + Id = x.Id, + Count = _urlRecordRepository.TableUntracked.Where(y => y.EntityName == x.EntityName && y.EntityId == x.EntityId).Count() + }; + + var result = query + .ToList() + .ToDictionary(x => x.Id, x => x.Count); + + return result; + } + #endregion } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index de577163fd..309f812304 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -49,21 +49,34 @@ private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord) { model.Id = urlRecord.Id; model.Slug = urlRecord.Slug; - model.EntityId = urlRecord.EntityId; model.EntityName = urlRecord.EntityName; + model.EntityId = urlRecord.EntityId; model.IsActive = urlRecord.IsActive; model.LanguageId = urlRecord.LanguageId; + + try + { + var slugCount = _urlRecordService.CountSlugsPerEntity(new int[] { urlRecord.Id }); + + model.SlugsPerEntity = (slugCount.ContainsKey(urlRecord.Id) ? slugCount[urlRecord.Id] : 0); + } + catch (Exception exc) + { + NotifyError(exc); + } } } - public ActionResult List() + public ActionResult List(string entityName, int? entityId) { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) return AccessDeniedView(); var model = new UrlRecordListModel { - GridPageSize = _adminAreaSettings.GridPageSize + GridPageSize = _adminAreaSettings.GridPageSize, + EntityName = entityName, + EntityId = entityId }; return View(model); @@ -78,7 +91,9 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) var allLanguages = _languageService.GetAllLanguages(true); var defaultLanguageName = T("Admin.System.SeNames.Language.Standard"); - var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, model.SeName, model.EntityName, model.IsActive); + var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, model.SeName, model.EntityName, model.EntityId, model.IsActive); + + var slugsPerEntity = _urlRecordService.CountSlugsPerEntity(urlRecords.Select(x => x.Id).Distinct().ToArray()); var gridModel = new GridModel { @@ -96,15 +111,18 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) languageName = (language != null ? language.Name : "".NaIfEmpty()); } - return new UrlRecordModel + var urlRecordModel = new UrlRecordModel { Id = x.Id, Slug = x.Slug, - EntityId = x.EntityId, EntityName = x.EntityName, + EntityId = x.EntityId, IsActive = x.IsActive, Language = languageName, + SlugsPerEntity = (slugsPerEntity.ContainsKey(x.Id) ? slugsPerEntity[x.Id] : 0) }; + + return urlRecordModel; }), Total = urlRecords.TotalCount }; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs index 3b07866768..6fd819787b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs @@ -12,10 +12,13 @@ public partial class UrlRecordListModel : ModelBase [AllowHtml] public string SeName { get; set; } - [SmartResourceDisplayName("Admin.Common.Entity")] + [SmartResourceDisplayName("Admin.System.SeNames.EntityName")] public string EntityName { get; set; } - [SmartResourceDisplayName("Common.IsActive")] + [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] + public int? EntityId { get; set; } + + [SmartResourceDisplayName("Admin.System.SeNames.IsActive")] public bool? IsActive { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs index b3db7e2089..50569db7d0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs @@ -11,12 +11,12 @@ public partial class UrlRecordModel : EntityModelBase [AllowHtml] public string Slug { get; set; } - [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] - public int EntityId { get; set; } - [SmartResourceDisplayName("Admin.System.SeNames.EntityName")] public string EntityName { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] + public int EntityId { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.IsActive")] public bool IsActive { get; set; } @@ -26,5 +26,8 @@ public partial class UrlRecordModel : EntityModelBase [SmartResourceDisplayName("Admin.System.SeNames.Language")] public string Language { get; set; } + + [SmartResourceDisplayName("Admin.System.SeNames.SlugsPerEntity")] + public int SlugsPerEntity { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml index 8df5062147..648f7bfef5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml @@ -36,6 +36,14 @@ @Html.EditorFor(model => Model.EntityName)
+ @Html.SmartLabelFor(model => model.EntityId) + + @Html.EditorFor(model => Model.EntityId) +
@Html.SmartLabelFor(model => model.IsActive) @@ -76,11 +84,15 @@ .Centered(); columns.Bound(x => x.Slug) .ClientTemplate("<#= Slug #>"); - columns.Bound(x => x.EntityId) - .Width(140) - .Centered(); + columns.Bound(x => x.SlugsPerEntity) + .Centered() + .Width(180) + .ClientTemplate("<#= SlugsPerEntity #>"); columns.Bound(x => x.EntityName) .Width(200); + columns.Bound(x => x.EntityId) + .Width(140) + .Centered(); columns.Bound(x => x.IsActive) .Template(item => @Html.SymbolForBool(item.IsActive)) .ClientTemplate(@Html.SymbolForBool("IsActive")) @@ -191,13 +203,13 @@ var searchModel = { SeName: $('#@Html.FieldIdFor(model => model.SeName)').val(), EntityName: $('#@Html.FieldIdFor(model => model.EntityName)').val(), + EntityId: $('#@Html.FieldIdFor(model => model.EntityId)').val(), IsActive: $('#@Html.FieldIdFor(model => model.IsActive)').val() }; e.data = searchModel; } function onDataBound(e) { - $('#urlrecord-grid input[type=checkbox][id!=mastercheckbox]').each(function () { var currentId = $(this).val(); var checked = jQuery.inArray(currentId, selectedIds); @@ -213,4 +225,13 @@ var numChkBoxesChecked = $('#urlrecord-grid input[type=checkbox][checked][id!=mastercheckbox]').length; $('#mastercheckbox').attr('checked', numChkBoxes == numChkBoxesChecked && numChkBoxes > 0); } + + function filterPerEntity(entityName, entityId) { + $('#@Html.FieldIdFor(model => model.EntityName)').val(entityName); + $('#@Html.FieldIdFor(model => model.EntityId)').val(entityId).blur(); + + var grid = $('#urlrecord-grid').data('tGrid'); + grid.currentPage = 1; + grid.ajaxRequest(); + } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml index 58798845b4..3d147687f9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml @@ -13,6 +13,14 @@ @Html.DisplayFor(model => model.Id)
+ @Html.SmartLabelFor(model => model.SlugsPerEntity) + + @Model.SlugsPerEntity +
@Html.SmartLabelFor(model => model.IsActive) From f0672cdfe53e938200d6d9e786e7316246571aa9 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 4 Nov 2015 10:59:19 +0100 Subject: [PATCH 023/732] SEO Names: Do not allow more than one active slug per entity and language --- .../201509150931528_ExportFramework2.cs | 4 ++++ .../Seo/IUrlRecordService.cs | 3 ++- .../Seo/UrlRecordService.cs | 5 ++++- .../Controllers/PaymentController.cs | 2 +- .../Controllers/UrlRecordController.cs | 20 ++++++++++++++++++- .../Models/UrlRecord/UrlRecordListModel.cs | 7 ++++++- .../Views/UrlRecord/List.cshtml | 11 +++++++++- .../Views/UrlRecord/_CreateOrUpdate.cshtml | 1 + 8 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 6de44115cc..cad4436211 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -98,6 +98,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "The number of SEO names per entity.", "Die Anzahl der SEO Namen pro Entitt."); + builder.AddOrUpdate("Admin.System.SeNames.ActiveSlugAlreadyExist", + "Only one active SEO name should be set per language.", + "Pro Sprache darf nur ein aktiver SEO Name festgelegt werden."); + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", diff --git a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs index c2e14366ff..16eefa19f8 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -59,8 +59,9 @@ public partial interface IUrlRecordService /// Entity name /// Entity identifier /// Whether to load only active records + /// Language identifier /// Customer collection - IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, bool? isActive); + IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, int? languageId, bool? isActive); /// /// Gets all URL records for the specified entity diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index 78ec3ed5c4..72c3bdd3fe 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -97,7 +97,7 @@ public virtual void UpdateUrlRecord(UrlRecord urlRecord) _cacheManager.RemoveByPattern(URLRECORD_PATTERN_KEY); } - public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, bool? isActive) + public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, int? languageId, bool? isActive) { var query = _urlRecordRepository.Table; @@ -113,6 +113,9 @@ public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSiz if (isActive.HasValue) query = query.Where(x => x.IsActive == isActive.Value); + if (languageId.HasValue) + query = query.Where(x => x.LanguageId == languageId); + query = query.OrderBy(x => x.Slug); var urlRecords = new PagedList(query, pageIndex, pageSize); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs index e2708847b0..4fba64859a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/PaymentController.cs @@ -207,7 +207,7 @@ public ActionResult Edit(string systemName) return View(model); } - [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")] + [HttpPost, ValidateInput(false), ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")] public ActionResult Edit(string systemName, bool continueEditing, PaymentMethodEditModel model, FormCollection form) { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManagePaymentMethods)) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index 309f812304..10bdf9a841 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -79,6 +79,14 @@ public ActionResult List(string entityName, int? entityId) EntityId = entityId }; + var allLanguages = _languageService.GetAllLanguages(true); + + model.AvailableLanguages = allLanguages + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + + model.AvailableLanguages.Insert(0, new SelectListItem { Text = T("Admin.System.SeNames.Language.Standard"), Value = "0" }); + return View(model); } @@ -91,7 +99,8 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) var allLanguages = _languageService.GetAllLanguages(true); var defaultLanguageName = T("Admin.System.SeNames.Language.Standard"); - var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, model.SeName, model.EntityName, model.EntityId, model.IsActive); + var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, + model.SeName, model.EntityName, model.EntityId, model.LanguageId, model.IsActive); var slugsPerEntity = _urlRecordService.CountSlugsPerEntity(urlRecords.Select(x => x.Id).Distinct().ToArray()); @@ -158,6 +167,15 @@ public ActionResult Edit(UrlRecordModel model, bool continueEditing) if (urlRecord == null) return RedirectToAction("List"); + if (!urlRecord.IsActive && model.IsActive) + { + var urlRecords = _urlRecordService.GetAllUrlRecords(0, int.MaxValue, null, model.EntityName, model.EntityId, model.LanguageId, true); + if (urlRecords.Count > 0) + { + ModelState.AddModelError("", T("Admin.System.SeNames.ActiveSlugAlreadyExist")); + } + } + if (ModelState.IsValid) { urlRecord.Slug = model.Slug; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs index 6fd819787b..414f8f32eb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordListModel.cs @@ -1,4 +1,5 @@ -using System.Web.Mvc; +using System.Collections.Generic; +using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Mvc; @@ -20,5 +21,9 @@ public partial class UrlRecordListModel : ModelBase [SmartResourceDisplayName("Admin.System.SeNames.IsActive")] public bool? IsActive { get; set; } + + [SmartResourceDisplayName("Admin.System.SeNames.Language")] + public int? LanguageId { get; set; } + public List AvailableLanguages { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml index 648f7bfef5..196aedd999 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml @@ -52,6 +52,14 @@ @Html.EditorFor(model => Model.IsActive)
+ @Html.SmartLabelFor(x => x.LanguageId) + + @Html.DropDownListFor(x => x.LanguageId, Model.AvailableLanguages, T("Common.Unspecified")) +
  @@ -204,7 +212,8 @@ SeName: $('#@Html.FieldIdFor(model => model.SeName)').val(), EntityName: $('#@Html.FieldIdFor(model => model.EntityName)').val(), EntityId: $('#@Html.FieldIdFor(model => model.EntityId)').val(), - IsActive: $('#@Html.FieldIdFor(model => model.IsActive)').val() + IsActive: $('#@Html.FieldIdFor(model => model.IsActive)').val(), + LanguageId: $('#@Html.FieldIdFor(model => model.LanguageId)').val() }; e.data = searchModel; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml index 3d147687f9..24bf8ab910 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml @@ -2,6 +2,7 @@ @using SmartStore.Admin.Models.UrlRecord; @using Telerik.Web.Mvc.UI; +@Html.ValidationSummary(true) @Html.HiddenFor(model => model.Id) From 339bd3221512e0b7d7f1921cf68e5d7fe676991e Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 4 Nov 2015 12:14:54 +0100 Subject: [PATCH 024/732] SEO Names: Link to filter all slugs per entity on entity's edit page --- .../Migrations/201509150931528_ExportFramework2.cs | 1 + .../SmartStore.Services/Seo/IUrlRecordService.cs | 8 ++++++++ .../SmartStore.Services/Seo/UrlRecordService.cs | 9 +++++++++ .../Controllers/UrlRecordController.cs | 14 ++++++++++++++ .../Administration/SmartStore.Admin.csproj | 1 + .../Views/Blog/_CreateOrUpdate.cshtml | 4 ++++ .../Views/Category/_CreateOrUpdate.cshtml | 4 ++++ .../Views/Forum/_CreateOrUpdateForum.cshtml | 4 ++++ .../Views/Forum/_CreateOrUpdateForumGroup.cshtml | 4 ++++ .../Views/Manufacturer/_CreateOrUpdate.cshtml | 4 ++++ .../Views/News/_CreateOrUpdate.cshtml | 4 ++++ .../Views/Product/_CreateOrUpdate.SEO.cshtml | 4 ++++ .../Views/UrlRecord/NamesPerEntity.cshtml | 6 ++++++ 13 files changed, 67 insertions(+) create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/NamesPerEntity.cshtml diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index cad4436211..374cf79ba1 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -38,6 +38,7 @@ public void Seed(SmartObjectContext context) public void MigrateLocaleResources(LocaleResourcesBuilder builder) { builder.AddOrUpdate("Common.Example", "Example", "Beispiel"); + builder.AddOrUpdate("Common.ShowAll", "Show all", "Alle anzeigen"); builder.AddOrUpdate("Admin.Common.Selected", "Selected", "Ausgewhlte"); builder.AddOrUpdate("Admin.Common.Entity", "Entity", "Entitt"); diff --git a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs index 16eefa19f8..d3bfa5a04d 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -108,5 +108,13 @@ public partial interface IUrlRecordService /// URL record identifier /// Dictionary of slugs per entity count Dictionary CountSlugsPerEntity(int[] urlRecordIds); + + /// + /// Get number of slugs per entity + /// + /// Entity name + /// Entity identifier + /// Number of slugs per entity + int CountSlugsPerEntity(string entityName, int entityId); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index 72c3bdd3fe..337cf5bd98 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -351,6 +351,15 @@ where urlRecordIds.Contains(x.Id) return result; } + public virtual int CountSlugsPerEntity(string entityName, int entityId) + { + var count = _urlRecordRepository.Table + .Where(x => x.EntityName == entityName && x.EntityId == entityId) + .Count(); + + return count; + } + #endregion } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index 10bdf9a841..2bf6480ff0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -239,5 +239,19 @@ public ActionResult DeleteSelected(ICollection selectedIds) return Json(new { Result = true }); } + + [ChildActionOnly] + public ActionResult NamesPerEntity(string entityName, int entityId) + { + if (entityName.IsEmpty() || entityId == 0) + return new EmptyResult(); + + var count = _urlRecordService.CountSlugsPerEntity(entityName, entityId); + + ViewBag.CountSlugsPerEntity = count; + ViewBag.UrlRecordListUrl = Url.Action("List", "UrlRecord", new { entityName = entityName, entityId = entityId }); + + return View(); + } } } \ 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 564a1aa36f..9b3e27db4d 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -538,6 +538,7 @@ + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Blog/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Blog/_CreateOrUpdate.cshtml index 71d80f25b4..effce8793b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Blog/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Blog/_CreateOrUpdate.cshtml @@ -126,6 +126,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml index 6899d373c1..9f98f0d5a0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml @@ -294,6 +294,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForum.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForum.cshtml index 2d27a88242..08b4cea0c7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForum.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForum.cshtml @@ -56,6 +56,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForumGroup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForumGroup.cshtml index 611d9cef72..8aaf21cf5e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForumGroup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Forum/_CreateOrUpdateForumGroup.cshtml @@ -68,6 +68,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/_CreateOrUpdate.cshtml index 25db45dfe1..44385835cb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/_CreateOrUpdate.cshtml @@ -215,6 +215,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/News/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/News/_CreateOrUpdate.cshtml index ad61050588..84562a94b1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/News/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/News/_CreateOrUpdate.cshtml @@ -128,6 +128,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/_CreateOrUpdate.SEO.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/_CreateOrUpdate.SEO.cshtml index 329d12360b..985ff1f3bb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/_CreateOrUpdate.SEO.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/_CreateOrUpdate.SEO.cshtml @@ -79,6 +79,10 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/NamesPerEntity.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/NamesPerEntity.cshtml new file mode 100644 index 0000000000..14e9654c01 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/NamesPerEntity.cshtml @@ -0,0 +1,6 @@ +@{ + Layout = null; +} + +  @T("Common.ShowAll") (@ViewBag.CountSlugsPerEntity) + \ No newline at end of file From 9278a0cefb33bc6ac264424d2e6db0291bce7b51 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 4 Nov 2015 14:05:17 +0100 Subject: [PATCH 025/732] SEO Names: Link from URL record edit page to entity's edit page --- changelog.md | 1 + .../Controllers/UrlRecordController.cs | 53 +++++++++++-------- .../Models/UrlRecord/UrlRecordModel.cs | 3 ++ .../Views/UrlRecord/Edit.cshtml | 3 ++ .../Views/UrlRecord/List.cshtml | 3 +- .../Views/UrlRecord/_CreateOrUpdate.cshtml | 21 +++----- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/changelog.md b/changelog.md index fb272219ce..7a5df6a93e 100644 --- a/changelog.md +++ b/changelog.md @@ -55,6 +55,7 @@ * #767 Remove assignments to a grouped product if the grouped product is deleted * #773 Reduce number of guest records created by search engine requests * #791 Preselected attributes or attribute combinations should always be appended as querystring to product page links +* Simplified managing of SEO names ### Bugfixes * #523 Redirecting to payment provider performed by core instead of plugin diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index 2bf6480ff0..3d82fa456d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -35,15 +35,18 @@ public UrlRecordController( _languageService = languageService; } - private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord) + private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord, bool forList = false) { - var allLanguages = _languageService.GetAllLanguages(true); + if (!forList) + { + var allLanguages = _languageService.GetAllLanguages(true); - model.AvailableLanguages = allLanguages - .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) - .ToList(); + model.AvailableLanguages = allLanguages + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); - model.AvailableLanguages.Insert(0, new SelectListItem { Text = T("Admin.System.SeNames.Language.Standard"), Value = "0" }); + model.AvailableLanguages.Insert(0, new SelectListItem { Text = T("Admin.System.SeNames.Language.Standard"), Value = "0" }); + } if (urlRecord != null) { @@ -54,15 +57,25 @@ private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord) model.IsActive = urlRecord.IsActive; model.LanguageId = urlRecord.LanguageId; - try + if (urlRecord.EntityName.IsCaseInsensitiveEqual("BlogPost")) { - var slugCount = _urlRecordService.CountSlugsPerEntity(new int[] { urlRecord.Id }); - - model.SlugsPerEntity = (slugCount.ContainsKey(urlRecord.Id) ? slugCount[urlRecord.Id] : 0); + model.EntityUrl = Url.Action("Edit", "Blog", new { id = urlRecord.EntityId }); } - catch (Exception exc) + else if (urlRecord.EntityName.IsCaseInsensitiveEqual("Forum")) { - NotifyError(exc); + model.EntityUrl = Url.Action("EditForum", "Forum", new { id = urlRecord.EntityId }); + } + else if (urlRecord.EntityName.IsCaseInsensitiveEqual("ForumGroup")) + { + model.EntityUrl = Url.Action("EditForumGroup", "Forum", new { id = urlRecord.EntityId }); + } + else if (urlRecord.EntityName.IsCaseInsensitiveEqual("NewsItem")) + { + model.EntityUrl = Url.Action("Edit", "News", new { id = urlRecord.EntityId }); + } + else + { + model.EntityUrl = Url.Action("Edit", urlRecord.EntityName, new { id = urlRecord.EntityId }); } } } @@ -120,16 +133,11 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) languageName = (language != null ? language.Name : "".NaIfEmpty()); } - var urlRecordModel = new UrlRecordModel - { - Id = x.Id, - Slug = x.Slug, - EntityName = x.EntityName, - EntityId = x.EntityId, - IsActive = x.IsActive, - Language = languageName, - SlugsPerEntity = (slugsPerEntity.ContainsKey(x.Id) ? slugsPerEntity[x.Id] : 0) - }; + var urlRecordModel = new UrlRecordModel(); + PrepareUrlRecordModel(urlRecordModel, x, true); + + urlRecordModel.Language = languageName; + urlRecordModel.SlugsPerEntity = (slugsPerEntity.ContainsKey(x.Id) ? slugsPerEntity[x.Id] : 0); return urlRecordModel; }), @@ -179,7 +187,6 @@ public ActionResult Edit(UrlRecordModel model, bool continueEditing) if (ModelState.IsValid) { urlRecord.Slug = model.Slug; - urlRecord.EntityId = model.EntityId; urlRecord.EntityName = model.EntityName; urlRecord.IsActive = model.IsActive; urlRecord.LanguageId = model.LanguageId; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs index 50569db7d0..a5996c6bbc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs @@ -17,6 +17,9 @@ public partial class UrlRecordModel : EntityModelBase [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] public int EntityId { get; set; } + [SmartResourceDisplayName("Admin.Common.Entity")] + public string EntityUrl { get; set; } + [SmartResourceDisplayName("Admin.System.SeNames.IsActive")] public bool IsActive { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml index ea6dfc9cdf..4914d5c1ea 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/Edit.cshtml @@ -11,6 +11,9 @@ @title - @Model.Slug @Html.ActionLink("(" + T("Admin.Common.BackToList") + ")", "List")
+ +  @T("Admin.Common.Entity") + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml index 196aedd999..fa2a560a7d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/List.cshtml @@ -100,7 +100,8 @@ .Width(200); columns.Bound(x => x.EntityId) .Width(140) - .Centered(); + .Centered() + .ClientTemplate("<#= EntityId #>"); columns.Bound(x => x.IsActive) .Template(item => @Html.SymbolForBool(item.IsActive)) .ClientTemplate(@Html.SymbolForBool("IsActive")) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml index 24bf8ab910..95ad8832ce 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml @@ -14,14 +14,6 @@ @Html.DisplayFor(model => model.Id) -
- - - From 4df73100cf842f4f52206eabe49f250809742087 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 4 Nov 2015 17:12:17 +0100 Subject: [PATCH 026/732] Export profiles: Minor UI fixes (further changes & fixes follow in a separate branch) --- .../Administration/Content/admin.less | 9 +++++--- .../Administration/Views/Export/Edit.cshtml | 21 ++++++++++--------- .../Views/Export/Preview.cshtml | 4 ++-- src/Presentation/SmartStore.Web/Web.config | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Content/admin.less b/src/Presentation/SmartStore.Web/Administration/Content/admin.less index 947ca2b377..620ce8206f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/admin.less +++ b/src/Presentation/SmartStore.Web/Administration/Content/admin.less @@ -430,12 +430,12 @@ td.adminData > input[type="checkbox"] { -------------------------------------------------------------- */ .large-select2 { .select2-choice { - height: 52px; - min-height: 52px; + height: 42px; + min-height: 42px; padding-top: 4px; > div > b { - padding-top: 14px; + padding-top: 10px; } } } @@ -444,10 +444,13 @@ td.adminData > input[type="checkbox"] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + height: 38px; img { margin-right: 12px; vertical-align: middle; + text-align: center; + max-height: 100%; } > div { margin-right: 0px !important; diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index f9174a7b29..55bac26fec 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -82,16 +82,17 @@ @Html.ValidationMessageFor(x => x.Enabled) - - - - + @if (Model.ScheduleTaskId > 0) + { + + + + + }
@Html.EditorFor(model => model.SeName) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "BlogPost", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.TextAreaFor(x => x.SeName, new { @class = "input-large" }) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "Category", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.EditorFor(x => x.SeName) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "Forum", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.EditorFor(x => x.SeName) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "ForumGroup", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.TextAreaFor(x => x.SeName) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "Manufacturer", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.EditorFor(model => model.SeName) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "NewsItem", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
@Html.TextAreaFor(x => x.SeName, new { @class = "input-large" }) + @if (Model.Id != 0) + { + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = "Product", entityId = @Model.Id }) + } @Html.ValidationMessageFor(model => model.SeName)
- @Html.SmartLabelFor(model => model.SlugsPerEntity) - - @Model.SlugsPerEntity -
@Html.SmartLabelFor(model => model.IsActive) @@ -37,25 +29,26 @@ @Html.EditorFor(model => model.Slug) + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = Model.EntityName, entityId = @Model.EntityId }) @Html.ValidationMessageFor(model => model.Slug)
- @Html.SmartLabelFor(model => model.EntityId) + @Html.SmartLabelFor(model => model.EntityName) - @Html.EditorFor(model => model.EntityId) - @Html.ValidationMessageFor(model => model.EntityId) + @Html.EditorFor(model => model.EntityName) + @Html.ValidationMessageFor(model => model.EntityName)
- @Html.SmartLabelFor(model => model.EntityName) + @Html.SmartLabelFor(model => model.EntityId) - @Html.EditorFor(model => model.EntityName) - @Html.ValidationMessageFor(model => model.EntityName) + @Html.EditorFor(model => model.EntityId) + @Html.ValidationMessageFor(model => model.EntityId)
- @Html.SmartLabelFor(x => x.ScheduleTaskId) - - @(Model.IsTaskEnabled ? T("Common.Scheduled") : T("Common.Unscheduled")) - - @Model.ScheduleTaskName -
+ @Html.SmartLabelFor(x => x.ScheduleTaskId) + + @Html.Action("MinimalTask", "ScheduleTask", new { taskId = Model.ScheduleTaskId, returnUrl = Request.RawUrl, cancellable = true, area = "admin" }) +

 

diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml index 1c0af5cf0a..b5e527976d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml @@ -22,10 +22,10 @@ {  @T("Admin.Configuration.ActivityLog") } - - diff --git a/src/Presentation/SmartStore.Web/Web.config b/src/Presentation/SmartStore.Web/Web.config index f45ffa195f..4a073d85ad 100644 --- a/src/Presentation/SmartStore.Web/Web.config +++ b/src/Presentation/SmartStore.Web/Web.config @@ -92,7 +92,7 @@ - + From 6b81ecadafd972363bc1d3b200968c9c86eb5520 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 4 Nov 2015 18:03:03 +0100 Subject: [PATCH 027/732] SEO Names: Input validation --- .../Controllers/UrlRecordController.cs | 2 +- .../Models/UrlRecord/UrlRecordModel.cs | 8 ++-- .../Views/UrlRecord/_CreateOrUpdate.cshtml | 4 +- .../MvcLocalization.Designer.cs | 41 +++++++++++-------- .../MvcLocalization.de.resx | 9 ++-- .../App_GlobalResources/MvcLocalization.resx | 9 ++-- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index 3d82fa456d..1eb07d10a5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -180,7 +180,7 @@ public ActionResult Edit(UrlRecordModel model, bool continueEditing) var urlRecords = _urlRecordService.GetAllUrlRecords(0, int.MaxValue, null, model.EntityName, model.EntityId, model.LanguageId, true); if (urlRecords.Count > 0) { - ModelState.AddModelError("", T("Admin.System.SeNames.ActiveSlugAlreadyExist")); + ModelState.AddModelError("IsActive", T("Admin.System.SeNames.ActiveSlugAlreadyExist")); } } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs index a5996c6bbc..589c616eac 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/UrlRecord/UrlRecordModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Mvc; @@ -7,14 +8,13 @@ namespace SmartStore.Admin.Models.UrlRecord { public partial class UrlRecordModel : EntityModelBase { - [SmartResourceDisplayName("Admin.System.SeNames.Name")] - [AllowHtml] + [Required, AllowHtml, SmartResourceDisplayName("Admin.System.SeNames.Name")] public string Slug { get; set; } - [SmartResourceDisplayName("Admin.System.SeNames.EntityName")] + [Required, SmartResourceDisplayName("Admin.System.SeNames.EntityName")] public string EntityName { get; set; } - [SmartResourceDisplayName("Admin.System.SeNames.EntityId")] + [Required, SmartResourceDisplayName("Admin.System.SeNames.EntityId")] public int EntityId { get; set; } [SmartResourceDisplayName("Admin.Common.Entity")] diff --git a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml index 95ad8832ce..ba361be39e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/UrlRecord/_CreateOrUpdate.cshtml @@ -2,7 +2,7 @@ @using SmartStore.Admin.Models.UrlRecord; @using Telerik.Web.Mvc.UI; -@Html.ValidationSummary(true) +@Html.ValidationSummary() @Html.HiddenFor(model => model.Id) @@ -29,8 +29,8 @@ diff --git a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.Designer.cs b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.Designer.cs index 07cb03a819..1eda45cf26 100644 --- a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.Designer.cs +++ b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.34209 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Resources { /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse - // über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der /str-Option erneut aus, oder Sie erstellen das Visual Studio-Projekt neu. + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option or rebuild the Visual Studio project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Web.Application.StronglyTypedResourceProxyBuilder", "12.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ internal MvcLocalization() { } /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal MvcLocalization() { } /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal MvcLocalization() { } /// - /// Sucht eine lokalisierte Zeichenfolge, die The field {0} must be a date. ähnelt. + /// Looks up a localized string similar to The field '{0}' must be a date.. /// internal static string FieldMustBeDate { get { @@ -70,7 +70,7 @@ internal static string FieldMustBeDate { } /// - /// Sucht eine lokalisierte Zeichenfolge, die The field {0} must be a number. ähnelt. + /// Looks up a localized string similar to The field '{0}' must be a number.. /// internal static string FieldMustBeNumeric { get { @@ -79,7 +79,7 @@ internal static string FieldMustBeNumeric { } /// - /// Sucht eine lokalisierte Zeichenfolge, die The value '{0}' is not valid for {1}. ähnelt. + /// Looks up a localized string similar to The value '{0}' is not valid for '{1}'.. /// internal static string PropertyValueInvalid { get { @@ -88,12 +88,21 @@ internal static string PropertyValueInvalid { } /// - /// Sucht eine lokalisierte Zeichenfolge, die A value is required. ähnelt. + /// Looks up a localized string similar to A value is required.. /// internal static string PropertyValueRequired { get { return ResourceManager.GetString("PropertyValueRequired", resourceCulture); } } + + /// + /// Looks up a localized string similar to The field '{0}' is required.. + /// + internal static string Required { + get { + return ResourceManager.GetString("Required", resourceCulture); + } + } } } diff --git a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.de.resx b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.de.resx index e1d0fa764f..fee5893b3b 100644 --- a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.de.resx +++ b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.de.resx @@ -118,15 +118,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Das Feld {0} muss ein Datum sein. + Das Feld '{0}' muss ein Datum sein. - Das Feld {0} muss numerisch sein. + Das Feld '{0}' muss numerisch sein. - Der Wert '{0}' für {1} ist ungültig. + Der Wert '{0}' für '{1}' ist ungültig. Ein Wert ist zwingend erforderlich. + + Das Feld '{0}' ist erforderlich. + \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.resx b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.resx index da4a85fa32..0e272fccd2 100644 --- a/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.resx +++ b/src/Presentation/SmartStore.Web/App_GlobalResources/MvcLocalization.resx @@ -118,15 +118,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - The field {0} must be a date. + The field '{0}' must be a date. - The field {0} must be a number. + The field '{0}' must be a number. - The value '{0}' is not valid for {1}. + The value '{0}' is not valid for '{1}'. A value is required. + + The field '{0}' is required. + \ No newline at end of file From 4f5247fe220897b012ab5e180ba5e944deceeebe Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 4 Nov 2015 20:50:40 +0100 Subject: [PATCH 028/732] Web-API: Added OData endpoint for shipment items --- .../Domain/Shipping/Shipment.cs | 1 + .../Domain/Shipping/ShipmentItem.cs | 6 ++++ .../Shipping/ShipmentService.cs | 31 ++++++++++++++++-- .../OData/ShipmentItemsController.cs | 32 +++++++++++++++++++ src/Plugins/SmartStore.WebApi/Description.txt | 2 +- .../SmartStore.WebApi.csproj | 1 + .../WebApiConfigurationProvider.cs | 2 ++ src/Plugins/SmartStore.WebApi/changelog.md | 4 +++ 8 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/Plugins/SmartStore.WebApi/Controllers/OData/ShipmentItemsController.cs diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs index d0841d1dd6..e189e22b12 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs @@ -59,6 +59,7 @@ public partial class Shipment : BaseEntity /// /// Gets or sets the shipment items /// + [DataMember] public virtual ICollection ShipmentItems { get { return _shipmentItems ?? (_shipmentItems = new HashSet()); } diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/ShipmentItem.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/ShipmentItem.cs index 48db08b9e1..fc77fb18b2 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/ShipmentItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/ShipmentItem.cs @@ -1,29 +1,35 @@ +using System.Runtime.Serialization; namespace SmartStore.Core.Domain.Shipping { /// /// Represents a shipment order product variant /// + [DataContract] public partial class ShipmentItem : BaseEntity { /// /// Gets or sets the shipment identifier /// + [DataMember] public int ShipmentId { get; set; } /// /// Gets or sets the order item identifier /// + [DataMember] public int OrderItemId { get; set; } /// /// Gets or sets the quantity /// + [DataMember] public int Quantity { get; set; } /// /// Gets the shipment /// + [DataMember] public virtual Shipment Shipment { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs b/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs index 301122a7fc..3a8c261164 100644 --- a/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs @@ -7,7 +7,6 @@ using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Events; -using SmartStore.Core.Plugins; using SmartStore.Services.Orders; namespace SmartStore.Services.Shipping @@ -240,7 +239,20 @@ public virtual void InsertShipmentItem(ShipmentItem shipmentItem) //event notifications _eventPublisher.EntityInserted(shipmentItem); - _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + + if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) + { + _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + } + else + { + var shipment = _shipmentRepository.Table + .Expand(x => x.Order) + .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); + + if (shipment != null) + _eventPublisher.PublishOrderUpdated(shipment.Order); + } } /// @@ -256,7 +268,20 @@ public virtual void UpdateShipmentItem(ShipmentItem shipmentItem) //event notifications _eventPublisher.EntityUpdated(shipmentItem); - _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + + if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) + { + _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + } + else + { + var shipment = _shipmentRepository.Table + .Expand(x => x.Order) + .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); + + if (shipment != null) + _eventPublisher.PublishOrderUpdated(shipment.Order); + } } #endregion diff --git a/src/Plugins/SmartStore.WebApi/Controllers/OData/ShipmentItemsController.cs b/src/Plugins/SmartStore.WebApi/Controllers/OData/ShipmentItemsController.cs new file mode 100644 index 0000000000..47fdf9982a --- /dev/null +++ b/src/Plugins/SmartStore.WebApi/Controllers/OData/ShipmentItemsController.cs @@ -0,0 +1,32 @@ +using System.Web.Http; +using SmartStore.Core.Domain.Shipping; +using SmartStore.Services.Shipping; +using SmartStore.Web.Framework.WebApi; +using SmartStore.Web.Framework.WebApi.OData; +using SmartStore.Web.Framework.WebApi.Security; + +namespace SmartStore.WebApi.Controllers.OData +{ + [WebApiAuthenticate(Permission = "ManageOrders")] + public class ShipmentItemsController : WebApiEntityController + { + protected override void Insert(ShipmentItem entity) + { + Service.InsertShipmentItem(entity); + } + protected override void Update(ShipmentItem entity) + { + Service.UpdateShipmentItem(entity); + } + protected override void Delete(ShipmentItem entity) + { + Service.DeleteShipmentItem(entity); + } + + [WebApiQueryable] + public SingleResult GetShipmentItem(int key) + { + return GetSingleResult(key); + } + } +} diff --git a/src/Plugins/SmartStore.WebApi/Description.txt b/src/Plugins/SmartStore.WebApi/Description.txt index cbe8fe9af5..085a7c5a09 100644 --- a/src/Plugins/SmartStore.WebApi/Description.txt +++ b/src/Plugins/SmartStore.WebApi/Description.txt @@ -1,6 +1,6 @@ FriendlyName: SmartStore.NET Web Api SystemName: SmartStore.WebApi -Version: 2.2.0.3 +Version: 2.2.0.4 Group: Api MinAppVersion: 2.2.0 Author: SmartStore AG diff --git a/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj b/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj index feaadf8fcc..20e3fc318c 100644 --- a/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj +++ b/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj @@ -215,6 +215,7 @@ + diff --git a/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs index 40c3a8b75c..db3d1f3377 100644 --- a/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs +++ b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs @@ -92,6 +92,7 @@ public void Configure(WebApiConfigurationBroadcaster configData) m.EntitySet(WebApiOdataEntitySet.ReturnRequests); m.EntitySet(WebApiOdataEntitySet.Settings); m.EntitySet(WebApiOdataEntitySet.Shipments); + m.EntitySet(WebApiOdataEntitySet.ShipmentItems); m.EntitySet(WebApiOdataEntitySet.ShippingMethods); m.EntitySet(WebApiOdataEntitySet.SpecificationAttributeOptions); m.EntitySet(WebApiOdataEntitySet.SpecificationAttributes); @@ -145,6 +146,7 @@ public static class WebApiOdataEntitySet public static string ReturnRequests { get { return "ReturnRequests"; } } public static string Settings { get { return "Settings"; } } public static string Shipments { get { return "Shipments"; } } + public static string ShipmentItems { get { return "ShipmentItems"; } } public static string ShippingMethods { get { return "ShippingMethods"; } } public static string SpecificationAttributeOptions { get { return "SpecificationAttributeOptions"; } } public static string SpecificationAttributes { get { return "SpecificationAttributes"; } } diff --git a/src/Plugins/SmartStore.WebApi/changelog.md b/src/Plugins/SmartStore.WebApi/changelog.md index 6c5bd87808..2191a7d055 100644 --- a/src/Plugins/SmartStore.WebApi/changelog.md +++ b/src/Plugins/SmartStore.WebApi/changelog.md @@ -1,5 +1,9 @@ #Release Notes +##Web Api 2.2.0.4 +###New Features +* Added OData endpoint for shipment items + ##Web Api 2.2.0.3 ###New Features * Added OData endpoint for payment method From fdf1c6349c2b8c8d660fafc51742a841d600d310 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 4 Nov 2015 22:44:13 +0100 Subject: [PATCH 029/732] Export Framework: Refactored enums --- .../{ExportCore.cs => ExportEnums.cs} | 26 ++++----- .../Domain/DataExchange/ExportProfile.cs | 2 +- .../Domain/DataExchange/ExportProjection.cs | 4 +- .../Plugins/Providers/ProviderMetadata.cs | 2 +- .../SmartStore.Core/SmartStore.Core.csproj | 2 +- src/Libraries/SmartStore.Core/XmlHelper.cs | 56 +++++++------------ .../DataExchange/ExportExtensions.cs | 9 +-- ...tributes.cs => ExportFeaturesAttribute.cs} | 8 +-- .../DataExchange/ExportService.cs | 4 +- .../ExportTask/ExportProfileTask.cs | 21 +++---- .../ExportTask/ExportProfileTaskContext.cs | 10 ++-- .../SmartStore.Services.csproj | 2 +- .../Providers/ProductExportXmlProvider.cs | 18 +++--- .../DependencyRegistrar.cs | 12 ++-- .../Models/DataExchange/ExportProfileModel.cs | 2 +- .../custom/telerik/telerik.smartstore.less | 2 +- 16 files changed, 83 insertions(+), 97 deletions(-) rename src/Libraries/SmartStore.Core/Domain/DataExchange/{ExportCore.cs => ExportEnums.cs} (87%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportAttributes.cs => ExportFeaturesAttribute.cs} (68%) diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportCore.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs similarity index 87% rename from src/Libraries/SmartStore.Core/Domain/DataExchange/ExportCore.cs rename to src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs index f9a5dcc18e..b7f041fed5 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportCore.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs @@ -61,62 +61,62 @@ public enum ExportAttributeValueMerging /// /// Controls data processing and projection items supported by export provider /// - public enum ExportSupport + public enum ExportFeatures { /// /// Whether to automatically create a file based public deployment when an export profile is created /// - CreateInitialPublicDeployment = 0, + CreatesInitialPublicDeployment = 0, /// /// Whether to offer option to include\exclude grouped products /// - ProjectionNoGroupedProducts, + CanOmitGroupedProducts, /// /// Whether to offer option to export attribute combinations as products /// - ProjectionAttributeCombinationAsProduct, + CanProjectAttributeCombinations, /// /// Whether to offer further options to manipulate the product description /// - ProjectionDescription, + CanProjectDescription, /// /// Whether to offer option to enter a brand fallback /// - ProjectionBrand, + OffersBrandFallback, /// /// Whether to offer option to set a picture size and to get the URL of the main image /// - ProjectionMainPictureUrl, + CanIncludeMainPicture, /// /// Whether to use SKU as manufacturer part number if MPN is empty /// - ProjectionUseOwnProductNo, + UsesSkuAsMpnFallback, /// /// Whether to offer option to enter a shipping time fallback /// - ProjectionShippingTime, + OffersShippingTimeFallback, /// /// Whether to offer option to enter a shipping costs fallback and a free shipping threshold /// - ProjectionShippingCosts, + OffersShippingCostsFallback, /// /// Whether to get the calculated old product price /// - ProjectionOldPrice, + UsesOldPrice, /// - /// Whether to get the calculated special and regular (ignoring special offers) price + /// Whether to get the calculated special and regular price (ignoring special offers) /// - ProjectionSpecialPrice + UsesSpecialPrice } /// diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs index 75331d4a2c..ea98e0838b 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs @@ -88,7 +88,7 @@ public ExportProfile() public bool PerStore { get; set; } /// - /// Identifier of an email account used to send a notification message of the completion of the export + /// Email Account identifier used to send a notification message an export completes /// public int EmailAccountId { get; set; } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs index abe979107d..472c94e114 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs @@ -7,7 +7,9 @@ namespace SmartStore.Core.Domain.DataExchange /// /// Settings projected onto an export /// - /// Note possible projection controlling: a) developer controls, b) merchant controls, c) developer controls what the merchant can control + /// + /// Note possible projection controlling: a) developer controls, b) merchant controls, c) developer controls what the merchant can control + /// [Serializable] public class ExportProjection { diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs index b1da4a1304..31d98942d4 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs @@ -76,7 +76,7 @@ public class ProviderMetadata /// /// Gets or sets an array of values that reflects what export data processing is supported by a provider /// - public ExportSupport[] ExportSupport { get; set; } + public ExportFeatures[] ExportSupport { get; set; } /// /// Gets or sets an array of widget system names, which depend on the current provider diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index ab5fd94130..afe41e0061 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -166,7 +166,7 @@ - + diff --git a/src/Libraries/SmartStore.Core/XmlHelper.cs b/src/Libraries/SmartStore.Core/XmlHelper.cs index b73c914f03..6d9f60def4 100644 --- a/src/Libraries/SmartStore.Core/XmlHelper.cs +++ b/src/Libraries/SmartStore.Core/XmlHelper.cs @@ -123,17 +123,7 @@ public static DateTime DeserializeDateTime(string dateTime) /// XML string public static string Serialize(T instance) { - if (instance != null) - { - using (var writer = new StringWriter()) - { - var xmlSerializer = new XmlSerializer(typeof(T)); - - xmlSerializer.Serialize(writer, instance); - return writer.ToString(); - } - } - return null; + return Serialize(instance, typeof(T)); } /// @@ -144,17 +134,18 @@ public static string Serialize(T instance) /// XML string public static string Serialize(object instance, Type type) { - if (instance != null) + if (instance == null) + return null; + + Guard.NotNull(() => type); + + using (var writer = new StringWriter()) { - using (var writer = new StringWriter()) - { - var xmlSerializer = new XmlSerializer(type); + var xmlSerializer = new XmlSerializer(type); - xmlSerializer.Serialize(writer, instance); - return writer.ToString(); - } + xmlSerializer.Serialize(writer, instance); + return writer.ToString(); } - return null; } /// @@ -165,16 +156,7 @@ public static string Serialize(object instance, Type type) /// Object instance public static T Deserialize(string xml) { - if (xml.HasValue()) - { - using (var reader = new StringReader(xml)) - { - var serializer = new XmlSerializer(typeof(T)); - - return (T)serializer.Deserialize(reader); - } - } - return (T)Activator.CreateInstance(typeof(T)); + return (T)Deserialize(xml, typeof(T)); } /// @@ -185,16 +167,16 @@ public static T Deserialize(string xml) /// Object instance public static object Deserialize(string xml, Type type) { - if (xml.HasValue()) - { - using (var reader = new StringReader(xml)) - { - var serializer = new XmlSerializer(type); + Guard.NotNull(() => type); - return serializer.Deserialize(reader); - } + if (xml.IsEmpty()) + return Activator.CreateInstance(type); + + using (var reader = new StringReader(xml)) + { + var serializer = new XmlSerializer(type); + return serializer.Deserialize(reader); } - return Activator.CreateInstance(type); } #endregion diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index c7f6827bb0..ae5b8ff6ce 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -21,19 +21,20 @@ public static class ExportExtensions /// true provider is valid, false provider is invalid. public static bool IsValid(this Provider provider) { - return (provider != null); + return provider != null; } /// /// Returns a value indicating whether the export provider supports a projection type /// /// Export provider - /// The type to check + /// The feature to check /// true provider supports type, false provider does not support type. - public static bool Supports(this Provider provider, ExportSupport type) + public static bool Supports(this Provider provider, ExportFeatures feature) { if (provider != null) - return provider.Metadata.ExportSupport.Contains(type); + return provider.Metadata.ExportSupport.Contains(feature); + return false; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportAttributes.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs similarity index 68% rename from src/Libraries/SmartStore.Services/DataExchange/ExportAttributes.cs rename to src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs index ece4d81d38..d29f5641d3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportAttributes.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs @@ -8,13 +8,13 @@ namespace SmartStore.Services.DataExchange /// Projection type controls whether to display corresponding projection fields while editing an export profile. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ExportSupportingAttribute : Attribute + public sealed class ExportFeaturesAttribute : Attribute { - public ExportSupportingAttribute(params ExportSupport[] types) + public ExportFeaturesAttribute(params ExportFeatures[] features) { - Types = types; + Features = features; } - public ExportSupport[] Types { get; set; } + public ExportFeatures[] Features { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs index 250e0eecff..aea03c6fa9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs @@ -116,7 +116,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi RemoveCriticalCharacters = true, CriticalCharacters = "¼,½,¾", PriceType = PriceDisplayType.PreSelectedPrice, - NoGroupedProducts = (provider.Supports(ExportSupport.ProjectionNoGroupedProducts) ? true : false) + NoGroupedProducts = (provider.Supports(ExportFeatures.CanOmitGroupedProducts) ? true : false) }; var filter = new ExportFilter @@ -150,7 +150,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi { if (cloneProfile == null) { - if (provider.Supports(ExportSupport.CreateInitialPublicDeployment)) + if (provider.Supports(ExportFeatures.CreatesInitialPublicDeployment)) { profile.Deployments.Add(new ExportDeployment { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 66698ab1a3..d9a6a17a36 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -1556,7 +1556,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro var productTemplate = ctx.ProductTemplates.FirstOrDefault(x => x.Key == product.ProductTemplateId); var pictureSize = _mediaSettings.Value.ProductDetailsPictureSize; - if (ctx.Supporting[ExportSupport.ProjectionMainPictureUrl] && ctx.Projection.PictureSize > 0) + if (ctx.SupportedFeatures[ExportFeatures.CanIncludeMainPicture] && ctx.Projection.PictureSize > 0) pictureSize = ctx.Projection.PictureSize; var perfLoadId = (ctx.IsPreview ? 0 : product.Id); // perf preview (it's a compromise) @@ -1762,12 +1762,12 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro #region more attribute controlled data - if (ctx.Supports(ExportSupport.ProjectionDescription)) + if (ctx.Supports(ExportFeatures.CanProjectDescription)) { PrepareProductDescription(ctx, expando, product); } - if (ctx.Supports(ExportSupport.ProjectionBrand)) + if (ctx.Supports(ExportFeatures.OffersBrandFallback)) { string brand = null; var productManus = ctx.DataContextProduct.ProductManufacturers.Load(perfLoadId); @@ -1781,7 +1781,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro expando._Brand = brand; } - if (ctx.Supports(ExportSupport.ProjectionMainPictureUrl)) + if (ctx.Supports(ExportFeatures.CanIncludeMainPicture)) { if (productPictures != null && productPictures.Any()) expando._MainPictureUrl = _pictureService.Value.GetPictureUrl(productPictures.First().Picture, ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); @@ -1823,18 +1823,18 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro // navigation properties GetDeliveryTimeAndQuantityUnit(ctx, exp, product.DeliveryTimeId, product.QuantityUnitId); - if (ctx.Supports(ExportSupport.ProjectionUseOwnProductNo) && product.ManufacturerPartNumber.IsEmpty()) + if (ctx.Supports(ExportFeatures.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty()) { exp.ManufacturerPartNumber = product.Sku; } - if (ctx.Supports(ExportSupport.ProjectionShippingTime)) + if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback)) { dynamic deliveryTime = exp.DeliveryTime; exp._ShippingTime = (deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name); } - if (ctx.Supports(ExportSupport.ProjectionShippingCosts)) + if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback)) { exp._FreeShippingThreshold = ctx.Projection.FreeShippingThreshold; @@ -1844,7 +1844,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro exp._ShippingCosts = ctx.Projection.ShippingCosts; } - if (ctx.Supports(ExportSupport.ProjectionOldPrice)) + if (ctx.Supports(ExportFeatures.UsesOldPrice)) { if (product.OldPrice != decimal.Zero && product.OldPrice != (decimal)exp.Price && !(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) { @@ -1864,7 +1864,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro } } - if (ctx.Supports(ExportSupport.ProjectionSpecialPrice)) + if (ctx.Supports(ExportFeatures.UsesSpecialPrice)) { exp._SpecialPrice = null; exp._RegularPrice = null; // price if a special price would not exist @@ -2907,7 +2907,8 @@ public void Execute(TaskExecutionContext taskContext) /// Any data passed on IExportExecuteContext.CustomProperties /// Product query that supersede profile filtering /// Export execute result - public ExportExecuteResult Execute(string providerSystemName, + public ExportExecuteResult Execute( + string providerSystemName, IComponentContext context, CancellationToken cancellationToken, ExportProfile profile = null, diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs index 2beab3db1b..5fee680634 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs @@ -40,8 +40,8 @@ public ExportProfileTaskContext( EntityIdsSelected = selectedIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); PreviewData = previewData; - Supporting = Enum.GetValues(typeof(ExportSupport)) - .Cast() + SupportedFeatures = Enum.GetValues(typeof(ExportFeatures)) + .Cast() .ToDictionary(x => x, x => Provider.Supports(x)); FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(profile.FolderName)); @@ -83,10 +83,10 @@ public bool IsPreview public ExportProfile Profile { get; private set; } public Provider Provider { get; private set; } - public Dictionary Supporting { get; private set; } - public bool Supports(ExportSupport type) + public Dictionary SupportedFeatures { get; private set; } + public bool Supports(ExportFeatures feature) { - return (!IsPreview && Supporting[type]); + return (!IsPreview && SupportedFeatures[feature]); } public ExportFilter Filter { get; private set; } diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 2de19dd74f..3c3f321c24 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -192,7 +192,7 @@ - + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs index ed7eb62390..03ed6a00ff 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs @@ -19,15 +19,15 @@ namespace SmartStore.GoogleMerchantCenter.Providers [SystemName("Feeds.GoogleMerchantCenterProductXml")] [FriendlyName("Google Merchant Center XML product feed")] [DisplayOrder(1)] - [ExportSupporting( - ExportSupport.CreateInitialPublicDeployment, - ExportSupport.ProjectionNoGroupedProducts, - ExportSupport.ProjectionAttributeCombinationAsProduct, - ExportSupport.ProjectionDescription, - ExportSupport.ProjectionUseOwnProductNo, - ExportSupport.ProjectionBrand, - ExportSupport.ProjectionMainPictureUrl, - ExportSupport.ProjectionSpecialPrice)] + [ExportFeatures( + ExportFeatures.CreatesInitialPublicDeployment, + ExportFeatures.CanOmitGroupedProducts, + ExportFeatures.CanProjectAttributeCombinations, + ExportFeatures.CanProjectDescription, + ExportFeatures.UsesSkuAsMpnFallback, + ExportFeatures.OffersBrandFallback, + ExportFeatures.CanIncludeMainPicture, + ExportFeatures.UsesSpecialPrice)] public class ProductExportXmlProvider : IExportProvider { private const string _googleNamespace = "http://base.google.com/ns/1.0"; diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index d9b7ca9b43..ee70cae011 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -768,7 +768,7 @@ protected override void Load(ContainerBuilder builder) var isConfigurable = typeof(IConfigurable).IsAssignableFrom(type); var isEditable = typeof(IUserEditable).IsAssignableFrom(type); var isHidden = GetIsHidden(type); - var exportSupport = GetExportSupport(type); + var exportSupport = GetExportFeatures(type); var registration = builder.RegisterType(type).Named(systemName).InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.None); registration.WithMetadata(m => @@ -863,16 +863,16 @@ private bool GetIsHidden(Type type) return false; } - private ExportSupport[] GetExportSupport(Type type) + private ExportFeatures[] GetExportFeatures(Type type) { - var attr = type.GetAttribute(false); + var attr = type.GetAttribute(false); - if (attr != null && attr.Types != null) + if (attr != null && attr.Features != null) { - return attr.Types; + return attr.Features; } - return new ExportSupport[0]; + return new ExportFeatures[0]; } private Tuple GetFriendlyName(Type type, PluginDescriptor descriptor) diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index 80dce133e9..3ebf4350d9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -94,7 +94,7 @@ public class ProviderModel public Type ConfigDataType { get; set; } public object ConfigData { get; set; } - public ExportSupport[] Supporting { get; set; } + public ExportFeatures[] Supporting { get; set; } [SmartResourceDisplayName("Common.Image")] public string ThumbnailUrl { get; set; } diff --git a/src/Presentation/SmartStore.Web/Content/bootstrap/custom/telerik/telerik.smartstore.less b/src/Presentation/SmartStore.Web/Content/bootstrap/custom/telerik/telerik.smartstore.less index 6b333e4237..ab27daa31b 100644 --- a/src/Presentation/SmartStore.Web/Content/bootstrap/custom/telerik/telerik.smartstore.less +++ b/src/Presentation/SmartStore.Web/Content/bootstrap/custom/telerik/telerik.smartstore.less @@ -153,7 +153,7 @@ } .t-numerictextbox .t-input { - width: 252px; + width: 301px; padding: 6px 8px 6px 3px; } .t-numerictextbox.small .t-input { From 17fcdd3af7b2a1d25971fe2a082b3cfb22ec7a90 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 4 Nov 2015 23:11:09 +0100 Subject: [PATCH 030/732] Export framework: minor refactoring --- .../ExportCategoryXmlProvider.cs | 2 +- .../ExportCustomerXlsxProvider.cs | 2 +- .../ExportCustomerXmlProvider.cs | 2 +- .../ExportManufacturerXmlProvider.cs | 2 +- .../ExportNewsSubscriptionCsvProvider.cs | 2 +- .../ExportProvider/ExportOrderXlsxProvider.cs | 2 +- .../ExportProvider/ExportOrderXmlProvider.cs | 2 +- .../ExportProductXlsxProvider.cs | 2 +- .../ExportProductXmlProvider.cs | 2 +- .../ExportTask/ExportDataContextCategory.cs | 3 +- .../ExportTask/ExportDataContextCustomer.cs | 3 +- .../ExportDataContextManufacturer.cs | 3 +- .../ExportTask/ExportDataContextProduct.cs | 3 +- .../ExportTask/ExportProfileTask.cs | 35 ++++++++++--------- .../DataExchange/IExportProvider.cs | 2 +- .../DataExchange/IExportService.cs | 2 +- .../Providers/ProductExportXmlProvider.cs | 2 +- .../Views/Export/_Tab.Projection.cshtml | 14 ++++---- 18 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs index 7e4e6f9fba..6bd4aa3d2f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs @@ -105,7 +105,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs index c2f4722f1a..2ab8dcb6b0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs @@ -215,7 +215,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs index cbf1c80719..4573e6c69c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs @@ -88,7 +88,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs index ebe52eb5f9..5341411be7 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs @@ -105,7 +105,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs index 00400d64e9..36479f946f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs @@ -74,7 +74,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs index 4cd8a5c5e2..ba85cc3b0d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs @@ -251,7 +251,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs index e04e6829bf..185181c013 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs @@ -261,7 +261,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs index 516d05488a..e9ec1bbc59 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs @@ -415,7 +415,7 @@ public void Execute(IExportExecuteContext context) GC.Collect(); } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs index a6894b4242..6352c98b03 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs @@ -88,7 +88,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs index f19fb56f47..8e2efa0f93 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs @@ -18,7 +18,8 @@ internal class ExportDataContextCategory private LazyMultimap _productCategories; private LazyMultimap _pictures; - public ExportDataContextCategory(IEnumerable categories, + public ExportDataContextCategory( + IEnumerable categories, Func> productCategories, Func> pictures) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs index ac85860cc9..f7c027a27d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs @@ -15,7 +15,8 @@ public class ExportDataContextCustomer private LazyMultimap _genericAttributes; - public ExportDataContextCustomer(IEnumerable customers, + public ExportDataContextCustomer( + IEnumerable customers, Func> genericAttributes) { if (customers == null) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs index 79bd1558ca..9d2002be35 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs @@ -18,7 +18,8 @@ internal class ExportDataContextManufacturer private LazyMultimap _productManufacturers; private LazyMultimap _pictures; - public ExportDataContextManufacturer(IEnumerable manufacturers, + public ExportDataContextManufacturer( + IEnumerable manufacturers, Func> productManufacturers, Func> pictures) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs index 14f9168be4..bb0d6ea6c1 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs @@ -24,7 +24,8 @@ internal class ExportDataContextProduct : PriceCalculationContext private LazyMultimap _productSpecificationAttributes; private LazyMultimap _productBundleItems; - public ExportDataContextProduct(IEnumerable products, + public ExportDataContextProduct( + IEnumerable products, Func> attributes, Func> attributeCombinations, Func> tierPrices, diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index d9a6a17a36..e6016546ef 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -51,6 +51,7 @@ using SmartStore.Services.Tax; using SmartStore.Utilities; using SmartStore.Utilities.Threading; +using SmartStore.Core.Localization; // note: namespace persisted in ScheduleTask.Type namespace SmartStore.Services.DataExchange.ExportTask @@ -132,6 +133,8 @@ private void InitDependencies(TaskExecutionContext context) _subscriptionRepository = context.Resolve>>(); } + public Localizer T { get; set; } + #endregion #region Utilities @@ -2598,7 +2601,7 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) if (ctx.Export.Abort != ExportAbortion.Hard) { - ctx.Provider.Value.ExecuteEnded(ctx.Export); + ctx.Provider.Value.OnExecuted(ctx.Export); } } } @@ -2624,7 +2627,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) ctx.Log = logger; ctx.Export.Log = logger; - ctx.ProgressInfo = _services.Localization.GetResource("Admin.DataExchange.Export.ProgressInfo"); + ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); if (ctx.Profile.ProviderConfigData.HasValue()) { @@ -2637,9 +2640,9 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) using (var scope = new DbContextScope(_services.DbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) { - ctx.DeliveryTimes = _deliveryTimeService.Value.GetAllDeliveryTimes().ToDictionary(x => x.Id, x => x); - ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id, x => x); - ctx.ProductTemplates = _productTemplateService.Value.GetAllProductTemplates().ToDictionary(x => x.Id, x => x); + ctx.DeliveryTimes = _deliveryTimeService.Value.GetAllDeliveryTimes().ToDictionary(x => x.Id); + ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id); + ctx.ProductTemplates = _productTemplateService.Value.GetAllProductTemplates().ToDictionary(x => x.Id); if (ctx.Provider.Value.EntityType == ExportEntityType.Product) { @@ -2681,7 +2684,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) ZipFile.CreateFromDirectory(ctx.FolderContent, ctx.ZipPath, CompressionLevel.Fastest, true); } - SetProgress(ctx, _services.Localization.GetResource("Common.Deployment")); + SetProgress(ctx, T("Common.Deployment")); foreach (var deployment in ctx.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled)) { @@ -2885,7 +2888,7 @@ public void Execute(TaskExecutionContext taskContext) var provider = _exportService.Value.LoadProvider(profile.ProviderSystemName); if (provider == null) - throw new SmartException(_services.Localization.GetResource("Admin.Common.ProviderNotLoaded").FormatInvariant(profile.ProviderSystemName.NaIfEmpty())); + throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); var ctx = new ExportProfileTaskContext(taskContext, profile, provider, selectedEntityIds); @@ -2927,7 +2930,7 @@ public ExportExecuteResult Execute( var provider = _exportService.Value.LoadProvider(providerSystemName); if (provider == null) - throw new SmartException(_services.Localization.GetResource("Admin.Common.ProviderNotLoaded").FormatInvariant(providerSystemName.NaIfEmpty())); + throw new SmartException(T("Admin.Common.ProviderNotLoaded", providerSystemName.NaIfEmpty())); if (profile == null) profile = _exportService.Value.CreateVolatileProfile(provider); @@ -2950,24 +2953,24 @@ public ExportExecuteResult Execute( var extension = Path.GetExtension(ctx.Result.Files.First().FileName); if (provider.Value.EntityType == ExportEntityType.Product) - prefix = _services.Localization.GetResource("Admin.Catalog.Products"); + prefix = T("Admin.Catalog.Products"); else if (provider.Value.EntityType == ExportEntityType.Order) - prefix = _services.Localization.GetResource("Admin.Orders"); + prefix = T("Admin.Orders"); else if (provider.Value.EntityType == ExportEntityType.Category) - prefix = _services.Localization.GetResource("Admin.Catalog.Categories"); + prefix = T("Admin.Catalog.Categories"); else if (provider.Value.EntityType == ExportEntityType.Manufacturer) - prefix = _services.Localization.GetResource("Admin.Catalog.Manufacturers"); + prefix = T("Admin.Catalog.Manufacturers"); else if (provider.Value.EntityType == ExportEntityType.Customer) - prefix = _services.Localization.GetResource("Admin.Customers"); + prefix = T("Admin.Customers"); else if (provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) - prefix = _services.Localization.GetResource("Admin.Promotions.NewsLetterSubscriptions"); + prefix = T("Admin.Promotions.NewsLetterSubscriptions"); else prefix = provider.Value.EntityType.ToString(); if (selectedEntityIds.HasValue()) - suffix = (selectedEntityIds.Contains(",") ? _services.Localization.GetResource("Admin.Common.Selected") : selectedEntityIds); + suffix = (selectedEntityIds.Contains(",") ? T("Admin.Common.Selected").Text : selectedEntityIds); else - suffix = _services.Localization.GetResource("Common.All"); + suffix = T("Common.All"); ctx.Result.DownloadFileName = string.Concat(prefix, "-", suffix).ToLower().ToValidFileName() + extension; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs index c0e683a0e8..b0872ef1eb 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs @@ -30,6 +30,6 @@ public partial interface IExportProvider : IProvider, IUserEditable /// Called once per store when export execution ended /// /// Export execution context - void ExecuteEnded(IExportExecuteContext context); + void OnExecuted(IExportExecuteContext context); } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs b/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs index 734777325f..e3823a7c4e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs @@ -8,7 +8,7 @@ namespace SmartStore.Services.DataExchange public interface IExportService { /// - /// Creates a volatile export project + /// Creates a volatile export profile /// /// Export provider /// New export profile diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs index 03ed6a00ff..bbcb1bcbda 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs @@ -401,7 +401,7 @@ public void Execute(IExportExecuteContext context) } } - public void ExecuteEnded(IExportExecuteContext context) + public void OnExecuted(IExportExecuteContext context) { // nothing to do } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml index 73113cc2a5..4815fba951 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml @@ -42,7 +42,7 @@ @if (Model.Provider.EntityType == ExportEntityType.Product) { - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionAttributeCombinationAsProduct)) + if (Model.Provider.Supporting.Contains(ExportFeatures.CanProjectAttributeCombinations)) { } - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionNoGroupedProducts)) + if (Model.Provider.Supporting.Contains(ExportFeatures.CanOmitGroupedProducts)) { - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionShippingTime)) + if (Model.Provider.Supporting.Contains(ExportFeatures.OffersShippingTimeFallback)) { } - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionShippingCosts)) + if (Model.Provider.Supporting.Contains(ExportFeatures.OffersShippingCostsFallback)) { } - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionMainPictureUrl)) + if (Model.Provider.Supporting.Contains(ExportFeatures.CanIncludeMainPicture)) { } - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionBrand)) + if (Model.Provider.Supporting.Contains(ExportFeatures.OffersBrandFallback)) { } - if (Model.Provider.Supporting.Contains(ExportSupport.ProjectionDescription)) + if (Model.Provider.Supporting.Contains(ExportFeatures.CanProjectDescription)) { } - if (Model.Provider.Supporting.Contains(ExportFeatures.CanOmitGroupedProducts)) + if (Model.Provider.Feature.HasFlag(ExportFeature.CanOmitGroupedProducts)) { - if (Model.Provider.Supporting.Contains(ExportFeatures.OffersShippingTimeFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeature.OffersShippingTimeFallback)) { } - if (Model.Provider.Supporting.Contains(ExportFeatures.OffersShippingCostsFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeature.OffersShippingCostsFallback)) { } - if (Model.Provider.Supporting.Contains(ExportFeatures.CanIncludeMainPicture)) + if (Model.Provider.Feature.HasFlag(ExportFeature.CanIncludeMainPicture)) { } - if (Model.Provider.Supporting.Contains(ExportFeatures.OffersBrandFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeature.OffersBrandFallback)) { } - if (Model.Provider.Supporting.Contains(ExportFeatures.CanProjectDescription)) + if (Model.Provider.Feature.HasFlag(ExportFeature.CanProjectDescription)) { } - if (Model.Provider.Feature.HasFlag(ExportFeature.CanOmitGroupedProducts)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.CanOmitGroupedProducts)) { - if (Model.Provider.Feature.HasFlag(ExportFeature.OffersShippingTimeFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.OffersShippingTimeFallback)) { } - if (Model.Provider.Feature.HasFlag(ExportFeature.OffersShippingCostsFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.OffersShippingCostsFallback)) { } - if (Model.Provider.Feature.HasFlag(ExportFeature.CanIncludeMainPicture)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.CanIncludeMainPicture)) { } - if (Model.Provider.Feature.HasFlag(ExportFeature.OffersBrandFallback)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.OffersBrandFallback)) { } - if (Model.Provider.Feature.HasFlag(ExportFeature.CanProjectDescription)) + if (Model.Provider.Feature.HasFlag(ExportFeatures.CanProjectDescription)) { + + + + + + + + + @@ -45,12 +46,20 @@ +
@Html.EditorFor(model => model.Slug) - @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = Model.EntityName, entityId = @Model.EntityId }) @Html.ValidationMessageFor(model => model.Slug) + @Html.Action("NamesPerEntity", "UrlRecord", new { entityName = Model.EntityName, entityId = @Model.EntityId })
@@ -71,7 +71,7 @@
@@ -110,7 +110,7 @@
@@ -123,7 +123,7 @@
@@ -145,7 +145,7 @@
@@ -158,7 +158,7 @@
@@ -171,7 +171,7 @@
From 3f19750ae6e7c5f9e085e4b756e2b95dde55749e Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 5 Nov 2015 18:38:36 +0100 Subject: [PATCH 031/732] Fix #804: ShopBar conflicts with 'close store' feature --- .../SmartStore.Core/ComponentModel/Expando.cs | 5 + .../201509150931528_ExportFramework2.cs | 9 + .../ExportTask/ExportProfileTask.cs | 2 + .../DataExchange/Internal/ExpandoEntity.cs | 25 ++ .../DataExchange/Internal/ExpandoHelpers.cs | 240 ++++++++++++++++ .../SmartStore.Services.csproj | 3 + .../Controllers/StoreClosedAttribute.cs | 22 +- .../Controllers/CommonController.cs | 40 ++- .../Themes/Alpha/Content/header.less | 4 + .../Themes/Alpha/Content/layout.less | 1 + .../Views/Home/StoreClosed.cshtml | 3 +- src/SmartStoreNET.Minimal.sln | 270 ++++++++++++++++++ 12 files changed, 608 insertions(+), 16 deletions(-) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs create mode 100644 src/SmartStoreNET.Minimal.sln diff --git a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs b/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs index 81c2ecb4f7..c696b77e66 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs @@ -112,6 +112,11 @@ protected virtual void Initialize(object instance) _instanceType = instance.GetType(); } + protected object WrappedObject + { + get { return _instance; } + } + IList InstancePropertyInfo { get diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 374cf79ba1..4cf5af7438 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -227,6 +227,15 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.Delete("Plugins.Widgets.OpenTrans.IsLexwareCompatibe"); builder.Delete("Admin.System.Maintenance.DeleteExportedFolders.TotalDeleted"); + + // Common + builder.AddOrUpdate("StoreClosed", + "We'll be back.", + "Wir sind bald wieder da."); + + builder.AddOrUpdate("StoreClosed.Hint", + "We're busy updating our online store for you and will be back soon.", + "Wir aktualisieren gerade das Angebot in unserem Online-Shop. Die Seite ist demnchst wieder verfgbar."); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index e6016546ef..d8bd7d7d4a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -52,6 +52,7 @@ using SmartStore.Utilities; using SmartStore.Utilities.Threading; using SmartStore.Core.Localization; +using SmartStore.Services.DataExchange.Internal; // note: namespace persisted in ScheduleTask.Type namespace SmartStore.Services.DataExchange.ExportTask @@ -62,6 +63,7 @@ public class ExportProfileTask : ITask #region Dependencies + private ExpandoHelpers _expandoHelpers; private ICommonServices _services; private Lazy _exportService; private Lazy _dataExchangeSettings; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs new file mode 100644 index 0000000000..565f9e00e5 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs @@ -0,0 +1,25 @@ +using SmartStore.ComponentModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Dynamic; + +namespace SmartStore.Services.DataExchange.Internal +{ + internal class ExpandoEntity : Expando + { + private readonly object _entity; + + public ExpandoEntity(object entity) + : base(entity) + { + } + + public object Entity + { + get { return base.WrappedObject; } + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs new file mode 100644 index 0000000000..d8da9e6f8c --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs @@ -0,0 +1,240 @@ +using SmartStore.ComponentModel; +using SmartStore.Core; +using SmartStore.Core.Domain.Common; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Domain.Stores; +using SmartStore.Services.DataExchange.ExportTask; +using SmartStore.Services.Localization; +using SmartStore.Services.Seo; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.DataExchange.Internal +{ + internal class ExpandoHelpers + { + private readonly ExportProfileTaskContext _ctx; + private readonly IUrlRecordService _urlRecordService; + private readonly ILocalizedEntityService _localizedEntityService; + + public ExpandoHelpers( + ExportProfileTaskContext context, + IUrlRecordService urlRecordService, + ILocalizedEntityService localizedEntityService) + { + _ctx = context; + _urlRecordService = urlRecordService; + _localizedEntityService = localizedEntityService; + } + + public dynamic ToExpando(Currency currency) + { + if (currency == null) + return null; + + dynamic expando = new ExpandoEntity(currency); + expando.Name = currency.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); + expando._Localized = GetLocalized(currency, x => x.Name); + + return expando; + } + + private dynamic ToExpando(Language language) + { + if (language == null) + return null; + + dynamic expando = new ExpandoEntity(language); + return expando; + } + + private dynamic ToExpando(Country country) + { + if (country == null) + return null; + + dynamic expando = new ExpandoEntity(country); + expando.Name = country.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); + expando._Localized = GetLocalized(country, x => x.Name); + + return expando; + } + + private dynamic ToExpando(Address address) + { + if (address == null) + return null; + + dynamic expando = new ExpandoEntity(address); + expando.Country = ToExpando(address.Country); + + if (address.StateProvinceId.GetValueOrDefault() > 0) + { + dynamic sp = new ExpandoEntity(address.StateProvince); + sp.Name = address.StateProvince.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); + sp._Localized = GetLocalized(address.StateProvince, x => x.Name); + expando.StateProvince = sp; + } + else + { + expando.StateProvince = null; + } + + return expando; + } + + private dynamic ToExpando(RewardPointsHistory points) + { + if (points == null) + return null; + + dynamic expando = new ExpandoEntity(points); + return expando; + } + + private dynamic ToExpando( Customer customer) + { + if (customer == null) + return null; + + dynamic expando = new ExpandoEntity(customer); + + expando.BillingAddress = null; + expando.ShippingAddress = null; + expando.Addresses = null; + expando.CustomerRoles = null; + + expando.RewardPointsHistory = null; + expando._RewardPointsBalance = 0; + + expando._GenericAttributes = null; + expando._HasNewsletterSubscription = false; + + return expando; + } + + private dynamic ToExpando(Store store) + { + if (store == null) + return null; + + dynamic expando = new ExpandoEntity(store); + expando.PrimaryStoreCurrency = ToExpando(store.PrimaryStoreCurrency); + expando.PrimaryExchangeRateCurrency = ToExpando(store.PrimaryExchangeRateCurrency); + + return expando; + } + + private dynamic ToExpando(DeliveryTime deliveryTime) + { + if (deliveryTime == null) + return null; + + dynamic expando = new ExpandoEntity(deliveryTime); + expando.Name = deliveryTime.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); + expando._Localized = GetLocalized(deliveryTime, x => x.Name); + + return expando; + } + + private dynamic ToExpando(QuantityUnit quantityUnit) + { + if (quantityUnit == null) + return null; + + dynamic expando = new ExpandoEntity(quantityUnit); + expando.Name = quantityUnit.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); + expando.Description = quantityUnit.GetLocalized(x => x.Description, _ctx.Projection.LanguageId ?? 0, true, false); + expando._Localized = GetLocalized(quantityUnit, + x => x.Name, + x => x.Description); + + return expando; + } + + private dynamic ToExpando(Picture picture, int thumbPictureSize, int detailsPictureSize) + { + if (picture == null) + return null; + + dynamic expando = new ExpandoEntity(picture); + + //// TODO!!!!! + //expando._ThumbImageUrl = _pictureService.Value.GetPictureUrl(picture, thumbPictureSize, false, _ctx.Store.Url); + //expando._ImageUrl = _pictureService.Value.GetPictureUrl(picture, detailsPictureSize, false, _ctx.Store.Url); + //expando._FullSizeImageUrl = _pictureService.Value.GetPictureUrl(picture, 0, false, _ctx.Store.Url); + + //var relativeUrl = _pictureService.Value.GetPictureUrl(picture); + //expando._FileName = relativeUrl.Substring(relativeUrl.LastIndexOf("/") + 1); + + //expando._ThumbLocalPath = _pictureService.Value.GetThumbLocalPath(picture); + + return expando; + } + + // TODO: weitermachen [...] + + private List GetLocalized(T entity, params Expression>[] keySelectors) + where T : BaseEntity, ILocalizedEntity + { + if (_ctx.Languages.Count <= 1) + return null; + + var localized = new List(); + + var localeKeyGroup = typeof(T).Name; + var isSlugSupported = typeof(ISlugSupported).IsAssignableFrom(typeof(T)); + + foreach (var language in _ctx.Languages) + { + var languageCulture = language.Value.LanguageCulture.EmptyNull().ToLower(); + + // add SeName + if (isSlugSupported) + { + var value = _urlRecordService.GetActiveSlug(entity.Id, localeKeyGroup, language.Value.Id); + if (value.HasValue()) + { + dynamic exp = new ExpandoObject(); + exp.Culture = languageCulture; + exp.LocaleKey = "SeName"; + exp.LocaleValue = value; + + localized.Add(exp); + } + } + + foreach (var keySelector in keySelectors) + { + var member = keySelector.Body as MemberExpression; + var propInfo = member.Member as PropertyInfo; + string localeKey = propInfo.Name; + var value = _localizedEntityService.GetLocalizedValue(language.Value.Id, entity.Id, localeKeyGroup, localeKey); + + // we better not export empty values. The risk is too high that they get reimported and unnecessarily pollute databases. + if (value.HasValue()) + { + dynamic exp = new ExpandoObject(); + exp.Culture = languageCulture; + exp.LocaleKey = localeKey; + exp.LocaleValue = value; + + localized.Add(exp); + } + } + } + + return (localized.Count == 0 ? null : localized); + } + } +} diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 3c3f321c24..b5d926c04e 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -185,6 +185,8 @@ + + @@ -572,6 +574,7 @@ Nop_Services_EuropaCheckVatService_checkVatService + diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/StoreClosedAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/StoreClosedAttribute.cs index b4cfa9c518..67cce55e92 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/StoreClosedAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/StoreClosedAttribute.cs @@ -7,6 +7,8 @@ using SmartStore.Core.Domain; using SmartStore.Core.Infrastructure; using SmartStore.Services.Customers; +using SmartStore.Services.Localization; +using SmartStore.Core.Logging; namespace SmartStore.Web.Framework.Controllers { @@ -20,7 +22,8 @@ public class StoreClosedAttribute : ActionFilterAttribute new Tuple("SmartStore.Web.Controllers.CommonController", "SetLanguage") }; - + public ILocalizationService Localizer { get; set; } + public Lazy Notifier { get; set; } public Lazy WorkContext { get; set; } public Lazy StoreInformationSettings { get; set; } @@ -60,8 +63,21 @@ public override void OnActionExecuting(ActionExecutingContext filterContext) } else { - var storeClosedUrl = new UrlHelper(filterContext.RequestContext).RouteUrl("StoreClosed"); - filterContext.Result = new RedirectResult(storeClosedUrl); + if (request.IsAjaxRequest()) + { + var storeClosedMessage = "{0} {1}".FormatCurrentUI( + Localizer.GetResource("StoreClosed", 0, false), + Localizer.GetResource("StoreClosed.Hint", 0, false)); + Notifier.Value.Error(storeClosedMessage); + + //filterContext.Result = new ContentResult { Content = "", ContentType = "text/html" }; + } + else + { + var storeClosedUrl = new UrlHelper(filterContext.RequestContext).RouteUrl("StoreClosed"); + filterContext.Result = new RedirectResult(storeClosedUrl); + } + } } } diff --git a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs index 4c411f9238..36d72e8a38 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs @@ -42,6 +42,7 @@ using SmartStore.Web.Framework.UI; using SmartStore.Web.Infrastructure.Cache; using SmartStore.Web.Models.Common; +using SmartStore.Core.Domain; namespace SmartStore.Web.Controllers { @@ -57,10 +58,12 @@ public partial class CommonController : PublicControllerBase private readonly Lazy _forumservice; private readonly Lazy _genericAttributeService; private readonly Lazy _mobileDeviceHelper; + private readonly Lazy _compareProductsService; private readonly static string[] s_hints = new string[] { "Shopsystem", "Onlineshop Software", "Shopsoftware", "E-Commerce Solution" }; - private readonly CustomerSettings _customerSettings; + private readonly StoreInformationSettings _storeInfoSettings; + private readonly CustomerSettings _customerSettings; private readonly TaxSettings _taxSettings; private readonly CatalogSettings _catalogSettings; private readonly ThemeSettings _themeSettings; @@ -70,8 +73,8 @@ public partial class CommonController : PublicControllerBase private readonly ForumSettings _forumSettings; private readonly LocalizationSettings _localizationSettings; private readonly Lazy _securitySettings; - - private readonly IOrderTotalCalculationService _orderTotalCalculationService; + private readonly IOrderTotalCalculationService _orderTotalCalculationService; + private readonly IPriceFormatter _priceFormatter; private readonly IPageAssetsBuilder _pageAssetsBuilder; private readonly Lazy _pictureService; @@ -90,7 +93,9 @@ public CommonController( Lazy forumService, Lazy genericAttributeService, Lazy mobileDeviceHelper, - CustomerSettings customerSettings, + Lazy compareProductsService, + StoreInformationSettings storeInfoSettings, + CustomerSettings customerSettings, TaxSettings taxSettings, CatalogSettings catalogSettings, EmailAccountSettings emailAccountSettings, @@ -115,8 +120,10 @@ public CommonController( this._forumservice = forumService; this._genericAttributeService = genericAttributeService; this._mobileDeviceHelper = mobileDeviceHelper; - - this._customerSettings = customerSettings; + this._compareProductsService = compareProductsService; + + this._storeInfoSettings = storeInfoSettings; + this._customerSettings = customerSettings; this._taxSettings = taxSettings; this._catalogSettings = catalogSettings; this._commonSettings = commonSettings; @@ -506,7 +513,18 @@ public ActionResult ShopBar() { var customer = _services.WorkContext.CurrentCustomer; - var unreadMessageCount = GetUnreadPrivateMessages(); + var isAdmin = customer.IsAdmin(); + var isRegistered = isAdmin || customer.IsRegistered(); + + if (_storeInfoSettings.StoreClosed) + { + if (!isAdmin || !_storeInfoSettings.StoreClosedAllowForAdmins) + { + return Content(""); + } + } + + var unreadMessageCount = GetUnreadPrivateMessages(); var unreadMessage = string.Empty; var alertMessage = string.Empty; if (unreadMessageCount > 0) @@ -542,8 +560,8 @@ public ActionResult ShopBar() } var model = new ShopBarModel { - IsAuthenticated = customer.IsRegistered(), - CustomerEmailUsername = customer.IsRegistered() ? (_customerSettings.UsernamesEnabled ? customer.Username : customer.Email) : "", + IsAuthenticated = isRegistered, + CustomerEmailUsername = isRegistered ? (_customerSettings.UsernamesEnabled ? customer.Username : customer.Email) : "", IsCustomerImpersonated = _services.WorkContext.OriginalCustomerIfImpersonated != null, DisplayAdminLink = _services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel), ShoppingCartEnabled = _services.Permissions.Authorize(StandardPermissionProvider.EnableShoppingCart), @@ -555,7 +573,7 @@ public ActionResult ShopBar() CompareProductsEnabled = _catalogSettings.CompareProductsEnabled }; - if (model.ShoppingCartEnabled || model.WishlistEnabled) + if (model.ShoppingCartEnabled || model.WishlistEnabled) { if (model.ShoppingCartEnabled) model.ShoppingCartItems = cart.GetTotalProducts(); @@ -566,7 +584,7 @@ public ActionResult ShopBar() if (_catalogSettings.CompareProductsEnabled) { - model.CompareItems = EngineContext.Current.Resolve().GetComparedProductsCount(); + model.CompareItems = _compareProductsService.Value.GetComparedProductsCount(); } return PartialView(model); diff --git a/src/Presentation/SmartStore.Web/Themes/Alpha/Content/header.less b/src/Presentation/SmartStore.Web/Themes/Alpha/Content/header.less index c783048936..012a75ee71 100644 --- a/src/Presentation/SmartStore.Web/Themes/Alpha/Content/header.less +++ b/src/Presentation/SmartStore.Web/Themes/Alpha/Content/header.less @@ -7,6 +7,10 @@ padding: 46px 0 0 0; } +.store-closed #header { + padding-top: 0; +} + #logobar { position: relative; padding: 10px 0; diff --git a/src/Presentation/SmartStore.Web/Themes/Alpha/Content/layout.less b/src/Presentation/SmartStore.Web/Themes/Alpha/Content/layout.less index ae6bc7a7fd..b6cb2e661c 100644 --- a/src/Presentation/SmartStore.Web/Themes/Alpha/Content/layout.less +++ b/src/Presentation/SmartStore.Web/Themes/Alpha/Content/layout.less @@ -11,6 +11,7 @@ #content-body { margin-top: 10px; padding-bottom: 10px; + min-height: 400px; } /* one column */ diff --git a/src/Presentation/SmartStore.Web/Views/Home/StoreClosed.cshtml b/src/Presentation/SmartStore.Web/Views/Home/StoreClosed.cshtml index e848f13ad2..f174ee3abe 100644 --- a/src/Presentation/SmartStore.Web/Views/Home/StoreClosed.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Home/StoreClosed.cshtml @@ -3,13 +3,12 @@ //title Html.AddTitleParts(T("PageTitle.StoreClosed").Text); + Html.AddBodyCssClass("store-closed"); }

@T("StoreClosed")

-
-
@T("StoreClosed.Hint")
diff --git a/src/SmartStoreNET.Minimal.sln b/src/SmartStoreNET.Minimal.sln new file mode 100644 index 0000000000..feeb737168 --- /dev/null +++ b/src/SmartStoreNET.Minimal.sln @@ -0,0 +1,270 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{7881B112-7843-4542-B1F7-F99553FB9BB7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Services", "Libraries\SmartStore.Services\SmartStore.Services.csproj", "{210541AD-F659-47DA-8763-16F36C5CD2F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Data", "Libraries\SmartStore.Data\SmartStore.Data.csproj", "{CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Core", "Libraries\SmartStore.Core\SmartStore.Core.csproj", "{6BDA8332-939F-45B7-A25E-7A797260AE59}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Web", "Presentation\SmartStore.Web\SmartStore.Web.csproj", "{4F1F649C-1020-45BE-A487-F416D9297FF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Admin", "Presentation\SmartStore.Web\Administration\SmartStore.Admin.csproj", "{152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Web.Framework", "Presentation\SmartStore.Web.Framework\SmartStore.Web.Framework.csproj", "{75FD4163-333C-4DD5-854D-2EF294E45D94}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E11A41CA-FA9F-4BB7-A35E-F80EE1084232}" + ProjectSection(SolutionItems) = preProject + AssemblySharedInfo.cs = AssemblySharedInfo.cs + AssemblyVersionInfo.cs = AssemblyVersionInfo.cs + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C0A5A9AE-1509-4795-82CD-0BE7D6A17C69}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.Config = .nuget\NuGet.Config + .nuget\NuGet.exe = .nuget\NuGet.exe + .nuget\NuGet.targets = .nuget\NuGet.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.OfflinePayment", "Plugins\SmartStore.OfflinePayment\SmartStore.OfflinePayment.csproj", "{692E9D31-1393-47BF-B372-63F671052F89}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Tax", "Plugins\SmartStore.Tax\SmartStore.Tax.csproj", "{94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.Shipping", "Plugins\SmartStore.Shipping\SmartStore.Shipping.csproj", "{ACC1E122-B2C8-4441-BDED-D4A77763331A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartStore.DevTools", "Plugins\SmartStore.DevTools\SmartStore.DevTools.csproj", "{542B9C12-E2A1-49BB-9134-0E3484F9D669}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + EFMigrations|Any CPU = EFMigrations|Any CPU + EFMigrations|Mixed Platforms = EFMigrations|Mixed Platforms + EFMigrations|x86 = EFMigrations|x86 + PluginDev|Any CPU = PluginDev|Any CPU + PluginDev|Mixed Platforms = PluginDev|Mixed Platforms + PluginDev|x86 = PluginDev|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Release|Any CPU.Build.0 = Release|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {210541AD-F659-47DA-8763-16F36C5CD2F4}.Release|x86.ActiveCfg = Release|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Release|Any CPU.Build.0 = Release|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CCD7F2C9-6A2C-4CF0-8E89-076B8FC0F144}.Release|x86.ActiveCfg = Release|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Debug|x86.ActiveCfg = Debug|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Release|Any CPU.Build.0 = Release|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6BDA8332-939F-45B7-A25E-7A797260AE59}.Release|x86.ActiveCfg = Release|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Release|Any CPU.Build.0 = Release|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4F1F649C-1020-45BE-A487-F416D9297FF3}.Release|x86.ActiveCfg = Release|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Debug|x86.ActiveCfg = Debug|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Release|Any CPU.Build.0 = Release|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {152C761A-DD2E-4C1F-AF89-DFB2547A3BCA}.Release|x86.ActiveCfg = Release|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Debug|x86.ActiveCfg = Debug|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Release|Any CPU.Build.0 = Release|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {75FD4163-333C-4DD5-854D-2EF294E45D94}.Release|x86.ActiveCfg = Release|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Debug|x86.ActiveCfg = Debug|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Release|Any CPU.Build.0 = Release|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {692E9D31-1393-47BF-B372-63F671052F89}.Release|x86.ActiveCfg = Release|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Debug|x86.ActiveCfg = Debug|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Release|Any CPU.Build.0 = Release|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2}.Release|x86.ActiveCfg = Release|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Debug|x86.ActiveCfg = Debug|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Release|Any CPU.Build.0 = Release|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {ACC1E122-B2C8-4441-BDED-D4A77763331A}.Release|x86.ActiveCfg = Release|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Debug|Any CPU.Build.0 = Debug|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Debug|x86.ActiveCfg = Debug|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.EFMigrations|Any CPU.ActiveCfg = EFMigrations|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.EFMigrations|Any CPU.Build.0 = EFMigrations|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.EFMigrations|Mixed Platforms.ActiveCfg = EFMigrations|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.EFMigrations|Mixed Platforms.Build.0 = EFMigrations|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.EFMigrations|x86.ActiveCfg = EFMigrations|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.PluginDev|Any CPU.ActiveCfg = PluginDev|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.PluginDev|Any CPU.Build.0 = PluginDev|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.PluginDev|Mixed Platforms.ActiveCfg = PluginDev|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.PluginDev|Mixed Platforms.Build.0 = PluginDev|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.PluginDev|x86.ActiveCfg = PluginDev|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Release|Any CPU.ActiveCfg = Release|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Release|Any CPU.Build.0 = Release|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {542B9C12-E2A1-49BB-9134-0E3484F9D669}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {692E9D31-1393-47BF-B372-63F671052F89} = {7881B112-7843-4542-B1F7-F99553FB9BB7} + {94D1BEEB-64A3-4EB6-9017-D66AFAF4F2B2} = {7881B112-7843-4542-B1F7-F99553FB9BB7} + {ACC1E122-B2C8-4441-BDED-D4A77763331A} = {7881B112-7843-4542-B1F7-F99553FB9BB7} + {542B9C12-E2A1-49BB-9134-0E3484F9D669} = {7881B112-7843-4542-B1F7-F99553FB9BB7} + EndGlobalSection + GlobalSection(NDepend) = preSolution + Project = ".\SmartStoreNET.ndproj" + EndGlobalSection +EndGlobal From 9de2317cb37303ace68b9b357acaf98722423dcd Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 5 Nov 2015 18:41:41 +0100 Subject: [PATCH 032/732] Web-API: Added OData action to add a shipment to an order and to set it as shipped --- .../Orders/IOrderProcessingService.cs | 9 ++ .../Orders/OrderProcessingService.cs | 72 ++++++++++++++ .../Controllers/OData/OrdersController.cs | 25 +++++ .../WebApiConfigurationProvider.cs | 6 ++ src/Plugins/SmartStore.WebApi/changelog.md | 1 + .../WebApi/OData/WebApiQueryableAttribute.cs | 13 ++- .../WebApi/WebApiEntityController.cs | 17 ++++ .../WebApi/WebApiExtension.cs | 2 + .../Controllers/OrderController.cs | 95 +++++-------------- 9 files changed, 166 insertions(+), 74 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs b/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs index 9452f42b7b..9c3f825b03 100644 --- a/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs @@ -266,5 +266,14 @@ public partial interface IOrderProcessingService /// Shopping cart /// true - OK; false - minimum order total amount is not reached bool ValidateMinOrderTotalAmount(IList cart); + + /// + /// Adds a shipment to an order + /// + /// Order + /// Tracking number + /// Quantities by order item identifiers. null to use the remaining total number of products for each order item. + /// New shipment, null if no shipment was added + Shipment AddShipment(Order order, string trackingNumber, Dictionary quantities); } } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs b/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs index 4995d1bd38..7c0e0f4bf6 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs @@ -2736,6 +2736,78 @@ public virtual bool ValidateMinOrderTotalAmount(IList return true; } + public virtual Shipment AddShipment(Order order, string trackingNumber, Dictionary quantities) + { + Guard.ArgumentNotNull(() => order); + + Shipment shipment = null; + decimal? totalWeight = null; + + foreach (var orderItem in order.OrderItems) + { + if (!orderItem.Product.IsShipEnabled) + continue; + + //ensure that this product can be shipped (have at least one item to ship) + var maxQtyToAdd = orderItem.GetTotalNumberOfItemsCanBeAddedToShipment(); + if (maxQtyToAdd <= 0) + continue; + + var qtyToAdd = 0; + + if (quantities != null && quantities.ContainsKey(orderItem.Id)) + qtyToAdd = quantities[orderItem.Id]; + else if (quantities == null) + qtyToAdd = maxQtyToAdd; + + if (qtyToAdd <= 0) + continue; + + if (qtyToAdd > maxQtyToAdd) + qtyToAdd = maxQtyToAdd; + + var orderItemTotalWeight = orderItem.ItemWeight.HasValue ? orderItem.ItemWeight * qtyToAdd : null; + if (orderItemTotalWeight.HasValue) + { + if (!totalWeight.HasValue) + totalWeight = 0; + totalWeight += orderItemTotalWeight.Value; + } + + if (shipment == null) + { + shipment = new Shipment + { + OrderId = order.Id, + Order = order, // otherwise order updated event would not be fired during InsertShipment + TrackingNumber = trackingNumber, + TotalWeight = null, + ShippedDateUtc = null, + DeliveryDateUtc = null, + CreatedOnUtc = DateTime.UtcNow, + }; + } + + var shipmentItem = new ShipmentItem + { + OrderItemId = orderItem.Id, + Quantity = qtyToAdd + }; + + shipment.ShipmentItems.Add(shipmentItem); + } + + if (shipment != null && shipment.ShipmentItems.Count > 0) + { + shipment.TotalWeight = totalWeight; + _shipmentService.InsertShipment(shipment); + + return shipment; + } + + return null; + } + #endregion } } diff --git a/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs b/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs index d633274612..2cf72507b8 100644 --- a/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs +++ b/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs @@ -168,5 +168,30 @@ public Order Cancel(int key) }); return entity; } + + [HttpPost] + public SingleResult AddShipment(int key, ODataActionParameters parameters) + { + var result = GetSingleResult(key); + var order = GetExpandedEntity(key, result, "OrderItems, OrderItems.Product, Shipments, Shipments.ShipmentItems"); + + this.ProcessEntity(() => + { + if (order.HasItemsToAddToShipment()) + { + var trackingNumber = parameters.GetValue("TrackingNumber"); + + var shipment = _orderProcessingService.Value.AddShipment(order, trackingNumber, null); + + if (shipment != null) + { + if (parameters.ContainsKey("SetAsShipped") && parameters.GetValue("SetAsShipped")) + _orderProcessingService.Value.Ship(shipment, true); + } + } + return null; + }); + return result; + } } } diff --git a/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs index db3d1f3377..7440806c04 100644 --- a/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs +++ b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs @@ -35,6 +35,12 @@ private void AddActionsToOrder(EntityTypeConfiguration config) config.Action("Cancel") .ReturnsFromEntitySet(WebApiOdataEntitySet.Orders); + + var addShipment = config.Action("AddShipment") + .ReturnsFromEntitySet(WebApiOdataEntitySet.Orders); + + addShipment.Parameter("TrackingNumber"); + addShipment.Parameter("SetAsShipped"); } private void AddActionsToProduct(EntityTypeConfiguration config) diff --git a/src/Plugins/SmartStore.WebApi/changelog.md b/src/Plugins/SmartStore.WebApi/changelog.md index 2191a7d055..c844fe4dbb 100644 --- a/src/Plugins/SmartStore.WebApi/changelog.md +++ b/src/Plugins/SmartStore.WebApi/changelog.md @@ -3,6 +3,7 @@ ##Web Api 2.2.0.4 ###New Features * Added OData endpoint for shipment items +* Added OData action to add a shipment to an order and to set it as shipped ##Web Api 2.2.0.3 ###New Features diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/OData/WebApiQueryableAttribute.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/OData/WebApiQueryableAttribute.cs index f3487d624a..b71ce1700a 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/OData/WebApiQueryableAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/OData/WebApiQueryableAttribute.cs @@ -17,11 +17,16 @@ protected virtual bool MissingClientPaging(HttpActionExecutedContext actionExecu try { - var responseContent = actionExecutedContext.Response.Content as ObjectContent; - bool singleResult = (responseContent != null && responseContent.Value is SingleResult); + var content = actionExecutedContext.Response.Content as ObjectContent; - if (singleResult) - return false; // 'true' would result in a 500 'internal server error' + if (content != null) + { + if (content.Value is HttpError) + return false; + + if (content.Value is SingleResult) + return false; // 'true' would result in a 500 'internal server error' + } var query = actionExecutedContext.Request.RequestUri.Query; diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs index 894b9d9400..4354426a9f 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs @@ -258,6 +258,23 @@ protected internal virtual TEntity GetExpandedEntity(int key, string properties) return entity; } + protected internal virtual TEntity GetExpandedEntity(int key, SingleResult result, string path) + { + var query = result.Queryable; + + foreach (var property in path.SplitSafe(",")) + { + query = query.Expand(property.Trim()); + } + + var entity = query.FirstOrDefault(x => x.Id == key); + + if (entity == null) + throw ExceptionEntityNotFound(key); + + return entity; + } + protected internal virtual TProperty GetExpandedProperty(int key, Expression> path) { var entity = GetExpandedEntity(key, path); diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs index 9051894b20..9b9c41a1c4 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiExtension.cs @@ -1,10 +1,12 @@ using System; +using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.Http; using System.Reflection; using System.Web.Http; using System.Web.Http.OData.Routing; +using SmartStore.Core; using SmartStore.Utilities; namespace SmartStore.Web.Framework.WebApi diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs index 186f1905b3..f2a096e559 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs @@ -2119,84 +2119,39 @@ public ActionResult AddShipment(int orderId, FormCollection form, bool continueE var order = _orderService.GetOrderById(orderId); if (order == null) - //No order found with the specified id return RedirectToAction("List"); - Shipment shipment = null; - - decimal? totalWeight = null; - foreach (var orderItem in order.OrderItems) - { - //is shippable - if (!orderItem.Product.IsShipEnabled) - continue; - - //ensure that this product can be shipped (have at least one item to ship) - var maxQtyToAdd = orderItem.GetTotalNumberOfItemsCanBeAddedToShipment(); - if (maxQtyToAdd <= 0) - continue; - - int qtyToAdd = 0; //parse quantity - foreach (string formKey in form.AllKeys) - if (formKey.Equals(string.Format("qtyToAdd{0}", orderItem.Id), StringComparison.InvariantCultureIgnoreCase)) - { - int.TryParse(form[formKey], out qtyToAdd); - break; - } - - //validate quantity - if (qtyToAdd <= 0) - continue; - if (qtyToAdd > maxQtyToAdd) - qtyToAdd = maxQtyToAdd; - - //ok. we have at least one item. let's create a shipment (if it does not exist) + var quantities = new Dictionary(); + var trackingNumber = form["TrackingNumber"]; - var orderItemTotalWeight = orderItem.ItemWeight.HasValue ? orderItem.ItemWeight * qtyToAdd : null; - if (orderItemTotalWeight.HasValue) - { - if (!totalWeight.HasValue) - totalWeight = 0; - totalWeight += orderItemTotalWeight.Value; - } + foreach (var orderItem in order.OrderItems) + { + foreach (string formKey in form.AllKeys) + { + if (formKey.Equals(string.Format("qtyToAdd{0}", orderItem.Id), StringComparison.InvariantCultureIgnoreCase)) + { + quantities.Add(orderItem.Id, form[formKey].ToInt()); + break; + } + } + } - if (shipment == null) - { - shipment = new Shipment() - { - OrderId = order.Id, - TrackingNumber = form["TrackingNumber"], - TotalWeight = null, - ShippedDateUtc = null, - DeliveryDateUtc = null, - CreatedOnUtc = DateTime.UtcNow, - }; - } - //create a shipment item - var shipmentItem = new ShipmentItem() - { - OrderItemId = orderItem.Id, - Quantity = qtyToAdd, - }; - shipment.ShipmentItems.Add(shipmentItem); - } + var shipment = _orderProcessingService.AddShipment(order, trackingNumber, quantities); - //if we have at least one item in the shipment, then save it - if (shipment != null && shipment.ShipmentItems.Count > 0) - { - shipment.TotalWeight = totalWeight; - _shipmentService.InsertShipment(shipment); + if (shipment != null) + { + NotifySuccess(_localizationService.GetResource("Admin.Orders.Shipments.Added")); - NotifySuccess(_localizationService.GetResource("Admin.Orders.Shipments.Added")); - return continueEditing - ? RedirectToAction("ShipmentDetails", new {id = shipment.Id}) - : RedirectToAction("Edit", new { id = orderId }); - } - else - { + return continueEditing + ? RedirectToAction("ShipmentDetails", new { id = shipment.Id }) + : RedirectToAction("Edit", new { id = orderId }); + } + else + { NotifyError(_localizationService.GetResource("Admin.Orders.Shipments.NoProductsSelected")); + return RedirectToAction("AddShipment", new { orderId = orderId }); - } + } } public ActionResult ShipmentDetails(int id) From 2abf201e18d5042ba8b134178f70079d61bf7e4b Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 5 Nov 2015 21:48:11 +0100 Subject: [PATCH 033/732] Web-API: OData actions should return SingleResult (instead of entity instance) to let expand option be recognized Fixed "Member 'CurrentValues' cannot be called for the entity of type 'TEntity' because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet." --- .../SmartStore.Data/ObjectContextBase.cs | 18 ++++--- .../Controllers/OData/OrdersController.cs | 49 +++++++++++-------- src/Plugins/SmartStore.WebApi/changelog.md | 2 + 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.cs index 5fff7f7588..5f722e155f 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.cs @@ -333,12 +333,18 @@ public IDictionary GetModifiedProperties(BaseEntity entity) var props = new Dictionary(); var entry = this.Entry(entity); - var modifiedPropertyNames = from p in entry.CurrentValues.PropertyNames - where entry.Property(p).IsModified - select p; - foreach (var name in modifiedPropertyNames) + + // be aware of the entity state. you cannot get modified properties for detached entities. + if (entry.State != System.Data.Entity.EntityState.Detached) { - props.Add(name, entry.Property(name).OriginalValue); + var modifiedPropertyNames = from p in entry.CurrentValues.PropertyNames + where entry.Property(p).IsModified + select p; + + foreach (var name in modifiedPropertyNames) + { + props.Add(name, entry.Property(name).OriginalValue); + } } return props; @@ -381,7 +387,7 @@ public override Task SaveChangesAsync() return result; } - // codehint: sm-add (required for UoW implementation) + // required for UoW implementation public string Alias { get; internal set; } // performance on bulk inserts diff --git a/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs b/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs index 2cf72507b8..f0f1c0eb9b 100644 --- a/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs +++ b/src/Plugins/SmartStore.WebApi/Controllers/OData/OrdersController.cs @@ -95,24 +95,26 @@ public IQueryable GetOrderItems(int key) // actions [HttpPost] - public Order PaymentPending(int key) + public SingleResult PaymentPending(int key) { - var entity = GetEntityByKeyNotNull(key); + var result = GetSingleResult(key); + var order = GetExpandedEntity(key, result, null); this.ProcessEntity(() => { - entity.PaymentStatus = PaymentStatus.Pending; - Service.UpdateOrder(entity); + order.PaymentStatus = PaymentStatus.Pending; + Service.UpdateOrder(order); return null; }); - return entity; + return result; } [HttpPost] - public Order PaymentPaid(int key, ODataActionParameters parameters) + public SingleResult PaymentPaid(int key, ODataActionParameters parameters) { - var entity = GetEntityByKeyNotNull(key); + var result = GetSingleResult(key); + var order = GetExpandedEntity(key, result, null); this.ProcessEntity(() => { @@ -120,20 +122,23 @@ public Order PaymentPaid(int key, ODataActionParameters parameters) if (paymentMethodName != null) { - entity.PaymentMethodSystemName = paymentMethodName; - Service.UpdateOrder(entity); + order.PaymentMethodSystemName = paymentMethodName; + Service.UpdateOrder(order); } - _orderProcessingService.Value.MarkOrderAsPaid(entity); + _orderProcessingService.Value.MarkOrderAsPaid(order); + return null; }); - return entity; + + return result; } [HttpPost] - public Order PaymentRefund(int key, ODataActionParameters parameters) + public SingleResult PaymentRefund(int key, ODataActionParameters parameters) { - var entity = GetEntityByKeyNotNull(key); + var result = GetSingleResult(key); + var order = GetExpandedEntity(key, result, null); this.ProcessEntity(() => { @@ -141,32 +146,35 @@ public Order PaymentRefund(int key, ODataActionParameters parameters) if (online) { - var errors = _orderProcessingService.Value.Refund(entity); + var errors = _orderProcessingService.Value.Refund(order); if (errors.Count > 0) return errors[0]; } else { - _orderProcessingService.Value.RefundOffline(entity); + _orderProcessingService.Value.RefundOffline(order); } return null; }); - return entity; + + return result; } [HttpPost] - public Order Cancel(int key) + public SingleResult Cancel(int key) { - var entity = GetEntityByKeyNotNull(key); + var result = GetSingleResult(key); + var order = GetExpandedEntity(key, result, "OrderItems, OrderItems.Product"); this.ProcessEntity(() => { - _orderProcessingService.Value.CancelOrder(entity, true); + _orderProcessingService.Value.CancelOrder(order, true); return null; }); - return entity; + + return result; } [HttpPost] @@ -191,6 +199,7 @@ public SingleResult AddShipment(int key, ODataActionParameters parameters } return null; }); + return result; } } diff --git a/src/Plugins/SmartStore.WebApi/changelog.md b/src/Plugins/SmartStore.WebApi/changelog.md index c844fe4dbb..23143ca0fc 100644 --- a/src/Plugins/SmartStore.WebApi/changelog.md +++ b/src/Plugins/SmartStore.WebApi/changelog.md @@ -4,6 +4,8 @@ ###New Features * Added OData endpoint for shipment items * Added OData action to add a shipment to an order and to set it as shipped +###Improvements +* OData actions should return SingleResult (instead of entity instance) to let expand option be recognized ##Web Api 2.2.0.3 ###New Features From 0fee2bbc2539a43a975bcadaab5ae49b7a52b73b Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 5 Nov 2015 23:21:01 +0100 Subject: [PATCH 034/732] Export framework: refactoring --- .../SmartStore.Core/ComponentModel/Expando.cs | 279 +++++++++--------- .../ComponentModel/PropertyBag.cs | 3 +- .../Extensions/TypeExtensions.cs | 18 +- .../SmartStore.Core/Utilities/TypeHelper.cs | 1 + ...portService.cs => ExportProfileService.cs} | 4 +- .../DataExchange/ExportSegmenter.cs | 4 +- .../ExportTask/ExportProfileTask.cs | 16 +- .../DataExchange/IDataExporter.cs | 48 +++ ...ortService.cs => IExportProfileService.cs} | 2 +- .../DataExchange/Internal/DynamicEntity.cs | 53 ++++ .../DataExchange/Internal/ExpandoEntity.cs | 25 -- .../DataExchange/Internal/ExpandoHelpers.cs | 22 +- .../SmartStore.Services.csproj | 7 +- .../Tasks/TaskExecutionContext.cs | 30 +- .../FeedGoogleMerchantCenterController.cs | 10 +- ...XmlProvider.cs => GmcExportXmlProvider.cs} | 4 +- .../SmartStore.GoogleMerchantCenter.csproj | 3 +- .../DependencyRegistrar.cs | 2 +- .../Controllers/ExportController.cs | 4 +- 19 files changed, 293 insertions(+), 242 deletions(-) rename src/Libraries/SmartStore.Services/DataExchange/{ExportService.cs => ExportProfileService.cs} (98%) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs rename src/Libraries/SmartStore.Services/DataExchange/{IExportService.cs => IExportProfileService.cs} (98%) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs delete mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs rename src/Plugins/SmartStore.GoogleMerchantCenter/Providers/{ProductExportXmlProvider.cs => GmcExportXmlProvider.cs} (99%) diff --git a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs b/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs index c696b77e66..c97e82ed75 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs @@ -36,7 +36,6 @@ using System.Linq; using System.Dynamic; using System.Reflection; -using Fasterflect; namespace SmartStore.ComponentModel { @@ -59,7 +58,7 @@ namespace SmartStore.ComponentModel /// Dictionary: Any of the extended properties are accessible via IDictionary interface /// [Serializable] - public class Expando : DynamicObject, IDynamicMetaObjectProvider + public class Expando : DynamicObject { /// /// Instance of object passed in @@ -71,7 +70,7 @@ public class Expando : DynamicObject, IDynamicMetaObjectProvider /// private Type _instanceType; - private IList _instancePropertyInfo; + private IList _instancePropertyInfos; /// /// String Dictionary that contains the extra dynamic values @@ -104,7 +103,6 @@ public Expando(object instance) Initialize(instance); } - protected virtual void Initialize(object instance) { _instance = instance; @@ -117,13 +115,13 @@ protected object WrappedObject get { return _instance; } } - IList InstancePropertyInfo + IList InstancePropertyInfos { get { - if (_instancePropertyInfo == null && _instance != null) - _instancePropertyInfo = _instance.GetType().Properties(Flags.InstancePublicDeclaredOnly); - return _instancePropertyInfo; + if (_instancePropertyInfos == null && _instance != null) + _instancePropertyInfos = Fasterflect.PropertyExtensions.Properties(_instance.GetType(), Fasterflect.Flags.InstancePublicDeclaredOnly); + return _instancePropertyInfos; } } @@ -143,68 +141,76 @@ public override IEnumerable GetDynamicMemberNames() /// public override bool TryGetMember(GetMemberBinder binder, out object result) { - result = null; - - // first check the Properties collection for member - if (Properties.Keys.Contains(binder.Name)) - { - result = Properties[binder.Name]; - return true; - } - - - // Next check for Public properties via Reflection - if (_instance != null) - { - try - { - return GetProperty(_instance, binder.Name, out result); - } - catch { } - } - - // failed to retrieve a property - result = null; - return false; + return TryGetMemberCore(binder.Name, out result); } + protected virtual bool TryGetMemberCore(string name, out object result) + { + result = null; + + // first check the Properties collection for member + if (Properties.Keys.Contains(name)) + { + result = Properties[name]; + return true; + } + + // Next check for public properties via Reflection + if (_instance != null) + { + try + { + return GetProperty(_instance, name, out result); + } + catch { } + } + + // failed to retrieve a property + result = null; + return false; + } - /// - /// Property setter implementation tries to retrieve value from instance - /// first then into this object - /// - /// - /// - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - - // first check to see if there's a native property to set - if (_instance != null) - { - try - { - bool result = SetProperty(_instance, binder.Name, value); - if (result) - return true; - } - catch { } - } - // no match - set or add to dictionary - Properties[binder.Name] = value; - return true; + /// + /// Property setter implementation tries to retrieve value from instance + /// first then into this object + /// + /// + /// + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return TrySetMemberCore(binder.Name, value); } - /// - /// Dynamic invocation method. Currently allows only for Reflection based - /// operation (no ability to add methods dynamically). - /// - /// - /// - /// - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + protected virtual bool TrySetMemberCore(string name, object value) + { + // first check to see if there's a native property to set + if (_instance != null) + { + try + { + bool result = SetProperty(_instance, name, value); + if (result) + return true; + } + catch { } + } + + // no match - set or add to dictionary + Properties[name] = value; + return true; + } + + /// + /// Dynamic invocation method. Currently allows only for Reflection based + /// operation (no ability to add methods dynamically). + /// + /// + /// + /// + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (_instance != null) { @@ -231,13 +237,10 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o /// protected bool GetProperty(object instance, string name, out object result) { - if (instance == null) - instance = this; - - var pi = _instanceType.Property(name, Flags.InstancePublic); + var pi = GetPropertyInfo(name); if (pi != null) { - result = instance.GetPropertyValue(pi.Name); + result = Fasterflect.PropertyExtensions.GetPropertyValue(instance ?? this, pi.Name); return true; } @@ -245,6 +248,16 @@ protected bool GetProperty(object instance, string name, out object result) return false; } + /// + /// Gets a PropertyInfo instance from the wrapped object + /// + /// Property name + protected PropertyInfo GetPropertyInfo(string name) + { + var pi = Fasterflect.PropertyExtensions.Property(_instanceType, name, Fasterflect.Flags.InstancePublic); + return pi; + } + /// /// Reflection helper method to set a property value /// @@ -254,13 +267,10 @@ protected bool GetProperty(object instance, string name, out object result) /// protected bool SetProperty(object instance, string name, object value) { - if (instance == null) - instance = this; - - var pi = _instanceType.Property(name, Flags.InstancePublic); + var pi = GetPropertyInfo(name); if (pi != null) { - instance.SetPropertyValue(pi.Name, value); + Fasterflect.PropertyInfoExtensions.Set(pi, instance ?? this, value); return true; } return false; @@ -276,16 +286,12 @@ protected bool SetProperty(object instance, string name, object value) /// protected bool InvokeMethod(object instance, string name, object[] args, out object result) { - if (instance == null) - instance = this; - // Look at the instanceType - var mi = _instanceType.Method(name, Flags.InstancePublic); - + var mi = Fasterflect.MethodExtensions.Method(_instanceType, name, Fasterflect.Flags.InstancePublic); if (mi != null) { - result = _instance.CallMethod(mi.Name, args); - return true; + result = Fasterflect.MethodInfoExtensions.Call(mi, instance ?? this, args); + return true; } result = null; @@ -293,73 +299,54 @@ protected bool InvokeMethod(object instance, string name, object[] args, out obj } - /// - /// Convenience method that provides a string Indexer - /// to the Properties collection AND the strongly typed - /// properties of the object by name. - /// - /// // dynamic - /// exp["Address"] = "112 nowhere lane"; - /// // strong - /// var name = exp["StronglyTypedProperty"] as string; - /// - /// - /// The getter checks the Properties dictionary first - /// then looks in PropertyInfo for properties. - /// The setter checks the instance properties before - /// checking the Properties dictionary. - /// - /// - /// - /// - public object this[string key] - { - get - { - try - { - // try to get from properties collection first - return Properties[key]; - } - catch (KeyNotFoundException) - { - // try reflection on instanceType - object result = null; - if (GetProperty(_instance, key, out result)) - return result; - - // no doesn't exist - throw; - } - } - set - { - if (Properties.ContainsKey(key)) - { - Properties[key] = value; - return; - } - - // check instance for existance of type first - var pi = _instanceType.Property(key, Flags.Public); - if (pi != null) - SetProperty(_instance, key, value); - else - Properties[key] = value; - } - } + /// + /// Convenience method that provides a string Indexer + /// to the Properties collection AND the strongly typed + /// properties of the object by name. + /// + /// // dynamic + /// exp["Address"] = "112 nowhere lane"; + /// // strong + /// var name = exp["StronglyTypedProperty"] as string; + /// + /// + /// The getter checks the Properties dictionary first + /// then looks in PropertyInfo for properties. + /// The setter checks the instance properties before + /// checking the Properties dictionary. + /// + /// + /// + /// + public object this[string key] + { + get + { + object result = null; + if (TryGetMemberCore(key, out result)) + { + return result; + } + + throw new KeyNotFoundException(); + } + set + { + TrySetMemberCore(key, value); + } + } - /// - /// Returns and the properties of - /// - /// - /// - public IEnumerable> GetProperties(bool includeInstanceProperties = false) + /// + /// Returns all properties + /// + /// + /// + public IEnumerable> GetProperties(bool includeInstanceProperties = false) { if (includeInstanceProperties && _instance != null) { - foreach (var prop in this.InstancePropertyInfo) + foreach (var prop in this.InstancePropertyInfos) yield return new KeyValuePair(prop.Name, prop.GetValue(_instance, null)); } @@ -388,13 +375,13 @@ public bool Contains(KeyValuePair item, bool includeInstanceProp /// public bool Contains(string propertyName, bool includeInstanceProperties = false) { - bool res = Properties.ContainsKey(propertyName); - if (res) + bool contains = Properties.ContainsKey(propertyName); + if (contains) return true; if (includeInstanceProperties && _instance != null) { - foreach (var prop in this.InstancePropertyInfo) + foreach (var prop in this.InstancePropertyInfos) { if (prop.Name == propertyName) return true; diff --git a/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs b/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs index fb2ab77767..d67fbb85b3 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs @@ -306,8 +306,7 @@ public bool FromXml(string xml) if (string.IsNullOrEmpty(xml)) return true; - var result = SerializationUtils.DeSerializeObject(xml, - this.GetType()) as PropertyBag; + var result = SerializationUtils.DeSerializeObject(xml, this.GetType()) as PropertyBag; if (result != null) { foreach (var item in result) diff --git a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs index 69752367c2..d2ca8d9df9 100644 --- a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs @@ -45,15 +45,8 @@ public static bool IsPredefinedSimpleType(this Type type) { return true; } + return s_predefinedTypes.Any(t => t == type); - //foreach (Type type2 in s_predefinedTypes) - //{ - // if (type2 == type) - // { - // return true; - // } - //} - //return false; } public static bool IsStruct(this Type type) @@ -75,15 +68,8 @@ public static bool IsPredefinedGenericType(this Type type) { return false; } + return s_predefinedGenericTypes.Any(t => t == type); - //foreach (Type type2 in s_predefinedGenericTypes) - //{ - // if (type2 == type) - // { - // return true; - // } - //} - //return false; } public static bool IsPredefinedType(this Type type) diff --git a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs index a887d720c1..34dbf27b18 100644 --- a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs @@ -61,6 +61,7 @@ public static Type GetElementType(Type type) return GetElementType(type3); } } + return type; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs similarity index 98% rename from src/Libraries/SmartStore.Services/DataExchange/ExportService.cs rename to src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index aea03c6fa9..dac3287770 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange { - public partial class ExportService : IExportService + public partial class ExportProfileService : IExportProfileService { private const string _defaultFileNamePattern = "%Store.Id%-%ExportProfile.Id%-%Misc.FileNumber%-%ExportProfile.SeoName%"; @@ -28,7 +28,7 @@ public partial class ExportService : IExportService private readonly DataExchangeSettings _dataExchangeSettings; private readonly ILocalizationService _localizationService; - public ExportService( + public ExportProfileService( IRepository exportProfileRepository, IRepository exportDeploymentRepository, IEventPublisher eventPublisher, diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs index b86df2cb45..6d5c6fb937 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs @@ -9,7 +9,7 @@ public interface IExportSegmenterConsumer /// /// Total number of records /// - int RecordTotal { get; } + int TotalRecords { get; } /// /// Gets current data segment @@ -78,7 +78,7 @@ public ExportSegmenter( /// /// Total number of records /// - public int RecordTotal + public int TotalRecords { get { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index d8bd7d7d4a..e4e9d02f8f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -65,7 +65,7 @@ public class ExportProfileTask : ITask private ExpandoHelpers _expandoHelpers; private ICommonServices _services; - private Lazy _exportService; + private Lazy _exportProfileService; private Lazy _dataExchangeSettings; private Lazy _productService; private Lazy _pictureService; @@ -101,7 +101,7 @@ public class ExportProfileTask : ITask private void InitDependencies(TaskExecutionContext context) { _services = context.Resolve(); - _exportService = context.Resolve>(); + _exportProfileService = context.Resolve>(); _dataExchangeSettings = context.Resolve>(); _productService = context.Resolve>(); _pictureService = context.Resolve>(); @@ -2546,7 +2546,7 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) throw new SmartException("Unsupported entity type '{0}'".FormatInvariant(ctx.Provider.Value.EntityType.ToString())); } - if (segmenter.RecordTotal <= 0) + if (segmenter.TotalRecords <= 0) { ctx.Log.Information("There are no records to export"); } @@ -2735,7 +2735,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) { ctx.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); - _exportService.Value.UpdateExportProfile(ctx.Profile); + _exportProfileService.Value.UpdateExportProfile(ctx.Profile); } } catch { } @@ -2882,12 +2882,12 @@ public void Execute(TaskExecutionContext taskContext) InitDependencies(taskContext); var profileId = taskContext.ScheduleTask.Alias.ToInt(); - var profile = _exportService.Value.GetExportProfileById(profileId); + var profile = _exportProfileService.Value.GetExportProfileById(profileId); var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); var selectedEntityIds = HttpRuntime.Cache[selectedIdsCacheKey] as string; - var provider = _exportService.Value.LoadProvider(profile.ProviderSystemName); + var provider = _exportProfileService.Value.LoadProvider(profile.ProviderSystemName); if (provider == null) throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); @@ -2929,13 +2929,13 @@ public ExportExecuteResult Execute( InitDependencies(taskContext); - var provider = _exportService.Value.LoadProvider(providerSystemName); + var provider = _exportProfileService.Value.LoadProvider(providerSystemName); if (provider == null) throw new SmartException(T("Admin.Common.ProviderNotLoaded", providerSystemName.NaIfEmpty())); if (profile == null) - profile = _exportService.Value.CreateVolatileProfile(provider); + profile = _exportProfileService.Value.CreateVolatileProfile(provider); var ctx = new ExportProfileTaskContext(taskContext, profile, provider, selectedEntityIds); ctx.QueryProducts = queryProducts; diff --git a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs new file mode 100644 index 0000000000..a2a31ac863 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs @@ -0,0 +1,48 @@ +using SmartStore.Core.Domain; +using SmartStore.Core.Plugins; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace SmartStore.Services.DataExchange +{ + public delegate void ProgressSetter(int value, int maximum, string message); + + public interface IDataExporter + { + void Export(DataExportRequest request, CancellationToken cancellationToken); + } + + public class DataExportRequest + { + private readonly static ProgressSetter _voidProgressSetter = DataExportRequest.SetProgress; + + public DataExportRequest(ExportProfile profile) + { + Guard.ArgumentNotNull(() => profile); + + CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); + ProgressSetter = _voidProgressSetter; + Profile = profile; + } + + public ExportProfile Profile { get; private set; } + + public Provider Provider { get; set; } + + public IEnumerable EntitiesToExport { get; set; } + + public ProgressSetter ProgressSetter { get; set; } + + public IDictionary CustomData { get; private set; } + + + private static void SetProgress(int val, int max, string msg) + { + // do nothing + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs b/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs similarity index 98% rename from src/Libraries/SmartStore.Services/DataExchange/IExportService.cs rename to src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs index e3823a7c4e..92ccb543a5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IExportService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs @@ -5,7 +5,7 @@ namespace SmartStore.Services.DataExchange { - public interface IExportService + public interface IExportProfileService { /// /// Creates a volatile export profile diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs new file mode 100644 index 0000000000..0a098c5055 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs @@ -0,0 +1,53 @@ +using SmartStore.ComponentModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Dynamic; +using SmartStore.Utilities; + +namespace SmartStore.Services.DataExchange.Internal +{ + internal class DynamicEntity : Expando + { + private readonly object _entity; + + public DynamicEntity(object entity) + : base(entity) + { + } + + // TODO: Umbenennen!!! + public object _Entity + { + get { return base.WrappedObject; } + } + + protected override bool TrySetMemberCore(string name, object value) + { + // Virtual navigation properties of entities should not be set as is. + // A previously created ExpandoEntity instance is set instead. + // But assigning the value to the wrapped entity instance would result + // in a type mismatch error, as both types obviously don't match. + // Therefore we save reference values in the local values dictionary. + + var pi = base.GetPropertyInfo(name); + + if (pi != null && (pi.PropertyType.IsPredefinedType() || !pi.GetMethod.IsVirtual)) + { + // Property exists, is NOT complex, or IS complex but NOT virtual + try + { + Fasterflect.PropertyInfoExtensions.Set(pi, WrappedObject, value); + return true; + } + catch { } + } + + // Property does not exist, or is virtual complex + Properties[name] = value; + return true; + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs deleted file mode 100644 index 565f9e00e5..0000000000 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoEntity.cs +++ /dev/null @@ -1,25 +0,0 @@ -using SmartStore.ComponentModel; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Dynamic; - -namespace SmartStore.Services.DataExchange.Internal -{ - internal class ExpandoEntity : Expando - { - private readonly object _entity; - - public ExpandoEntity(object entity) - : base(entity) - { - } - - public object Entity - { - get { return base.WrappedObject; } - } - } -} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs index d8da9e6f8c..657a6675ed 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs @@ -42,7 +42,7 @@ public dynamic ToExpando(Currency currency) if (currency == null) return null; - dynamic expando = new ExpandoEntity(currency); + dynamic expando = new DynamicEntity(currency); expando.Name = currency.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); expando._Localized = GetLocalized(currency, x => x.Name); @@ -54,7 +54,7 @@ private dynamic ToExpando(Language language) if (language == null) return null; - dynamic expando = new ExpandoEntity(language); + dynamic expando = new DynamicEntity(language); return expando; } @@ -63,7 +63,7 @@ private dynamic ToExpando(Country country) if (country == null) return null; - dynamic expando = new ExpandoEntity(country); + dynamic expando = new DynamicEntity(country); expando.Name = country.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); expando._Localized = GetLocalized(country, x => x.Name); @@ -75,12 +75,12 @@ private dynamic ToExpando(Address address) if (address == null) return null; - dynamic expando = new ExpandoEntity(address); + dynamic expando = new DynamicEntity(address); expando.Country = ToExpando(address.Country); if (address.StateProvinceId.GetValueOrDefault() > 0) { - dynamic sp = new ExpandoEntity(address.StateProvince); + dynamic sp = new DynamicEntity(address.StateProvince); sp.Name = address.StateProvince.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); sp._Localized = GetLocalized(address.StateProvince, x => x.Name); expando.StateProvince = sp; @@ -98,7 +98,7 @@ private dynamic ToExpando(RewardPointsHistory points) if (points == null) return null; - dynamic expando = new ExpandoEntity(points); + dynamic expando = new DynamicEntity(points); return expando; } @@ -107,7 +107,7 @@ private dynamic ToExpando( Customer customer) if (customer == null) return null; - dynamic expando = new ExpandoEntity(customer); + dynamic expando = new DynamicEntity(customer); expando.BillingAddress = null; expando.ShippingAddress = null; @@ -128,7 +128,7 @@ private dynamic ToExpando(Store store) if (store == null) return null; - dynamic expando = new ExpandoEntity(store); + dynamic expando = new DynamicEntity(store); expando.PrimaryStoreCurrency = ToExpando(store.PrimaryStoreCurrency); expando.PrimaryExchangeRateCurrency = ToExpando(store.PrimaryExchangeRateCurrency); @@ -140,7 +140,7 @@ private dynamic ToExpando(DeliveryTime deliveryTime) if (deliveryTime == null) return null; - dynamic expando = new ExpandoEntity(deliveryTime); + dynamic expando = new DynamicEntity(deliveryTime); expando.Name = deliveryTime.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); expando._Localized = GetLocalized(deliveryTime, x => x.Name); @@ -152,7 +152,7 @@ private dynamic ToExpando(QuantityUnit quantityUnit) if (quantityUnit == null) return null; - dynamic expando = new ExpandoEntity(quantityUnit); + dynamic expando = new DynamicEntity(quantityUnit); expando.Name = quantityUnit.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); expando.Description = quantityUnit.GetLocalized(x => x.Description, _ctx.Projection.LanguageId ?? 0, true, false); expando._Localized = GetLocalized(quantityUnit, @@ -167,7 +167,7 @@ private dynamic ToExpando(Picture picture, int thumbPictureSize, int detailsPict if (picture == null) return null; - dynamic expando = new ExpandoEntity(picture); + dynamic expando = new DynamicEntity(picture); //// TODO!!!!! //expando._ThumbImageUrl = _pictureService.Value.GetPictureUrl(picture, thumbPictureSize, false, _ctx.Store.Url); diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index b5d926c04e..a6af2b2012 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -185,7 +185,8 @@ - + + @@ -198,9 +199,9 @@ - + - + diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs index f18a23e6ca..838065d364 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs @@ -45,6 +45,21 @@ public T ResolveNamed(string name) where T : class public ScheduleTask ScheduleTask { get; set; } + /// + /// Persists a task's progress information to the database + /// + /// Progress value (numerator) + /// Progress maximum (denominator) + /// Progress message. Can be null. + /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. + public void SetProgress(int value, int maximum, string message, bool immediately = false) + { + float fraction = (float)value / (float)Math.Max(maximum, 1f); + int percentage = (int)Math.Round(fraction * 100f, 0); + + SetProgress(Math.Min(Math.Max(percentage, 0), 100), message, immediately); + } + /// /// Persists a task's progress information to the database /// @@ -75,20 +90,5 @@ public void SetProgress(int? progress, string message, bool immediately = false catch { } } } - - /// - /// Persists a task's progress information to the database - /// - /// Progress numerator - /// Progress denominator - /// Progress message. Can be null. - /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. - public void SetProgress(int numerator, int denominator, string message, bool immediately = false) - { - float fraction = (float)numerator / (float)Math.Max(denominator, 1f); - int percentage = (int)Math.Round(fraction * 100f, 0); - - SetProgress(Math.Min(Math.Max(percentage, 0), 100), message, immediately); - } } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs index 86eb437175..7a310a4c18 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs @@ -18,12 +18,12 @@ public class FeedGoogleMerchantCenterController : PluginControllerBase { private readonly IGoogleFeedService _googleFeedService; private readonly AdminAreaSettings _adminAreaSettings; - private readonly IExportService _exportService; + private readonly IExportProfileService _exportService; public FeedGoogleMerchantCenterController( IGoogleFeedService googleFeedService, AdminAreaSettings adminAreaSettings, - IExportService exportService) + IExportProfileService exportService) { _googleFeedService = googleFeedService; _adminAreaSettings = adminAreaSettings; @@ -61,7 +61,7 @@ public ActionResult ProductEditTab(int productId) ViewBag.DefaultAgeGroup = T("Common.Auto"); // we do not have export profile context here, so we simply use the first profile - var profile = _exportService.GetExportProfilesBySystemName(ProductExportXmlProvider.SystemName).FirstOrDefault(); + var profile = _exportService.GetExportProfilesBySystemName(GmcExportXmlProvider.SystemName).FirstOrDefault(); if (profile != null) { var config = XmlHelper.Deserialize(profile.ProviderConfigData, typeof(ProfileConfigurationModel)) as ProfileConfigurationModel; @@ -73,12 +73,12 @@ public ActionResult ProductEditTab(int productId) ViewBag.DefaultMaterial = config.Material; ViewBag.DefaultPattern = config.Pattern; - if (config.Gender.HasValue() && config.Gender != ProductExportXmlProvider.Unspecified) + if (config.Gender.HasValue() && config.Gender != GmcExportXmlProvider.Unspecified) { ViewBag.DefaultGender = T("Plugins.Feed.Froogle.Gender" + culture.TextInfo.ToTitleCase(config.Gender)); } - if (config.AgeGroup.HasValue() && config.AgeGroup != ProductExportXmlProvider.Unspecified) + if (config.AgeGroup.HasValue() && config.AgeGroup != GmcExportXmlProvider.Unspecified) { ViewBag.DefaultAgeGroup = T("Plugins.Feed.Froogle.AgeGroup" + culture.TextInfo.ToTitleCase(config.AgeGroup)); } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs similarity index 99% rename from src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs rename to src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs index bbcb1bcbda..412737afc7 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/ProductExportXmlProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs @@ -28,7 +28,7 @@ namespace SmartStore.GoogleMerchantCenter.Providers ExportFeatures.OffersBrandFallback, ExportFeatures.CanIncludeMainPicture, ExportFeatures.UsesSpecialPrice)] - public class ProductExportXmlProvider : IExportProvider + public class GmcExportXmlProvider : IExportProvider { private const string _googleNamespace = "http://base.google.com/ns/1.0"; @@ -36,7 +36,7 @@ public class ProductExportXmlProvider : IExportProvider private readonly IMeasureService _measureService; private readonly MeasureSettings _measureSettings; - public ProductExportXmlProvider( + public GmcExportXmlProvider( IGoogleFeedService googleFeedService, IMeasureService measureService, MeasureSettings measureSettings) diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj index e571790b5d..0826ad5831 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj @@ -42,6 +42,7 @@ + true @@ -175,7 +176,7 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index ee70cae011..1daa71bd03 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -221,7 +221,7 @@ public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, b builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 33d35d9fbb..a244d1f807 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -38,7 +38,7 @@ namespace SmartStore.Admin.Controllers public class ExportController : AdminControllerBase { private readonly ICommonServices _services; - private readonly IExportService _exportService; + private readonly IExportProfileService _exportService; private readonly PluginMediator _pluginMediator; private readonly ICategoryService _categoryService; private readonly IManufacturerService _manufacturerService; @@ -53,7 +53,7 @@ public class ExportController : AdminControllerBase public ExportController( ICommonServices services, - IExportService exportService, + IExportProfileService exportService, PluginMediator pluginMediator, ICategoryService categoryService, IManufacturerService manufacturerService, From cdfb6aad69170a0abd3592945b7c5858919578ae Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 6 Nov 2015 03:14:13 +0100 Subject: [PATCH 035/732] Export framework: further refactoring --- .../{Expando.cs => HybridExpando.cs} | 146 ++++++- .../SmartStore.Core/SmartStore.Core.csproj | 2 +- .../ExportTask/ExportProfileTask.cs | 380 +++++++----------- .../DataExchange/Internal/DynamicEntity.cs | 15 +- .../DataExchange/Internal/ExpandoHelpers.cs | 240 ----------- .../SmartStore.Services.csproj | 5 +- .../Tasks/ITaskExecutor.cs | 6 +- .../Tasks/ITaskScheduler.cs | 3 +- .../Tasks/TaskExecutionContext.cs | 3 + .../SmartStore.Services/Tasks/TaskExecutor.cs | 9 +- .../Tasks/TaskScheduler.cs | 14 +- .../Controllers/TaskSchedulerController.cs | 6 +- 12 files changed, 325 insertions(+), 504 deletions(-) rename src/Libraries/SmartStore.Core/ComponentModel/{Expando.cs => HybridExpando.cs} (80%) delete mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs diff --git a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs similarity index 80% rename from src/Libraries/SmartStore.Core/ComponentModel/Expando.cs rename to src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs index c97e82ed75..42061fad53 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/Expando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs @@ -36,6 +36,7 @@ using System.Linq; using System.Dynamic; using System.Reflection; +using System.Collections; namespace SmartStore.ComponentModel { @@ -58,7 +59,7 @@ namespace SmartStore.ComponentModel /// Dictionary: Any of the extended properties are accessible via IDictionary interface /// [Serializable] - public class Expando : DynamicObject + public class HybridExpando : DynamicObject, IDictionary { /// /// Instance of object passed in @@ -85,7 +86,7 @@ public class Expando : DynamicObject /// /// Note you can subclass Expando. /// - public Expando() + public HybridExpando() { Initialize(this); } @@ -98,7 +99,7 @@ public Expando() /// check native properties and only check the Dictionary! /// /// - public Expando(object instance) + public HybridExpando(object instance) { Initialize(instance); } @@ -120,12 +121,12 @@ IList InstancePropertyInfos get { if (_instancePropertyInfos == null && _instance != null) - _instancePropertyInfos = Fasterflect.PropertyExtensions.Properties(_instance.GetType(), Fasterflect.Flags.InstancePublicDeclaredOnly); + _instancePropertyInfos = Fasterflect.PropertyExtensions.Properties(_instanceType, Fasterflect.Flags.InstancePublic); return _instancePropertyInfos; } } - public override IEnumerable GetDynamicMemberNames() + public override IEnumerable GetDynamicMemberNames() { foreach (var prop in this.GetProperties(false)) yield return prop.Key; @@ -344,15 +345,22 @@ public object this[string key] /// public IEnumerable> GetProperties(bool includeInstanceProperties = false) { - if (includeInstanceProperties && _instance != null) + foreach (var key in this.Properties.Keys) + { + yield return new KeyValuePair(key, this.Properties[key]); + } + + if (includeInstanceProperties && _instance != null) { foreach (var prop in this.InstancePropertyInfos) - yield return new KeyValuePair(prop.Name, prop.GetValue(_instance, null)); + { + if (!this.Properties.ContainsKey(prop.Name)) + { + yield return new KeyValuePair(prop.Name, prop.GetValue(_instance, null)); + } + } } - foreach (var key in this.Properties.Keys) - yield return new KeyValuePair(key, this.Properties[key]); - } /// @@ -391,18 +399,116 @@ public bool Contains(string propertyName, bool includeInstanceProperties = false return false; } - public bool TryGetValue(string propertyName, out object value) - { - value = null; + #region IDictionary - if (this.Contains(propertyName, true)) - { - value = this[propertyName]; - return true; - } + ICollection IDictionary.Keys + { + get + { + return GetProperties(true).Select(x => x.Key).AsReadOnly(); + } + } - return false; + ICollection IDictionary.Values + { + get + { + return GetProperties(true).Select(x => x.Value).AsReadOnly(); + } + } + + int ICollection>.Count + { + get + { + return Properties.Count + InstancePropertyInfos.Count; + } + } + + bool ICollection>.IsReadOnly + { + get + { + return false; + } + } + + object IDictionary.this[string key] + { + get + { + return this[key]; + } + + set + { + this[key] = value; + } + } + + bool IDictionary.ContainsKey(string key) + { + return Contains(key, true); + } + + void IDictionary.Add(string key, object value) + { + throw new NotImplementedException(); + } + + bool IDictionary.Remove(string key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(string key, out object value) + { + value = null; + + if (this.Contains(key, true)) + { + value = this[key]; + return true; + } + + return false; + } + + void ICollection>.Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void ICollection>.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return Contains(item.Key, true); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetProperties(true).GetEnumerator(); } - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetProperties(true).GetEnumerator(); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index afe41e0061..b23a0d7b0f 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -303,7 +303,7 @@ - + diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index e4e9d02f8f..7e8b6e66c7 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -63,7 +63,6 @@ public class ExportProfileTask : ITask #region Dependencies - private ExpandoHelpers _expandoHelpers; private ICommonServices _services; private Lazy _exportProfileService; private Lazy _dataExchangeSettings; @@ -727,22 +726,8 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Currency currency) if (currency == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = currency; - - expando.Id = currency.Id; + dynamic expando = new DynamicEntity(currency); expando.Name = currency.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); - expando.CurrencyCode = currency.CurrencyCode; - expando.Rate = currency.Rate; - expando.DisplayLocale = currency.DisplayLocale; - expando.CustomFormatting = currency.CustomFormatting; - expando.LimitedToStores = currency.LimitedToStores; - expando.Published = currency.Published; - expando.DisplayOrder = currency.DisplayOrder; - expando.CreatedOnUtc = currency.CreatedOnUtc; - expando.UpdatedOnUtc = currency.UpdatedOnUtc; - expando.DomainEndings = currency.DomainEndings; - expando._Localized = GetLocalized(ctx, currency, x => x.Name); return expando; @@ -753,19 +738,7 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Language language) if (language == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = language; - - expando.Id = language.Id; - expando.Name = language.Name; - expando.LanguageCulture = language.LanguageCulture; - expando.UniqueSeoCode = language.UniqueSeoCode; - expando.FlagImageFileName = language.FlagImageFileName; - expando.Rtl = language.Rtl; - expando.LimitedToStores = language.LimitedToStores; - expando.Published = language.Published; - expando.DisplayOrder = language.DisplayOrder; - + dynamic expando = new DynamicEntity(language); return expando; } @@ -774,21 +747,8 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Country country) if (country == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = country; - - expando.Id = country.Id; + dynamic expando = new DynamicEntity(country); expando.Name = country.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); - expando.AllowsBilling = country.AllowsBilling; - expando.AllowsShipping = country.AllowsShipping; - expando.TwoLetterIsoCode = country.TwoLetterIsoCode; - expando.ThreeLetterIsoCode = country.ThreeLetterIsoCode; - expando.NumericIsoCode = country.NumericIsoCode; - expando.SubjectToVat = country.SubjectToVat; - expando.Published = country.Published; - expando.DisplayOrder = country.DisplayOrder; - expando.LimitedToStores = country.LimitedToStores; - expando._Localized = GetLocalized(ctx, country, x => x.Name); return expando; @@ -799,39 +759,14 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Address address) if (address == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = address; - - expando.Id = address.Id; - expando.FirstName = address.FirstName; - expando.LastName = address.LastName; - expando.Email = address.Email; - expando.Company = address.Company; - expando.CountryId = address.CountryId; - expando.StateProvinceId = address.StateProvinceId; - expando.City = address.City; - expando.Address1 = address.Address1; - expando.Address2 = address.Address2; - expando.ZipPostalCode = address.ZipPostalCode; - expando.PhoneNumber = address.PhoneNumber; - expando.FaxNumber = address.FaxNumber; - expando.CreatedOnUtc = address.CreatedOnUtc; - + dynamic expando = new DynamicEntity(address); expando.Country = ToExpando(ctx, address.Country); - if (address.StateProvince != null) + if (address.StateProvinceId.GetValueOrDefault() > 0) { - dynamic sp = new ExpandoObject(); - sp._Entity = address.StateProvince; - sp.Id = address.StateProvince.Id; - sp.CountryId = address.StateProvince.CountryId; + dynamic sp = new DynamicEntity(address.StateProvince); sp.Name = address.StateProvince.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); - sp.Abbreviation = address.StateProvince.Abbreviation; - sp.Published = address.StateProvince.Published; - sp.DisplayOrder = address.StateProvince.DisplayOrder; - sp._Localized = GetLocalized(ctx, address.StateProvince, x => x.Name); - expando.StateProvince = sp; } else @@ -1053,25 +988,7 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, ProductVariantAttributeC if (pvac == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = pvac; - - expando.Id = pvac.Id; - expando.StockQuantity = pvac.StockQuantity; - expando.AllowOutOfStockOrders = pvac.AllowOutOfStockOrders; - expando.AttributesXml = pvac.AttributesXml; - expando.Sku = pvac.Sku; - expando.Gtin = pvac.Gtin; - expando.ManufacturerPartNumber = pvac.ManufacturerPartNumber; - expando.Price = pvac.Price; - expando.Length = pvac.Length; - expando.Width = pvac.Width; - expando.Height = pvac.Height; - expando.BasePriceAmount = pvac.BasePriceAmount; - expando.BasePriceBaseAmount = pvac.BasePriceBaseAmount; - expando.AssignedPictureIds = pvac.AssignedPictureIds; - expando.DeliveryTimeId = pvac.DeliveryTimeId; - expando.IsActive = pvac.IsActive; + dynamic expando = new DynamicEntity(pvac); GetDeliveryTimeAndQuantityUnit(ctx, expando, pvac.DeliveryTimeId, pvac.QuantityUnitId); @@ -1083,27 +1000,13 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Manufacturer manufacture if (manufacturer == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = manufacturer; - - expando.Id = manufacturer.Id; + dynamic expando = new DynamicEntity(manufacturer); expando.Name = manufacturer.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); expando.SeName = manufacturer.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); expando.Description = manufacturer.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); - expando.ManufacturerTemplateId = manufacturer.ManufacturerTemplateId; expando.MetaKeywords = manufacturer.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaDescription = manufacturer.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaTitle = manufacturer.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); - expando.PictureId = manufacturer.PictureId; - expando.PageSize = manufacturer.PageSize; - expando.AllowCustomersToSelectPageSize = manufacturer.AllowCustomersToSelectPageSize; - expando.PageSizeOptions = manufacturer.PageSizeOptions; - expando.PriceRanges = manufacturer.PriceRanges; - expando.Published = manufacturer.Published; - expando.Deleted = manufacturer.Deleted; - expando.DisplayOrder = manufacturer.DisplayOrder; - expando.CreatedOnUtc = manufacturer.CreatedOnUtc; - expando.UpdatedOnUtc = manufacturer.UpdatedOnUtc; expando.Picture = null; @@ -1122,36 +1025,15 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Category category) if (category == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = category; - - expando.Id = category.Id; + dynamic expando = new DynamicEntity(category); expando.Name = category.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); expando.SeName = category.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); expando.FullName = category.GetLocalized(x => x.FullName, ctx.Projection.LanguageId ?? 0, true, false); expando.Description = category.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); expando.BottomDescription = category.GetLocalized(x => x.BottomDescription, ctx.Projection.LanguageId ?? 0, true, false); - expando.CategoryTemplateId = category.CategoryTemplateId; expando.MetaKeywords = category.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaDescription = category.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaTitle = category.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); - expando.ParentCategoryId = category.ParentCategoryId; - expando.PictureId = category.PictureId; - expando.PageSize = category.PageSize; - expando.AllowCustomersToSelectPageSize = category.AllowCustomersToSelectPageSize; - expando.PageSizeOptions = category.PageSizeOptions; - expando.PriceRanges = category.PriceRanges; - expando.ShowOnHomePage = category.ShowOnHomePage; - expando.HasDiscountsApplied = category.HasDiscountsApplied; - expando.Published = category.Published; - expando.Deleted = category.Deleted; - expando.DisplayOrder = category.DisplayOrder; - expando.CreatedOnUtc = category.CreatedOnUtc; - expando.UpdatedOnUtc = category.UpdatedOnUtc; - expando.SubjectToAcl = category.SubjectToAcl; - expando.LimitedToStores = category.LimitedToStores; - expando.Alias = category.Alias; - expando.DefaultViewMode = category.DefaultViewMode; expando.Picture = null; @@ -1172,111 +1054,18 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Product product) if (product == null) return null; - dynamic expando = new ExpandoObject(); - expando._Entity = product; + dynamic expando = new DynamicEntity(product); - expando.Id = product.Id; - expando.ProductTypeId = product.ProductTypeId; - expando.ParentGroupedProductId = product.ParentGroupedProductId; - expando.VisibleIndividually = product.VisibleIndividually; expando.Name = product.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); expando.SeName = product.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); expando.ShortDescription = product.GetLocalized(x => x.ShortDescription, ctx.Projection.LanguageId ?? 0, true, false); expando.FullDescription = product.GetLocalized(x => x.FullDescription, ctx.Projection.LanguageId ?? 0, true, false); - expando.AdminComment = product.AdminComment; - expando.ProductTemplateId = product.ProductTemplateId; - expando.ShowOnHomePage = product.ShowOnHomePage; - expando.HomePageDisplayOrder = product.HomePageDisplayOrder; expando.MetaKeywords = product.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaDescription = product.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); expando.MetaTitle = product.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); - expando.AllowCustomerReviews = product.AllowCustomerReviews; - expando.ApprovedRatingSum = product.ApprovedRatingSum; - expando.NotApprovedRatingSum = product.NotApprovedRatingSum; - expando.ApprovedTotalReviews = product.ApprovedTotalReviews; - expando.NotApprovedTotalReviews = product.NotApprovedTotalReviews; - expando.SubjectToAcl = product.SubjectToAcl; - expando.LimitedToStores = product.LimitedToStores; - expando.Sku = product.Sku; - expando.ManufacturerPartNumber = product.ManufacturerPartNumber; - expando.Gtin = product.Gtin; - expando.IsGiftCard = product.IsGiftCard; - expando.GiftCardTypeId = product.GiftCardTypeId; - expando.RequireOtherProducts = product.RequireOtherProducts; - expando.RequiredProductIds = product.RequiredProductIds; - expando.AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts; - expando.IsDownload = product.IsDownload; - expando.DownloadId = product.DownloadId; - expando.UnlimitedDownloads = product.UnlimitedDownloads; - expando.MaxNumberOfDownloads = product.MaxNumberOfDownloads; - expando.DownloadExpirationDays = product.DownloadExpirationDays; - expando.DownloadActivationTypeId = product.DownloadActivationTypeId; - expando.HasSampleDownload = product.HasSampleDownload; - expando.SampleDownloadId = product.SampleDownloadId; - expando.HasUserAgreement = product.HasUserAgreement; - expando.UserAgreementText = product.UserAgreementText; - expando.IsRecurring = product.IsRecurring; - expando.RecurringCycleLength = product.RecurringCycleLength; - expando.RecurringCyclePeriodId = product.RecurringCyclePeriodId; - expando.RecurringTotalCycles = product.RecurringTotalCycles; - expando.IsShipEnabled = product.IsShipEnabled; - expando.IsFreeShipping = product.IsFreeShipping; - expando.AdditionalShippingCharge = product.AdditionalShippingCharge; - expando.IsTaxExempt = product.IsTaxExempt; - expando.IsEsd = product.IsEsd; - expando.TaxCategoryId = product.TaxCategoryId; - expando.ManageInventoryMethodId = product.ManageInventoryMethodId; - expando.StockQuantity = product.StockQuantity; - expando.DisplayStockAvailability = product.DisplayStockAvailability; - expando.DisplayStockQuantity = product.DisplayStockQuantity; - expando.MinStockQuantity = product.MinStockQuantity; - expando.LowStockActivityId = product.LowStockActivityId; - expando.NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow; - expando.BackorderModeId = product.BackorderModeId; - expando.AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions; - expando.OrderMinimumQuantity = product.OrderMinimumQuantity; - expando.OrderMaximumQuantity = product.OrderMaximumQuantity; - expando.AllowedQuantities = product.AllowedQuantities; - expando.DisableBuyButton = product.DisableBuyButton; - expando.DisableWishlistButton = product.DisableWishlistButton; - expando.AvailableForPreOrder = product.AvailableForPreOrder; - expando.CallForPrice = product.CallForPrice; - expando.Price = product.Price; - expando.OldPrice = product.OldPrice; - expando.ProductCost = product.ProductCost; - expando.SpecialPrice = product.SpecialPrice; - expando.SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc; - expando.SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc; - expando.CustomerEntersPrice = product.CustomerEntersPrice; - expando.MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice; - expando.MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice; - expando.HasTierPrices = product.HasTierPrices; - expando.LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice; - expando.HasDiscountsApplied = product.HasDiscountsApplied; - expando.Weight = product.Weight; - expando.Length = product.Length; - expando.Width = product.Width; - expando.Height = product.Height; - expando.AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc; - expando.AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc; - expando.DisplayOrder = product.DisplayOrder; - expando.Published = product.Published; - expando.Deleted = product.Deleted; - expando.CreatedOnUtc = product.CreatedOnUtc; - expando.UpdatedOnUtc = product.UpdatedOnUtc; - expando.DeliveryTimeId = product.DeliveryTimeId; - expando.QuantityUnitId = product.QuantityUnitId; - expando.BasePriceEnabled = product.BasePriceEnabled; - expando.BasePriceMeasureUnit = product.BasePriceMeasureUnit; - expando.BasePriceAmount = product.BasePriceAmount; - expando.BasePriceBaseAmount = product.BasePriceBaseAmount; - expando.BasePriceHasValue = product.BasePriceHasValue; expando.BundleTitleText = product.GetLocalized(x => x.BundleTitleText, ctx.Projection.LanguageId ?? 0, true, false); - expando.BundlePerItemShipping = product.BundlePerItemShipping; - expando.BundlePerItemPricing = product.BundlePerItemPricing; - expando.BundlePerItemShoppingCart = product.BundlePerItemShoppingCart; - - expando.AppliedDiscounts = null; + + expando.AppliedDiscounts = null; expando.TierPrices = null; expando.ProductAttributes = null; expando.ProductAttributeCombinations = null; @@ -1299,6 +1088,138 @@ private dynamic ToExpando(ExportProfileTaskContext ctx, Product product) return expando; } + //private dynamic ToExpando(ExportProfileTaskContext ctx, Product product) + //{ + // if (product == null) + // return null; + + // dynamic expando = new ExpandoObject(); + // expando._Entity = product; + + // expando.Id = product.Id; + // expando.ProductTypeId = product.ProductTypeId; + // expando.ParentGroupedProductId = product.ParentGroupedProductId; + // expando.VisibleIndividually = product.VisibleIndividually; + // expando.Name = product.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + // expando.SeName = product.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); + // expando.ShortDescription = product.GetLocalized(x => x.ShortDescription, ctx.Projection.LanguageId ?? 0, true, false); + // expando.FullDescription = product.GetLocalized(x => x.FullDescription, ctx.Projection.LanguageId ?? 0, true, false); + // expando.AdminComment = product.AdminComment; + // expando.ProductTemplateId = product.ProductTemplateId; + // expando.ShowOnHomePage = product.ShowOnHomePage; + // expando.HomePageDisplayOrder = product.HomePageDisplayOrder; + // expando.MetaKeywords = product.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); + // expando.MetaDescription = product.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); + // expando.MetaTitle = product.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); + // expando.AllowCustomerReviews = product.AllowCustomerReviews; + // expando.ApprovedRatingSum = product.ApprovedRatingSum; + // expando.NotApprovedRatingSum = product.NotApprovedRatingSum; + // expando.ApprovedTotalReviews = product.ApprovedTotalReviews; + // expando.NotApprovedTotalReviews = product.NotApprovedTotalReviews; + // expando.SubjectToAcl = product.SubjectToAcl; + // expando.LimitedToStores = product.LimitedToStores; + // expando.Sku = product.Sku; + // expando.ManufacturerPartNumber = product.ManufacturerPartNumber; + // expando.Gtin = product.Gtin; + // expando.IsGiftCard = product.IsGiftCard; + // expando.GiftCardTypeId = product.GiftCardTypeId; + // expando.RequireOtherProducts = product.RequireOtherProducts; + // expando.RequiredProductIds = product.RequiredProductIds; + // expando.AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts; + // expando.IsDownload = product.IsDownload; + // expando.DownloadId = product.DownloadId; + // expando.UnlimitedDownloads = product.UnlimitedDownloads; + // expando.MaxNumberOfDownloads = product.MaxNumberOfDownloads; + // expando.DownloadExpirationDays = product.DownloadExpirationDays; + // expando.DownloadActivationTypeId = product.DownloadActivationTypeId; + // expando.HasSampleDownload = product.HasSampleDownload; + // expando.SampleDownloadId = product.SampleDownloadId; + // expando.HasUserAgreement = product.HasUserAgreement; + // expando.UserAgreementText = product.UserAgreementText; + // expando.IsRecurring = product.IsRecurring; + // expando.RecurringCycleLength = product.RecurringCycleLength; + // expando.RecurringCyclePeriodId = product.RecurringCyclePeriodId; + // expando.RecurringTotalCycles = product.RecurringTotalCycles; + // expando.IsShipEnabled = product.IsShipEnabled; + // expando.IsFreeShipping = product.IsFreeShipping; + // expando.AdditionalShippingCharge = product.AdditionalShippingCharge; + // expando.IsTaxExempt = product.IsTaxExempt; + // expando.IsEsd = product.IsEsd; + // expando.TaxCategoryId = product.TaxCategoryId; + // expando.ManageInventoryMethodId = product.ManageInventoryMethodId; + // expando.StockQuantity = product.StockQuantity; + // expando.DisplayStockAvailability = product.DisplayStockAvailability; + // expando.DisplayStockQuantity = product.DisplayStockQuantity; + // expando.MinStockQuantity = product.MinStockQuantity; + // expando.LowStockActivityId = product.LowStockActivityId; + // expando.NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow; + // expando.BackorderModeId = product.BackorderModeId; + // expando.AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions; + // expando.OrderMinimumQuantity = product.OrderMinimumQuantity; + // expando.OrderMaximumQuantity = product.OrderMaximumQuantity; + // expando.AllowedQuantities = product.AllowedQuantities; + // expando.DisableBuyButton = product.DisableBuyButton; + // expando.DisableWishlistButton = product.DisableWishlistButton; + // expando.AvailableForPreOrder = product.AvailableForPreOrder; + // expando.CallForPrice = product.CallForPrice; + // expando.Price = product.Price; + // expando.OldPrice = product.OldPrice; + // expando.ProductCost = product.ProductCost; + // expando.SpecialPrice = product.SpecialPrice; + // expando.SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc; + // expando.SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc; + // expando.CustomerEntersPrice = product.CustomerEntersPrice; + // expando.MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice; + // expando.MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice; + // expando.HasTierPrices = product.HasTierPrices; + // expando.LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice; + // expando.HasDiscountsApplied = product.HasDiscountsApplied; + // expando.Weight = product.Weight; + // expando.Length = product.Length; + // expando.Width = product.Width; + // expando.Height = product.Height; + // expando.AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc; + // expando.AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc; + // expando.DisplayOrder = product.DisplayOrder; + // expando.Published = product.Published; + // expando.Deleted = product.Deleted; + // expando.CreatedOnUtc = product.CreatedOnUtc; + // expando.UpdatedOnUtc = product.UpdatedOnUtc; + // expando.DeliveryTimeId = product.DeliveryTimeId; + // expando.QuantityUnitId = product.QuantityUnitId; + // expando.BasePriceEnabled = product.BasePriceEnabled; + // expando.BasePriceMeasureUnit = product.BasePriceMeasureUnit; + // expando.BasePriceAmount = product.BasePriceAmount; + // expando.BasePriceBaseAmount = product.BasePriceBaseAmount; + // expando.BasePriceHasValue = product.BasePriceHasValue; + // expando.BundleTitleText = product.GetLocalized(x => x.BundleTitleText, ctx.Projection.LanguageId ?? 0, true, false); + // expando.BundlePerItemShipping = product.BundlePerItemShipping; + // expando.BundlePerItemPricing = product.BundlePerItemPricing; + // expando.BundlePerItemShoppingCart = product.BundlePerItemShoppingCart; + + // expando.AppliedDiscounts = null; + // expando.TierPrices = null; + // expando.ProductAttributes = null; + // expando.ProductAttributeCombinations = null; + // expando.ProductPictures = null; + // expando.ProductCategories = null; + // expando.ProductManufacturers = null; + // expando.ProductTags = null; + // expando.ProductSpecificationAttributes = null; + // expando.ProductBundleItems = null; + + // expando._Localized = GetLocalized(ctx, product, + // x => x.Name, + // x => x.ShortDescription, + // x => x.FullDescription, + // x => x.MetaKeywords, + // x => x.MetaDescription, + // x => x.MetaTitle, + // x => x.BundleTitleText); + + // return expando; + //} + private dynamic ToExpando(ExportProfileTaskContext ctx, Order order) { if (order == null) @@ -1901,10 +1822,15 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro foreach (var combination in productAttributeCombinations.Where(x => x.IsActive)) { - var expandoCombination = ((IDictionary)expando).ToExpandoObject(); // clone - - matterOfDataMerging(expandoCombination, combination); - result.Add(expandoCombination); + // TODO: Das ist nocht nicht zu Ende gedacht, wegen MergeWithCombination etc. + var clone = new ExpandoObject(); + clone.Merge((IDictionary)expando, true); + matterOfDataMerging(clone, combination); + result.Add(clone); + + //var expandoCombination = ((IDictionary)expando).ToExpandoObject(); // clone + //matterOfDataMerging(expandoCombination, combination); + //result.Add(expandoCombination); } } else diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs index 0a098c5055..6cccfb958a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs @@ -9,19 +9,24 @@ namespace SmartStore.Services.DataExchange.Internal { - internal class DynamicEntity : Expando + internal class DynamicEntity : HybridExpando { - private readonly object _entity; + public DynamicEntity(DynamicEntity dynamicEntity) + : this(dynamicEntity.WrappedObject) + { + base.Properties.Merge(dynamicEntity, true); + } public DynamicEntity(object entity) : base(entity) { + // TODO: Umbenennen!!! + base.Properties["_Entity"] = entity; } - // TODO: Umbenennen!!! - public object _Entity + public void Merge() { - get { return base.WrappedObject; } + } protected override bool TrySetMemberCore(string name, object value) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs deleted file mode 100644 index 657a6675ed..0000000000 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/ExpandoHelpers.cs +++ /dev/null @@ -1,240 +0,0 @@ -using SmartStore.ComponentModel; -using SmartStore.Core; -using SmartStore.Core.Domain.Common; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Directory; -using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.Domain.Seo; -using SmartStore.Core.Domain.Stores; -using SmartStore.Services.DataExchange.ExportTask; -using SmartStore.Services.Localization; -using SmartStore.Services.Seo; -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Services.DataExchange.Internal -{ - internal class ExpandoHelpers - { - private readonly ExportProfileTaskContext _ctx; - private readonly IUrlRecordService _urlRecordService; - private readonly ILocalizedEntityService _localizedEntityService; - - public ExpandoHelpers( - ExportProfileTaskContext context, - IUrlRecordService urlRecordService, - ILocalizedEntityService localizedEntityService) - { - _ctx = context; - _urlRecordService = urlRecordService; - _localizedEntityService = localizedEntityService; - } - - public dynamic ToExpando(Currency currency) - { - if (currency == null) - return null; - - dynamic expando = new DynamicEntity(currency); - expando.Name = currency.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); - expando._Localized = GetLocalized(currency, x => x.Name); - - return expando; - } - - private dynamic ToExpando(Language language) - { - if (language == null) - return null; - - dynamic expando = new DynamicEntity(language); - return expando; - } - - private dynamic ToExpando(Country country) - { - if (country == null) - return null; - - dynamic expando = new DynamicEntity(country); - expando.Name = country.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); - expando._Localized = GetLocalized(country, x => x.Name); - - return expando; - } - - private dynamic ToExpando(Address address) - { - if (address == null) - return null; - - dynamic expando = new DynamicEntity(address); - expando.Country = ToExpando(address.Country); - - if (address.StateProvinceId.GetValueOrDefault() > 0) - { - dynamic sp = new DynamicEntity(address.StateProvince); - sp.Name = address.StateProvince.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); - sp._Localized = GetLocalized(address.StateProvince, x => x.Name); - expando.StateProvince = sp; - } - else - { - expando.StateProvince = null; - } - - return expando; - } - - private dynamic ToExpando(RewardPointsHistory points) - { - if (points == null) - return null; - - dynamic expando = new DynamicEntity(points); - return expando; - } - - private dynamic ToExpando( Customer customer) - { - if (customer == null) - return null; - - dynamic expando = new DynamicEntity(customer); - - expando.BillingAddress = null; - expando.ShippingAddress = null; - expando.Addresses = null; - expando.CustomerRoles = null; - - expando.RewardPointsHistory = null; - expando._RewardPointsBalance = 0; - - expando._GenericAttributes = null; - expando._HasNewsletterSubscription = false; - - return expando; - } - - private dynamic ToExpando(Store store) - { - if (store == null) - return null; - - dynamic expando = new DynamicEntity(store); - expando.PrimaryStoreCurrency = ToExpando(store.PrimaryStoreCurrency); - expando.PrimaryExchangeRateCurrency = ToExpando(store.PrimaryExchangeRateCurrency); - - return expando; - } - - private dynamic ToExpando(DeliveryTime deliveryTime) - { - if (deliveryTime == null) - return null; - - dynamic expando = new DynamicEntity(deliveryTime); - expando.Name = deliveryTime.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); - expando._Localized = GetLocalized(deliveryTime, x => x.Name); - - return expando; - } - - private dynamic ToExpando(QuantityUnit quantityUnit) - { - if (quantityUnit == null) - return null; - - dynamic expando = new DynamicEntity(quantityUnit); - expando.Name = quantityUnit.GetLocalized(x => x.Name, _ctx.Projection.LanguageId ?? 0, true, false); - expando.Description = quantityUnit.GetLocalized(x => x.Description, _ctx.Projection.LanguageId ?? 0, true, false); - expando._Localized = GetLocalized(quantityUnit, - x => x.Name, - x => x.Description); - - return expando; - } - - private dynamic ToExpando(Picture picture, int thumbPictureSize, int detailsPictureSize) - { - if (picture == null) - return null; - - dynamic expando = new DynamicEntity(picture); - - //// TODO!!!!! - //expando._ThumbImageUrl = _pictureService.Value.GetPictureUrl(picture, thumbPictureSize, false, _ctx.Store.Url); - //expando._ImageUrl = _pictureService.Value.GetPictureUrl(picture, detailsPictureSize, false, _ctx.Store.Url); - //expando._FullSizeImageUrl = _pictureService.Value.GetPictureUrl(picture, 0, false, _ctx.Store.Url); - - //var relativeUrl = _pictureService.Value.GetPictureUrl(picture); - //expando._FileName = relativeUrl.Substring(relativeUrl.LastIndexOf("/") + 1); - - //expando._ThumbLocalPath = _pictureService.Value.GetThumbLocalPath(picture); - - return expando; - } - - // TODO: weitermachen [...] - - private List GetLocalized(T entity, params Expression>[] keySelectors) - where T : BaseEntity, ILocalizedEntity - { - if (_ctx.Languages.Count <= 1) - return null; - - var localized = new List(); - - var localeKeyGroup = typeof(T).Name; - var isSlugSupported = typeof(ISlugSupported).IsAssignableFrom(typeof(T)); - - foreach (var language in _ctx.Languages) - { - var languageCulture = language.Value.LanguageCulture.EmptyNull().ToLower(); - - // add SeName - if (isSlugSupported) - { - var value = _urlRecordService.GetActiveSlug(entity.Id, localeKeyGroup, language.Value.Id); - if (value.HasValue()) - { - dynamic exp = new ExpandoObject(); - exp.Culture = languageCulture; - exp.LocaleKey = "SeName"; - exp.LocaleValue = value; - - localized.Add(exp); - } - } - - foreach (var keySelector in keySelectors) - { - var member = keySelector.Body as MemberExpression; - var propInfo = member.Member as PropertyInfo; - string localeKey = propInfo.Name; - var value = _localizedEntityService.GetLocalizedValue(language.Value.Id, entity.Id, localeKeyGroup, localeKey); - - // we better not export empty values. The risk is too high that they get reimported and unnecessarily pollute databases. - if (value.HasValue()) - { - dynamic exp = new ExpandoObject(); - exp.Culture = languageCulture; - exp.LocaleKey = localeKey; - exp.LocaleValue = value; - - localized.Add(exp); - } - } - } - - return (localized.Count == 0 ? null : localized); - } - } -} diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index a6af2b2012..1c3348629e 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -187,7 +187,6 @@ - @@ -575,7 +574,9 @@ Nop_Services_EuropaCheckVatService_checkVatService - + + + diff --git a/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs b/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs index b14c0e7c22..053cf45937 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs @@ -1,10 +1,14 @@ using System; +using System.Collections.Generic; using SmartStore.Core.Domain.Tasks; namespace SmartStore.Services.Tasks { public interface ITaskExecutor { - void Execute(ScheduleTask task, bool throwOnError = false); + void Execute( + ScheduleTask task, + IDictionary taskParameters = null, + bool throwOnError = false); } } diff --git a/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs index c1805dc7a8..f852d12952 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs @@ -53,7 +53,8 @@ public interface ITaskScheduler /// Executes a single task immediately /// /// - void RunSingleTask(int scheduleTaskId); + /// Optional task parameters + void RunSingleTask(int scheduleTaskId, IDictionary taskParameters = null); /// /// Verifies the authentication token which is generated right before the HTTP endpoint gets called. diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs index 838065d364..8efba4376f 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs @@ -21,6 +21,7 @@ internal TaskExecutionContext(IComponentContext componentContext, ScheduleTask o { this._componentContext = componentContext; this._originalTask = originalTask; + this.Parameters = new Dictionary(); } public T Resolve(object key = null) where T : class @@ -45,6 +46,8 @@ public T ResolveNamed(string name) where T : class public ScheduleTask ScheduleTask { get; set; } + public IDictionary Parameters { get; set; } + /// /// Persists a task's progress information to the database /// diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs index 92e7fa4668..18408a15c0 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs @@ -10,6 +10,7 @@ using SmartStore.Core; using SmartStore.Core.Domain.Customers; using System.Diagnostics; +using System.Collections.Generic; namespace SmartStore.Services.Tasks { @@ -43,7 +44,10 @@ public TaskExecutor( public ILogger Logger { get; set; } - public void Execute(ScheduleTask task, bool throwOnError = false) + public void Execute( + ScheduleTask task, + IDictionary taskParameters = null, + bool throwOnError = false) { if (task.IsRunning) return; @@ -102,7 +106,8 @@ public void Execute(ScheduleTask task, bool throwOnError = false) var ctx = new TaskExecutionContext(_componentContext, task) { ScheduleTask = task.Clone(), - CancellationToken = cts.Token + CancellationToken = cts.Token, + Parameters = taskParameters ?? new Dictionary() }; instance.Execute(ctx); diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs index 30eb8b8288..3ff2e5e04f 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs @@ -10,6 +10,7 @@ using SmartStore.Core.Async; using SmartStore.Core.Domain.Tasks; using SmartStore.Core.Logging; +using SmartStore.Collections; namespace SmartStore.Services.Tasks { @@ -101,9 +102,18 @@ public bool VerifyAuthToken(string authToken) return _authTokens.TryRemove(authToken, out val); } - public void RunSingleTask(int scheduleTaskId) + public void RunSingleTask(int scheduleTaskId, IDictionary taskParameters = null) { - CallEndpoint(_baseUrl + "/Execute/{0}".FormatInvariant(scheduleTaskId)); + string query = ""; + + if (taskParameters != null && taskParameters.Any()) + { + var qs = new QueryString(); + taskParameters.Each(x => qs.Add(x.Key, x.Value)); + query = qs.ToString(); + } + + CallEndpoint(_baseUrl + "/Execute/{0}{1}".FormatInvariant(scheduleTaskId, query)); } private void Elapsed(object sender, System.Timers.ElapsedEventArgs e) diff --git a/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs b/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs index b52e1c033b..9b7b6763c2 100644 --- a/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs @@ -9,7 +9,7 @@ using SmartStore.Web.Framework.Controllers; using SmartStore.Services.Security; using SmartStore.Services; - +using SmartStore.Collections; namespace SmartStore.Web.Controllers { @@ -77,8 +77,8 @@ public ActionResult Execute(int id /* taskId */) var task = _scheduledTaskService.GetTaskById(id); if (task == null) return HttpNotFound(); - - _taskExecutor.Execute(task); + + _taskExecutor.Execute(task, QueryString.Current.ToDictionary()); return Content("Task '{0}' executed".FormatCurrent(task.Name)); } From daf63a30a9745770b3c3bf43bafee33bd94062c1 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 6 Nov 2015 18:14:34 +0100 Subject: [PATCH 036/732] Export framework: minor refactoring --- .../Extensions/DictionaryExtensions.cs | 29 ++++++++++--------- .../ExportTask/ExportProfileTask.cs | 7 ++++- .../DataExchange/Internal/DynamicEntity.cs | 10 ++++++- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs index 6b91605920..8cfb8c1142 100644 --- a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs @@ -12,7 +12,7 @@ namespace SmartStore public static class DictionaryExtensions { - public static void AddRange(this IDictionary values, IEnumerable> other) + public static void AddRange(this IDictionary values, IEnumerable> other) { foreach (var kvp in other) { @@ -37,13 +37,13 @@ public static void Merge(this IDictionary instance, object value instance.Merge(new RouteValueDictionary(values), replaceExisting); } - public static void Merge(this IDictionary instance, IDictionary from, bool replaceExisting = true) + public static void Merge(this IDictionary instance, IDictionary from, bool replaceExisting = true) { - foreach (KeyValuePair keyValuePair in from) + foreach (var kvp in from) { - if (replaceExisting || !instance.ContainsKey(keyValuePair.Key)) + if (replaceExisting || !instance.ContainsKey(kvp.Key)) { - instance[keyValuePair.Key] = keyValuePair.Value; + instance[kvp.Key] = kvp.Value; } } } @@ -60,28 +60,31 @@ public static void PrependInValue(this IDictionary instance, str public static string ToAttributeString(this IDictionary instance) { - StringBuilder builder = new StringBuilder(); + var sb = new StringBuilder(); foreach (KeyValuePair pair in instance) { object[] args = new object[] { HttpUtility.HtmlAttributeEncode(pair.Key), HttpUtility.HtmlAttributeEncode(pair.Value.ToString()) }; - builder.Append(" {0}=\"{1}\"".FormatWith(args)); + sb.Append(" {0}=\"{1}\"".FormatWith(args)); } - return builder.ToString(); + return sb.ToString(); } - public static T GetValue(this IDictionary instance, K key) + public static TValue GetValue(this IDictionary instance, TKey key) { try { object val; if (instance != null && instance.TryGetValue(key, out val) && val != null) - return (T)Convert.ChangeType(val, typeof(T), CultureInfo.InvariantCulture); + { + return (TValue)Convert.ChangeType(val, typeof(TValue), CultureInfo.InvariantCulture); + } } - catch (Exception exc) + catch (Exception ex) { - exc.Dump(); + ex.Dump(); } - return default(T); + + return default(TValue); } public static ExpandoObject ToExpandoObject(this IDictionary source, bool castIfPossible = false) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 7e8b6e66c7..b36699541b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -1824,10 +1824,15 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro { // TODO: Das ist nocht nicht zu Ende gedacht, wegen MergeWithCombination etc. var clone = new ExpandoObject(); - clone.Merge((IDictionary)expando, true); + clone.Merge((IDictionary)expando); matterOfDataMerging(clone, combination); result.Add(clone); + // TODO: das hier ist besser. Aber ein Product-Klon muss immer noch erstellt werden. + //var clone = new DynamicEntity((DynamicEntity)expando); + //matterOfDataMerging(clone, combination); + //result.Add(clone); + //var expandoCombination = ((IDictionary)expando).ToExpandoObject(); // clone //matterOfDataMerging(expandoCombination, combination); //result.Add(expandoCombination); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs index 6cccfb958a..6940371341 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs @@ -24,9 +24,17 @@ public DynamicEntity(object entity) base.Properties["_Entity"] = entity; } - public void Merge() + public void Merge(string name, object value) { + Properties[name] = value; + } + public void MergeRange(IDictionary other) + { + foreach (var kvp in other) + { + Properties[kvp.Key] = kvp.Value; + } } protected override bool TrySetMemberCore(string name, object value) From 3fd05481dbd7b12b53313cbb44a0606651820f8c Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 6 Nov 2015 19:28:04 +0100 Subject: [PATCH 037/732] Export framework: renaming --- ...rtExecuteResult.cs => DataExportResult.cs} | 8 +-- .../DataExchange/Deployment/IFilePublisher.cs | 18 +++++ ...ortSegmenter.cs => ExportDataSegmenter.cs} | 14 ++-- .../DataExchange/ExportExecuteContext.cs | 8 +-- .../ExportTask/ExportProfileTask.cs | 36 +++++----- .../ExportTask/ExportProfileTaskContext.cs | 65 ++++++++++--------- .../DataExchange/IDataExporter.cs | 9 ++- .../CategoryExportContext.cs} | 6 +- .../CustomerExportContext.cs} | 6 +- .../ManufacturerExportContext.cs} | 6 +- .../OrderExportContext.cs} | 6 +- .../ProductExportContext.cs} | 6 +- .../CategoryXmlExportProvider.cs} | 4 +- .../CustomerXlsxExportProvider.cs} | 4 +- .../CustomerXmlExportProvider.cs} | 4 +- .../ManufacturerXmlExportProvider.cs} | 4 +- .../OrderXlsxExportProvider.cs} | 4 +- .../OrderXmlExportProvider.cs} | 4 +- .../ProductXlsxExportProvider.cs} | 6 +- .../ProductXmlExportProvider.cs} | 4 +- .../SubscriberCsvExportProvider.cs} | 4 +- .../DataExchange/_Pseudo/AdminController.cs | 19 ++++++ .../DataExchange/_Pseudo/DataExportTask.cs | 62 ++++++++++++++++++ .../SmartStore.Services.csproj | 39 +++++------ .../FeedGoogleMerchantCenterController.cs | 6 +- ...XmlProvider.cs => GmcXmlExportProvider.cs} | 4 +- .../SmartStore.GoogleMerchantCenter.csproj | 2 +- .../Controllers/CategoryController.cs | 4 +- .../Controllers/CustomerController.cs | 10 +-- .../Controllers/ExportController.cs | 2 +- .../Controllers/ManufacturerController.cs | 4 +- .../NewsLetterSubscriptionController.cs | 4 +- .../Controllers/OrderController.cs | 10 +-- .../Controllers/ProductController.cs | 10 +-- 34 files changed, 255 insertions(+), 147 deletions(-) rename src/Libraries/SmartStore.Services/DataExchange/{ExportExecuteResult.cs => DataExportResult.cs} (85%) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs rename src/Libraries/SmartStore.Services/DataExchange/{ExportSegmenter.cs => ExportDataSegmenter.cs} (89%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportTask/ExportDataContextCategory.cs => Internal/CategoryExportContext.cs} (92%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportTask/ExportDataContextCustomer.cs => Internal/CustomerExportContext.cs} (89%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportTask/ExportDataContextManufacturer.cs => Internal/ManufacturerExportContext.cs} (92%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportTask/ExportDataContextOrder.cs => Internal/OrderExportContext.cs} (95%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportTask/ExportDataContextProduct.cs => Internal/ProductExportContext.cs} (96%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportCategoryXmlProvider.cs => Providers/CategoryXmlExportProvider.cs} (96%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportCustomerXlsxProvider.cs => Providers/CustomerXlsxExportProvider.cs} (98%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportCustomerXmlProvider.cs => Providers/CustomerXmlExportProvider.cs} (94%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportManufacturerXmlProvider.cs => Providers/ManufacturerXmlExportProvider.cs} (96%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportOrderXlsxProvider.cs => Providers/OrderXlsxExportProvider.cs} (98%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportOrderXmlProvider.cs => Providers/OrderXmlExportProvider.cs} (99%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportProductXlsxProvider.cs => Providers/ProductXlsxExportProvider.cs} (99%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportProductXmlProvider.cs => Providers/ProductXmlExportProvider.cs} (94%) rename src/Libraries/SmartStore.Services/DataExchange/{ExportProvider/ExportNewsSubscriptionCsvProvider.cs => Providers/SubscriberCsvExportProvider.cs} (93%) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/_Pseudo/AdminController.cs create mode 100644 src/Libraries/SmartStore.Services/DataExchange/_Pseudo/DataExportTask.cs rename src/Plugins/SmartStore.GoogleMerchantCenter/Providers/{GmcExportXmlProvider.cs => GmcXmlExportProvider.cs} (99%) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteResult.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs similarity index 85% rename from src/Libraries/SmartStore.Services/DataExchange/ExportExecuteResult.cs rename to src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs index 6bb8a87429..0e1f708763 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs @@ -5,11 +5,11 @@ namespace SmartStore.Services.DataExchange { [Serializable] - public class ExportExecuteResult + public class DataExportResult { - public ExportExecuteResult() + public DataExportResult() { - Files = new List(); + Files = new List(); } /// @@ -29,7 +29,7 @@ public bool Succeeded /// /// Files created by last export /// - public List Files { get; set; } + public IList Files { get; set; } /// /// The path of the folder with the export files diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs new file mode 100644 index 0000000000..2f0211fd2b --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs @@ -0,0 +1,18 @@ +using SmartStore.Core.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.DataExchange.Deployment +{ + public interface IFilePublisher + { + void Publish(DataExportResult result, ExportDeployment deployment); + } + + /* + Implement without IoC: HttpFilePublisher, EmailFilePublisher, FtpFilePublisher, FileSystemFilePublisher + */ +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportDataSegmenter.cs similarity index 89% rename from src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs rename to src/Libraries/SmartStore.Services/DataExchange/ExportDataSegmenter.cs index 6d5c6fb937..a5be05b008 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportSegmenter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportDataSegmenter.cs @@ -4,7 +4,7 @@ namespace SmartStore.Services.DataExchange { - public interface IExportSegmenterConsumer + public interface IExportDataSegmenterConsumer { /// /// Total number of records @@ -14,7 +14,7 @@ public interface IExportSegmenterConsumer /// /// Gets current data segment /// - List CurrentSegment { get; } + IReadOnlyCollection CurrentSegment { get; } /// /// Reads the next segment @@ -23,7 +23,7 @@ public interface IExportSegmenterConsumer bool ReadNextSegment(); } - internal interface IExportSegmenterProvider : IExportSegmenterConsumer, IDisposable + internal interface IExportDataSegmenterProvider : IExportDataSegmenterConsumer, IDisposable { /// /// Whether there is data available @@ -36,7 +36,7 @@ internal interface IExportSegmenterProvider : IExportSegmenterConsumer, IDisposa int RecordPerSegmentCount { get; set; } } - public class ExportSegmenter : IExportSegmenterProvider where T : BaseEntity + public class ExportDataSegmenter : IExportDataSegmenterProvider where T : BaseEntity { private Func> _load; private Action> _loaded; @@ -53,7 +53,7 @@ public class ExportSegmenter : IExportSegmenterProvider where T : BaseEntity private Queue _data; - public ExportSegmenter( + public ExportDataSegmenter( Func> load, Action> loaded, Func> convert, @@ -127,7 +127,7 @@ public bool HasData /// /// Gets current data segment /// - public List CurrentSegment + public IReadOnlyCollection CurrentSegment { get { @@ -136,7 +136,7 @@ public List CurrentSegment while (_data.Count > 0 && (entity = _data.Dequeue()) != null) { - _convert(entity).ForEach(x => records.Add(x)); + _convert(entity).Each(x => records.Add(x)); if (++_countRecords >= _limit && _limit > 0) return records; diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs index 1459463bfb..17ae53a8bf 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs @@ -11,7 +11,7 @@ public interface IExportExecuteContext /// /// Provides the data to be exported /// - IExportSegmenterConsumer Segmenter { get; } + IExportDataSegmenterConsumer Segmenter { get; } /// /// The store context to be used for the export @@ -116,11 +116,11 @@ public interface IExportExecuteContext public class ExportExecuteContext : IExportExecuteContext { - private ExportExecuteResult _result; + private DataExportResult _result; private CancellationToken _cancellation; private ExportAbortion _providerAbort; - internal ExportExecuteContext(ExportExecuteResult result, CancellationToken cancellation, string folder) + internal ExportExecuteContext(DataExportResult result, CancellationToken cancellation, string folder) { _result = result; _cancellation = cancellation; @@ -129,7 +129,7 @@ internal ExportExecuteContext(ExportExecuteResult result, CancellationToken canc CustomProperties = new Dictionary(); } - public IExportSegmenterConsumer Segmenter { get; set; } + public IExportDataSegmenterConsumer Segmenter { get; set; } public dynamic Store { get; internal set; } public dynamic Customer { get; internal set; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index b36699541b..48c50afc11 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -193,7 +193,7 @@ private List GetLocalized(ExportProfileTaskContext ctx, T entity, pa return (localized.Count == 0 ? null : localized); } - private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, int pageIndex = 0) + private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, int pageIndex = 0) { var offset = ctx.Profile.Offset + (pageIndex * PageSize); @@ -206,13 +206,13 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i switch (ctx.Provider.Value.EntityType) { case ExportEntityType.Product: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetProducts(ctx, skip), entities => { // load data behind navigation properties for current queue in one go - ctx.DataContextProduct = new ExportDataContextProduct(entities, + ctx.DataContextProduct = new ProductExportContext(entities, x => _productAttributeService.Value.GetProductVariantAttributesByProductIds(x, null), x => _productAttributeService.Value.GetProductVariantAttributeCombinations(x), x => _productService.Value.GetTierPricesByProductIds(x, (ctx.Projection.CurrencyId ?? 0) != 0 ? ctx.ContextCustomer : null, ctx.Store.Id), @@ -231,12 +231,12 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; case ExportEntityType.Order: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetOrders(ctx, skip), entities => { - ctx.DataContextOrder = new ExportDataContextOrder(entities, + ctx.DataContextOrder = new OrderExportContext(entities, x => _customerService.Value.GetCustomersByIds(x), x => _customerService.Value.GetRewardPointsHistoriesByCustomerIds(x), x => _addressesService.Value.GetAddressByIds(x), @@ -250,12 +250,12 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; case ExportEntityType.Manufacturer: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetManufacturers(ctx, skip), entities => { - ctx.DataContextManufacturer = new ExportDataContextManufacturer(entities, + ctx.DataContextManufacturer = new ManufacturerExportContext(entities, x => _manufacturerService.Value.GetProductManufacturersByManufacturerIds(x), x => _pictureService.Value.GetPicturesByIds(x) ); @@ -266,12 +266,12 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; case ExportEntityType.Category: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetCategories(ctx, skip), entities => { - ctx.DataContextCategory = new ExportDataContextCategory(entities, + ctx.DataContextCategory = new CategoryExportContext(entities, x => _categoryService.Value.GetProductCategoriesByCategoryIds(x), x => _pictureService.Value.GetPicturesByIds(x) ); @@ -282,12 +282,12 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; case ExportEntityType.Customer: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetCustomers(ctx, skip), entities => { - ctx.DataContextCustomer = new ExportDataContextCustomer(entities, + ctx.DataContextCustomer = new CustomerExportContext(entities, x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer") ); }, @@ -297,7 +297,7 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; case ExportEntityType.NewsLetterSubscription: - ctx.Export.Segmenter = new ExportSegmenter + ctx.Export.Segmenter = new ExportDataSegmenter ( skip => GetNewsLetterSubscriptions(ctx, skip), null, @@ -311,7 +311,7 @@ private IExportSegmenterProvider CreateSegmenter(ExportProfileTaskContext ctx, i break; } - return ctx.Export.Segmenter as IExportSegmenterProvider; + return ctx.Export.Segmenter as IExportDataSegmenterProvider; } private void PrepareProductDescription(ExportProfileTaskContext ctx, dynamic expando, Product product) @@ -2504,7 +2504,7 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) // create info for deployment list in profile edit if (File.Exists(ctx.Export.FilePath)) { - ctx.Result.Files.Add(new ExportExecuteResult.ExportFileInfo + ctx.Result.Files.Add(new DataExportResult.ExportFileInfo { StoreId = ctx.Store.Id, FileName = ctx.Export.FileName @@ -2664,7 +2664,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) { if (!ctx.IsPreview && ctx.Profile.Id != 0) { - ctx.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); + ctx.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); _exportProfileService.Value.UpdateExportProfile(ctx.Profile); } @@ -2773,7 +2773,7 @@ public static FileStreamResult Export(string providerSystemName, string selected error = null; var cancellation = new CancellationTokenSource(TimeSpan.FromHours(3.0)); - var task = AsyncRunner.Run((container, ct) => + var task = AsyncRunner.Run((container, ct) => { var exportTask = new ExportProfileTask(); return exportTask.Execute(providerSystemName, container, ct, null, selectedEntityIds); @@ -2843,7 +2843,7 @@ public void Execute(TaskExecutionContext taskContext) /// Any data passed on IExportExecuteContext.CustomProperties /// Product query that supersede profile filtering /// Export execute result - public ExportExecuteResult Execute( + public DataExportResult Execute( string providerSystemName, IComponentContext context, CancellationToken cancellationToken, @@ -2949,7 +2949,7 @@ public void Preview(ExportProfile profile, Provider provider, I while (segmenter.ReadNextSegment()) { - segmenter.CurrentSegment.ForEach(x => ctx.PreviewData(x)); + segmenter.CurrentSegment.Each(x => ctx.PreviewData(x)); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs index 5fee680634..576b1ffc08 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs @@ -14,16 +14,17 @@ using SmartStore.Core.Plugins; using SmartStore.Services.Tasks; using SmartStore.Utilities; +using SmartStore.Services.DataExchange.Internal; namespace SmartStore.Services.DataExchange.ExportTask { internal class ExportProfileTaskContext { - private ExportDataContextProduct _dataContextProduct; - private ExportDataContextOrder _dataContextOrder; - private ExportDataContextManufacturer _dataContextManufacturer; - private ExportDataContextCategory _dataContextCategory; - private ExportDataContextCustomer _dataContextCustomer; + private ProductExportContext _productExportContext; + private OrderExportContext _orderExportContext; + private ManufacturerExportContext _manufacturerExportContext; + private CategoryExportContext _categoryExportContext; + private CustomerExportContext _customerExportContext; public ExportProfileTaskContext( TaskExecutionContext taskContext, @@ -56,7 +57,7 @@ public ExportProfileTaskContext( RecordsPerStore = new Dictionary(); EntityIdsLoaded = new List(); - Result = new ExportExecuteResult + Result = new DataExportResult { FileFolder = (IsFileBasedExport ? FolderContent : null) }; @@ -140,78 +141,78 @@ public string[] GetDeploymentFiles(ExportDeployment deployment) public HashSet NewsletterSubscriptions { get; set; } // data loaded once per page - public ExportDataContextProduct DataContextProduct + public ProductExportContext DataContextProduct { get { - return _dataContextProduct; + return _productExportContext; } set { - if (_dataContextProduct != null) - _dataContextProduct.Clear(); + if (_productExportContext != null) + _productExportContext.Clear(); - _dataContextProduct = value; + _productExportContext = value; } } - public ExportDataContextOrder DataContextOrder + public OrderExportContext DataContextOrder { get { - return _dataContextOrder; + return _orderExportContext; } set { - if (_dataContextOrder != null) - _dataContextOrder.Clear(); + if (_orderExportContext != null) + _orderExportContext.Clear(); - _dataContextOrder = value; + _orderExportContext = value; } } - public ExportDataContextManufacturer DataContextManufacturer + public ManufacturerExportContext DataContextManufacturer { get { - return _dataContextManufacturer; + return _manufacturerExportContext; } set { - if (_dataContextManufacturer != null) - _dataContextManufacturer.Clear(); + if (_manufacturerExportContext != null) + _manufacturerExportContext.Clear(); - _dataContextManufacturer = value; + _manufacturerExportContext = value; } } - public ExportDataContextCategory DataContextCategory + public CategoryExportContext DataContextCategory { get { - return _dataContextCategory; + return _categoryExportContext; } set { - if (_dataContextCategory != null) - _dataContextCategory.Clear(); + if (_categoryExportContext != null) + _categoryExportContext.Clear(); - _dataContextCategory = value; + _categoryExportContext = value; } } - public ExportDataContextCustomer DataContextCustomer + public CustomerExportContext DataContextCustomer { get { - return _dataContextCustomer; + return _customerExportContext; } set { - if (_dataContextCustomer != null) - _dataContextCustomer.Clear(); + if (_customerExportContext != null) + _customerExportContext.Clear(); - _dataContextCustomer = value; + _customerExportContext = value; } } public ExportExecuteContext Export { get; set; } - public ExportExecuteResult Result { get; set; } + public DataExportResult Result { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs index a2a31ac863..036fa99739 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs @@ -13,7 +13,14 @@ namespace SmartStore.Services.DataExchange public interface IDataExporter { - void Export(DataExportRequest request, CancellationToken cancellationToken); + DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); + + // Handle model conversion for grid in backend's controller + IList Preview(DataExportRequest request); + + // useful for decision making whether export should + // be processed sync or async + long GetDataCount(DataExportRequest request); } public class DataExportRequest diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/CategoryExportContext.cs similarity index 92% rename from src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs rename to src/Libraries/SmartStore.Services/DataExchange/Internal/CategoryExportContext.cs index 8e2efa0f93..179189c0db 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCategory.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/CategoryExportContext.cs @@ -5,9 +5,9 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Media; -namespace SmartStore.Services.DataExchange.ExportTask +namespace SmartStore.Services.DataExchange.Internal { - internal class ExportDataContextCategory + internal class CategoryExportContext { protected List _categoryIds; protected List _pictureIds; @@ -18,7 +18,7 @@ internal class ExportDataContextCategory private LazyMultimap _productCategories; private LazyMultimap _pictures; - public ExportDataContextCategory( + public CategoryExportContext( IEnumerable categories, Func> productCategories, Func> pictures) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/CustomerExportContext.cs similarity index 89% rename from src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs rename to src/Libraries/SmartStore.Services/DataExchange/Internal/CustomerExportContext.cs index f7c027a27d..e765815557 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextCustomer.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/CustomerExportContext.cs @@ -5,9 +5,9 @@ using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; -namespace SmartStore.Services.DataExchange.ExportTask +namespace SmartStore.Services.DataExchange.Internal { - public class ExportDataContextCustomer + public class CustomerExportContext { protected List _customerIds; @@ -15,7 +15,7 @@ public class ExportDataContextCustomer private LazyMultimap _genericAttributes; - public ExportDataContextCustomer( + public CustomerExportContext( IEnumerable customers, Func> genericAttributes) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ManufacturerExportContext.cs similarity index 92% rename from src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs rename to src/Libraries/SmartStore.Services/DataExchange/Internal/ManufacturerExportContext.cs index 9d2002be35..83445889cc 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextManufacturer.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/ManufacturerExportContext.cs @@ -5,9 +5,9 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Media; -namespace SmartStore.Services.DataExchange.ExportTask +namespace SmartStore.Services.DataExchange.Internal { - internal class ExportDataContextManufacturer + internal class ManufacturerExportContext { protected List _manufacturerIds; protected List _pictureIds; @@ -18,7 +18,7 @@ internal class ExportDataContextManufacturer private LazyMultimap _productManufacturers; private LazyMultimap _pictures; - public ExportDataContextManufacturer( + public ManufacturerExportContext( IEnumerable manufacturers, Func> productManufacturers, Func> pictures) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextOrder.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/OrderExportContext.cs similarity index 95% rename from src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextOrder.cs rename to src/Libraries/SmartStore.Services/DataExchange/Internal/OrderExportContext.cs index 38fa4893ae..6ae1998304 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextOrder.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/OrderExportContext.cs @@ -7,9 +7,9 @@ using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Shipping; -namespace SmartStore.Services.DataExchange.ExportTask +namespace SmartStore.Services.DataExchange.Internal { - internal class ExportDataContextOrder + internal class OrderExportContext { protected List _orderIds; protected List _customerIds; @@ -27,7 +27,7 @@ internal class ExportDataContextOrder private LazyMultimap _orderItems; private LazyMultimap _shipments; - public ExportDataContextOrder(IEnumerable orders, + public OrderExportContext(IEnumerable orders, Func> customers, Func> rewardPointsHistory, Func> addresses, diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/ProductExportContext.cs similarity index 96% rename from src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs rename to src/Libraries/SmartStore.Services/DataExchange/Internal/ProductExportContext.cs index bb0d6ea6c1..503e3db6db 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportDataContextProduct.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/ProductExportContext.cs @@ -6,9 +6,9 @@ using SmartStore.Core.Domain.Discounts; using SmartStore.Services.Catalog; -namespace SmartStore.Services.DataExchange.ExportTask +namespace SmartStore.Services.DataExchange.Internal { - internal class ExportDataContextProduct : PriceCalculationContext + internal class ProductExportContext : PriceCalculationContext { private List _productIdsBundleItems; @@ -24,7 +24,7 @@ internal class ExportDataContextProduct : PriceCalculationContext private LazyMultimap _productSpecificationAttributes; private LazyMultimap _productBundleItems; - public ExportDataContextProduct( + public ProductExportContext( IEnumerable products, Func> attributes, Func> attributeCombinations, diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs similarity index 96% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs index 6bd4aa3d2f..5b72bc9d64 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCategoryXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports XML formatted category data to a file @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreCategoryXml")] [FriendlyName("SmartStore XML category export")] [IsHidden(true)] - public class ExportCategoryXmlProvider : IExportProvider + public class CategoryXmlExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs similarity index 98% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs index 2ab8dcb6b0..22a9d229ec 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs @@ -10,7 +10,7 @@ using SmartStore.Core.Plugins; using SmartStore.Services.Customers; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports Excel formatted customer data to a file @@ -18,7 +18,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreCustomerXlsx")] [FriendlyName("SmartStore Excel customer export")] [IsHidden(true)] - public class ExportCustomerXlsxProvider : IExportProvider + public class CustomerXlsxExportProvider : IExportProvider { private string[] Properties { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs similarity index 94% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs index 4573e6c69c..b88d3d8cd3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportCustomerXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports XML formatted customer data to a file @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreCustomerXml")] [FriendlyName("SmartStore XML customer export")] [IsHidden(true)] - public class ExportCustomerXmlProvider : IExportProvider + public class CustomerXmlExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs similarity index 96% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs index 5341411be7..edc60a007d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportManufacturerXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports XML formatted manufacturer data to a file @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreManufacturerXml")] [FriendlyName("SmartStore XML manufacturer export")] [IsHidden(true)] - public class ExportManufacturerXmlProvider : IExportProvider + public class ManufacturerXmlExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs similarity index 98% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs index ba85cc3b0d..9f468e071f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs @@ -7,7 +7,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports Excel formatted order data to a file @@ -15,7 +15,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreOrderXlsx")] [FriendlyName("SmartStore Excel order export")] [IsHidden(true)] - public class ExportOrderXlsxProvider : IExportProvider + public class OrderXlsxExportProvider : IExportProvider { private string[] Properties { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs similarity index 99% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs index 185181c013..a21a94d2f9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportOrderXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports XML formatted order data to a file @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreOrderXml")] [FriendlyName("SmartStore XML order export")] [IsHidden(true)] - public class ExportOrderXmlProvider : IExportProvider + public class OrderXmlExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs similarity index 99% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs index e9ec1bbc59..a433bf5c8d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXlsxProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs @@ -14,7 +14,7 @@ using SmartStore.Services.Localization; using SmartStore.Services.Stores; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports Excel formatted product data to a file @@ -22,7 +22,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreProductXlsx")] [FriendlyName("SmartStore Excel product export")] [IsHidden(true)] - public class ExportProductXlsxProvider : IExportProvider + public class ProductXlsxExportProvider : IExportProvider { private readonly ILanguageService _languageService; private readonly IProductService _productService; @@ -130,7 +130,7 @@ private string[] Properties } } - public ExportProductXlsxProvider( + public ProductXlsxExportProvider( ILanguageService languageService, IProductService productService, IStoreMappingService storeMappingService) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs similarity index 94% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs index 6352c98b03..a0cc291bf1 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportProductXmlProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports XML formatted product data to a file @@ -16,7 +16,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreProductXml")] [FriendlyName("SmartStore XML product export")] [IsHidden(true)] - public class ExportProductXmlProvider : IExportProvider + public class ProductXmlExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs similarity index 93% rename from src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs rename to src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs index 36479f946f..2ca56f48f8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProvider/ExportNewsSubscriptionCsvProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs @@ -5,7 +5,7 @@ using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -namespace SmartStore.Services.DataExchange.ExportProvider +namespace SmartStore.Services.DataExchange.Providers { /// /// Exports CSV formatted newsletter subscription data to a file @@ -13,7 +13,7 @@ namespace SmartStore.Services.DataExchange.ExportProvider [SystemName("Exports.SmartStoreNewsSubscriptionCsv")] [FriendlyName("SmartStore CSV newsletter subscription export")] [IsHidden(true)] - public class ExportNewsSubscriptionCsvProvider : IExportProvider + public class SubscriberCsvExportProvider : IExportProvider { public static string SystemName { diff --git a/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/AdminController.cs b/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/AdminController.cs new file mode 100644 index 0000000000..e0661ccf40 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/AdminController.cs @@ -0,0 +1,19 @@ +//using SmartStore.Core.Infrastructure; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Threading.Tasks; +//using System.Web; +//using System.Web.Mvc; + +//namespace SmartStore.Services.DataExchange +//{ +// internal class AdminController : Controller +// { +// protected ActionResult Export(string providerSystemName, string selectedIds) +// { +// var exporter = EngineContext.Current.Resolve(); +// } +// } +//} diff --git a/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/DataExportTask.cs new file mode 100644 index 0000000000..57c80834f6 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/_Pseudo/DataExportTask.cs @@ -0,0 +1,62 @@ +using SmartStore.Core.Localization; +using SmartStore.Services.Tasks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; + +namespace SmartStore.Services.DataExchange +{ + internal class DataExportTask : ITask + { + private readonly IDataExporter _exporter; + private readonly IExportProfileService _exportProfileService; + + public DataExportTask( + IDataExporter exporter, + IExportProfileService exportProfileService) + { + _exporter = exporter; + _exportProfileService = exportProfileService; + } + + public Localizer T { get; set; } + + public void Execute(TaskExecutionContext ctx) + { + // TODO: proper error handling + + var profileId = ctx.ScheduleTask.Alias.ToInt(); + var profile = _exportProfileService.GetExportProfileById(profileId); + + // TODO: find a better way to transmit selected entity ids (e.g. new TaskExecutionContext.Parameters property) + var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); + var selectedEntityIds = HttpRuntime.Cache[selectedIdsCacheKey] as string; + HttpRuntime.Cache.Remove(selectedIdsCacheKey); + + // load provider + var provider = _exportProfileService.LoadProvider(profile.ProviderSystemName); + if (provider == null) + throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); + + // build export request + var request = new DataExportRequest(profile); + request.ProgressSetter = delegate(int val, int max, string msg) + { + ctx.SetProgress(val, max, msg); + }; + if (selectedEntityIds.HasValue()) + { + request.EntitiesToExport = selectedEntityIds.ToIntArray(); + } + + // process! + _exporter.Export(request, ctx.CancellationToken); + + ctx.CancellationToken.ThrowIfCancellationRequested(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 1c3348629e..f63d565765 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -169,33 +169,36 @@ + - - - - - - - - - - - - + + + + + + + + + + + + - + - - + + - + + + @@ -574,9 +577,7 @@ Nop_Services_EuropaCheckVatService_checkVatService - - - + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs index 7a310a4c18..55425cbc7e 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs @@ -61,7 +61,7 @@ public ActionResult ProductEditTab(int productId) ViewBag.DefaultAgeGroup = T("Common.Auto"); // we do not have export profile context here, so we simply use the first profile - var profile = _exportService.GetExportProfilesBySystemName(GmcExportXmlProvider.SystemName).FirstOrDefault(); + var profile = _exportService.GetExportProfilesBySystemName(GmcXmlExportProvider.SystemName).FirstOrDefault(); if (profile != null) { var config = XmlHelper.Deserialize(profile.ProviderConfigData, typeof(ProfileConfigurationModel)) as ProfileConfigurationModel; @@ -73,12 +73,12 @@ public ActionResult ProductEditTab(int productId) ViewBag.DefaultMaterial = config.Material; ViewBag.DefaultPattern = config.Pattern; - if (config.Gender.HasValue() && config.Gender != GmcExportXmlProvider.Unspecified) + if (config.Gender.HasValue() && config.Gender != GmcXmlExportProvider.Unspecified) { ViewBag.DefaultGender = T("Plugins.Feed.Froogle.Gender" + culture.TextInfo.ToTitleCase(config.Gender)); } - if (config.AgeGroup.HasValue() && config.AgeGroup != GmcExportXmlProvider.Unspecified) + if (config.AgeGroup.HasValue() && config.AgeGroup != GmcXmlExportProvider.Unspecified) { ViewBag.DefaultAgeGroup = T("Plugins.Feed.Froogle.AgeGroup" + culture.TextInfo.ToTitleCase(config.AgeGroup)); } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs similarity index 99% rename from src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs rename to src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index 412737afc7..3f1dac0bd2 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcExportXmlProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -28,7 +28,7 @@ namespace SmartStore.GoogleMerchantCenter.Providers ExportFeatures.OffersBrandFallback, ExportFeatures.CanIncludeMainPicture, ExportFeatures.UsesSpecialPrice)] - public class GmcExportXmlProvider : IExportProvider + public class GmcXmlExportProvider : IExportProvider { private const string _googleNamespace = "http://base.google.com/ns/1.0"; @@ -36,7 +36,7 @@ public class GmcExportXmlProvider : IExportProvider private readonly IMeasureService _measureService; private readonly MeasureSettings _measureSettings; - public GmcExportXmlProvider( + public GmcXmlExportProvider( IGoogleFeedService googleFeedService, IMeasureService measureService, MeasureSettings measureSettings) diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj index 0826ad5831..a2b1919903 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj @@ -176,7 +176,7 @@ - + diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs index 76d0abdaf9..10b7fab549 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs @@ -13,7 +13,7 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Discounts; using SmartStore.Services.Filter; using SmartStore.Services.Helpers; @@ -759,7 +759,7 @@ public ActionResult ExportXml() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportCategoryXmlProvider.SystemName, null); + return Export(CategoryXmlExportProvider.SystemName, null); } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs index 96de99a9d5..ab57549c4e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs @@ -24,7 +24,7 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Directory; using SmartStore.Services.Forums; using SmartStore.Services.Helpers; @@ -1765,7 +1765,7 @@ public ActionResult ExportExcelAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) return AccessDeniedView(); - return Export(ExportCustomerXlsxProvider.SystemName, null); + return Export(CustomerXlsxExportProvider.SystemName, null); } [Compress] @@ -1774,7 +1774,7 @@ public ActionResult ExportExcelSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) return AccessDeniedView(); - return Export(ExportCustomerXlsxProvider.SystemName, selectedIds); + return Export(CustomerXlsxExportProvider.SystemName, selectedIds); } [Compress] @@ -1783,7 +1783,7 @@ public ActionResult ExportXmlAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) return AccessDeniedView(); - return Export(ExportCustomerXmlProvider.SystemName, null); + return Export(CustomerXmlExportProvider.SystemName, null); } [Compress] @@ -1792,7 +1792,7 @@ public ActionResult ExportXmlSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) return AccessDeniedView(); - return Export(ExportCustomerXmlProvider.SystemName, selectedIds); + return Export(CustomerXmlExportProvider.SystemName, selectedIds); } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index a244d1f807..984f3641a1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -244,7 +244,7 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile try { var publicFolder = Path.Combine(HttpRuntime.AppDomainAppPath, ExportProfileTask.PublicFolder); - var resultInfo = XmlHelper.Deserialize(profile.ResultInfo); + var resultInfo = XmlHelper.Deserialize(profile.ResultInfo); if (resultInfo != null && resultInfo.Files != null) { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs index 840cecc2e4..d08dd31393 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs @@ -10,7 +10,7 @@ using SmartStore.Core.Logging; using SmartStore.Services.Catalog; using SmartStore.Services.Common; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; using SmartStore.Services.Media; @@ -444,7 +444,7 @@ public ActionResult ExportXml() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportManufacturerXmlProvider.SystemName, null); + return Export(ManufacturerXmlExportProvider.SystemName, null); } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs index 8ba0f92043..19cccae106 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs @@ -3,7 +3,7 @@ using System.Web.Mvc; using SmartStore.Admin.Models.Messages; using SmartStore.Core.Domain.Common; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Helpers; using SmartStore.Services.Messages; using SmartStore.Services.Security; @@ -156,7 +156,7 @@ public ActionResult ExportCsv() if (!_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) return AccessDeniedView(); - return Export(ExportNewsSubscriptionCsvProvider.SystemName, null); + return Export(SubscriberCsvExportProvider.SystemName, null); } [HttpPost] diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs index f2a096e559..55e8e13585 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs @@ -20,7 +20,7 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; @@ -877,7 +877,7 @@ public ActionResult ExportXmlAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) return AccessDeniedView(); - return Export(ExportOrderXmlProvider.SystemName, null); + return Export(OrderXmlExportProvider.SystemName, null); } [HttpPost, Compress] @@ -886,7 +886,7 @@ public ActionResult ExportXmlSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) return AccessDeniedView(); - return Export(ExportOrderXmlProvider.SystemName, selectedIds); + return Export(OrderXmlExportProvider.SystemName, selectedIds); } [Compress] @@ -895,7 +895,7 @@ public ActionResult ExportExcelAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) return AccessDeniedView(); - return Export(ExportOrderXlsxProvider.SystemName, null); + return Export(OrderXlsxExportProvider.SystemName, null); } [HttpPost, Compress] @@ -904,7 +904,7 @@ public ActionResult ExportExcelSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) return AccessDeniedView(); - return Export(ExportOrderXlsxProvider.SystemName, selectedIds); + return Export(OrderXlsxExportProvider.SystemName, selectedIds); } public ActionResult ExportPdf(bool all, string selectedIds = null) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index 5b79e00770..5e22159a4c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -27,7 +27,7 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.DataExchange.ExportProvider; +using SmartStore.Services.DataExchange.Providers; using SmartStore.Services.Directory; using SmartStore.Services.Discounts; using SmartStore.Services.ExportImport; @@ -2796,7 +2796,7 @@ public ActionResult ExportXmlAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportProductXmlProvider.SystemName, null); + return Export(ProductXmlExportProvider.SystemName, null); } [HttpPost, Compress] @@ -2805,7 +2805,7 @@ public ActionResult ExportXmlSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportProductXmlProvider.SystemName, selectedIds); + return Export(ProductXmlExportProvider.SystemName, selectedIds); } [Compress] @@ -2814,7 +2814,7 @@ public ActionResult ExportExcelAll() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportProductXlsxProvider.SystemName, null); + return Export(ProductXlsxExportProvider.SystemName, null); } [HttpPost, Compress] @@ -2823,7 +2823,7 @@ public ActionResult ExportExcelSelected(string selectedIds) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - return Export(ExportProductXlsxProvider.SystemName, selectedIds); + return Export(ProductXlsxExportProvider.SystemName, selectedIds); } public ActionResult ExportPdf(bool all, string selectedIds = null) From f84479657188817ff57b96744375f0927720342f Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sat, 7 Nov 2015 02:12:51 +0100 Subject: [PATCH 038/732] Export framework: minor renaming --- .../ComponentModel/HybridExpando.cs | 13 +- .../ExportTask/ExportProfileTask.cs | 154 +++++++++--------- .../ExportTask/ExportProfileTaskContext.cs | 19 ++- .../DataExchange/Internal/DynamicEntity.cs | 32 +--- 4 files changed, 98 insertions(+), 120 deletions(-) diff --git a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs index 42061fad53..6697e19d0c 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs @@ -324,12 +324,12 @@ public object this[string key] get { object result = null; - if (TryGetMemberCore(key, out result)) + if (!TryGetMemberCore(key, out result)) { - return result; + throw new KeyNotFoundException(); } - throw new KeyNotFoundException(); + return result; } set { @@ -383,9 +383,10 @@ public bool Contains(KeyValuePair item, bool includeInstanceProp /// public bool Contains(string propertyName, bool includeInstanceProperties = false) { - bool contains = Properties.ContainsKey(propertyName); - if (contains) - return true; + if (Properties.ContainsKey(propertyName)) + { + return true; + } if (includeInstanceProperties && _instance != null) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 48c50afc11..86c909fc27 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -206,13 +206,13 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct switch (ctx.Provider.Value.EntityType) { case ExportEntityType.Product: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetProducts(ctx, skip), entities => { // load data behind navigation properties for current queue in one go - ctx.DataContextProduct = new ProductExportContext(entities, + ctx.ProductExportContext = new ProductExportContext(entities, x => _productAttributeService.Value.GetProductVariantAttributesByProductIds(x, null), x => _productAttributeService.Value.GetProductVariantAttributeCombinations(x), x => _productService.Value.GetTierPricesByProductIds(x, (ctx.Projection.CurrencyId ?? 0) != 0 ? ctx.ContextCustomer : null, ctx.Store.Id), @@ -231,12 +231,12 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; case ExportEntityType.Order: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetOrders(ctx, skip), entities => { - ctx.DataContextOrder = new OrderExportContext(entities, + ctx.OrderExportContext = new OrderExportContext(entities, x => _customerService.Value.GetCustomersByIds(x), x => _customerService.Value.GetRewardPointsHistoriesByCustomerIds(x), x => _addressesService.Value.GetAddressByIds(x), @@ -250,12 +250,12 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; case ExportEntityType.Manufacturer: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetManufacturers(ctx, skip), entities => { - ctx.DataContextManufacturer = new ManufacturerExportContext(entities, + ctx.ManufacturerExportContext = new ManufacturerExportContext(entities, x => _manufacturerService.Value.GetProductManufacturersByManufacturerIds(x), x => _pictureService.Value.GetPicturesByIds(x) ); @@ -266,12 +266,12 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; case ExportEntityType.Category: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetCategories(ctx, skip), entities => { - ctx.DataContextCategory = new CategoryExportContext(entities, + ctx.CategoryExportContext = new CategoryExportContext(entities, x => _categoryService.Value.GetProductCategoriesByCategoryIds(x), x => _pictureService.Value.GetPicturesByIds(x) ); @@ -282,12 +282,12 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; case ExportEntityType.Customer: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetCustomers(ctx, skip), entities => { - ctx.DataContextCustomer = new CustomerExportContext(entities, + ctx.CustomerExportContext = new CustomerExportContext(entities, x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer") ); }, @@ -297,7 +297,7 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; case ExportEntityType.NewsLetterSubscription: - ctx.Export.Segmenter = new ExportDataSegmenter + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter ( skip => GetNewsLetterSubscriptions(ctx, skip), null, @@ -307,11 +307,11 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct break; default: - ctx.Export.Segmenter = null; + ctx.ExecuteContext.Segmenter = null; break; } - return ctx.Export.Segmenter as IExportDataSegmenterProvider; + return ctx.ExecuteContext.Segmenter as IExportDataSegmenterProvider; } private void PrepareProductDescription(ExportProfileTaskContext ctx, dynamic expando, Product product) @@ -352,7 +352,7 @@ private void PrepareProductDescription(ExportProfileTaskContext ctx, dynamic exp else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription || ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndDescription) { - var productManus = ctx.DataContextProduct.ProductManufacturers.Load(product.Id); + var productManus = ctx.ProductExportContext.ProductManufacturers.Load(product.Id); if (productManus != null && productManus.Any()) description = productManus.First().Manufacturer.GetLocalized(x => x.Name, languageId, true, false); @@ -429,7 +429,7 @@ private decimal CalculatePrice(ExportProfileTaskContext ctx, Product product, bo // price type if (ctx.Projection.PriceType.HasValue && !forAttributeCombination) { - var priceCalculationContext = ctx.DataContextProduct as PriceCalculationContext; + var priceCalculationContext = ctx.ProductExportContext as PriceCalculationContext; if (ctx.Projection.PriceType.Value == PriceDisplayType.LowestPrice) { @@ -1486,13 +1486,13 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro pictureSize = ctx.Projection.PictureSize; var perfLoadId = (ctx.IsPreview ? 0 : product.Id); // perf preview (it's a compromise) - var productPictures = ctx.DataContextProduct.ProductPictures.Load(perfLoadId); - var productManufacturers = ctx.DataContextProduct.ProductManufacturers.Load(perfLoadId); - var productCategories = ctx.DataContextProduct.ProductCategories.Load(product.Id); - var productAttributes = ctx.DataContextProduct.Attributes.Load(product.Id); - var productAttributeCombinations = ctx.DataContextProduct.AttributeCombinations.Load(product.Id); - var productTags = ctx.DataContextProduct.ProductTags.Load(perfLoadId); - var specificationAttributes = ctx.DataContextProduct.ProductSpecificationAttributes.Load(perfLoadId); + var productPictures = ctx.ProductExportContext.ProductPictures.Load(perfLoadId); + var productManufacturers = ctx.ProductExportContext.ProductManufacturers.Load(perfLoadId); + var productCategories = ctx.ProductExportContext.ProductCategories.Load(product.Id); + var productAttributes = ctx.ProductExportContext.Attributes.Load(product.Id); + var productAttributeCombinations = ctx.ProductExportContext.AttributeCombinations.Load(product.Id); + var productTags = ctx.ProductExportContext.ProductTags.Load(perfLoadId); + var specificationAttributes = ctx.ProductExportContext.ProductSpecificationAttributes.Load(perfLoadId); dynamic expando = ToExpando(ctx, product); @@ -1609,7 +1609,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro if (product.HasTierPrices) { - var tierPrices = ctx.DataContextProduct.TierPrices.Load(product.Id) + var tierPrices = ctx.ProductExportContext.TierPrices.Load(product.Id) .RemoveDuplicatedQuantities(); expando.TierPrices = tierPrices @@ -1630,7 +1630,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro if (product.HasDiscountsApplied) { - var appliedDiscounts = ctx.DataContextProduct.AppliedDiscounts.Load(product.Id); + var appliedDiscounts = ctx.ProductExportContext.AppliedDiscounts.Load(product.Id); expando.AppliedDiscounts = appliedDiscounts .Select(x => ToExpando(ctx, x)) @@ -1656,7 +1656,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro if (product.ProductType == ProductType.BundledProduct) { - var bundleItems = ctx.DataContextProduct.ProductBundleItems.Load(perfLoadId); + var bundleItems = ctx.ProductExportContext.ProductBundleItems.Load(perfLoadId); expando.ProductBundleItems = bundleItems .Select(x => @@ -1696,7 +1696,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro if (ctx.Supports(ExportFeatures.OffersBrandFallback)) { string brand = null; - var productManus = ctx.DataContextProduct.ProductManufacturers.Load(perfLoadId); + var productManus = ctx.ProductExportContext.ProductManufacturers.Load(perfLoadId); if (productManus != null && productManus.Any()) brand = productManus.First().Manufacturer.GetLocalized(x => x.Name, languageId, true, false); @@ -1851,14 +1851,14 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Order order { var result = new List(); - ctx.DataContextOrder.Addresses.Collect(order.ShippingAddressId ?? 0); + ctx.OrderExportContext.Addresses.Collect(order.ShippingAddressId ?? 0); var perfLoadId = (ctx.IsPreview ? 0 : order.Id); - var addresses = ctx.DataContextOrder.Addresses.Load(ctx.IsPreview ? 0 : order.BillingAddressId); - var customers = ctx.DataContextOrder.Customers.Load(order.CustomerId); - var rewardPointsHistories = ctx.DataContextOrder.RewardPointsHistories.Load(ctx.IsPreview ? 0 : order.CustomerId); - var orderItems = ctx.DataContextOrder.OrderItems.Load(perfLoadId); - var shipments = ctx.DataContextOrder.Shipments.Load(perfLoadId); + var addresses = ctx.OrderExportContext.Addresses.Load(ctx.IsPreview ? 0 : order.BillingAddressId); + var customers = ctx.OrderExportContext.Customers.Load(order.CustomerId); + var rewardPointsHistories = ctx.OrderExportContext.RewardPointsHistories.Load(ctx.IsPreview ? 0 : order.CustomerId); + var orderItems = ctx.OrderExportContext.OrderItems.Load(perfLoadId); + var shipments = ctx.OrderExportContext.Shipments.Load(perfLoadId); dynamic expando = ToExpando(ctx, order); @@ -1918,13 +1918,13 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Manufacture { var result = new List(); - var productManufacturers = ctx.DataContextManufacturer.ProductManufacturers.Load(manu.Id); + var productManufacturers = ctx.ManufacturerExportContext.ProductManufacturers.Load(manu.Id); dynamic expando = ToExpando(ctx, manu); if (!ctx.IsPreview && manu.PictureId.HasValue) { - var pictures = ctx.DataContextManufacturer.Pictures.Load(manu.PictureId.Value); + var pictures = ctx.ManufacturerExportContext.Pictures.Load(manu.PictureId.Value); if (pictures.Count > 0) expando.Picture = ToExpando(ctx, pictures.First(), _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize); @@ -1954,13 +1954,13 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Category ca { var result = new List(); - var productCategories = ctx.DataContextCategory.ProductCategories.Load(category.Id); + var productCategories = ctx.CategoryExportContext.ProductCategories.Load(category.Id); dynamic expando = ToExpando(ctx, category); if (!ctx.IsPreview && category.PictureId.HasValue) { - var pictures = ctx.DataContextCategory.Pictures.Load(category.PictureId.Value); + var pictures = ctx.CategoryExportContext.Pictures.Load(category.PictureId.Value); if (pictures.Count > 0) expando.Picture = ToExpando(ctx, pictures.First(), _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); @@ -1991,7 +1991,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Customer cu var result = new List(); var perfLoadId = (ctx.IsPreview ? 0 : customer.Id); - var genericAttributes = ctx.DataContextCustomer.GenericAttributes.Load(perfLoadId); + var genericAttributes = ctx.CustomerExportContext.GenericAttributes.Load(perfLoadId); dynamic expando = ToExpando(ctx, customer); @@ -2142,7 +2142,7 @@ private List GetProducts(ExportProfileTaskContext ctx, int skip) { SetProgress(ctx, products.Count); - _services.DbContext.DetachEntities(result); + _services.DbContext.DetachEntities(result); } catch { } @@ -2430,7 +2430,7 @@ private List Init(ExportProfileTaskContext ctx, int? totalRecords = null) private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) { - if (ctx.Export.Abort != ExportAbortion.None) + if (ctx.ExecuteContext.Abort != ExportAbortion.None) return; int fileIndex = 0; @@ -2459,15 +2459,15 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) ctx.Log.Information(logHead.ToString()); } - ctx.Export.Store = ToExpando(ctx, ctx.Store); + ctx.ExecuteContext.Store = ToExpando(ctx, ctx.Store); - ctx.Export.MaxFileNameLength = _dataExchangeSettings.Value.MaxFileNameLength; + ctx.ExecuteContext.MaxFileNameLength = _dataExchangeSettings.Value.MaxFileNameLength; - ctx.Export.FileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); + ctx.ExecuteContext.FileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); - ctx.Export.HasPublicDeployment = ctx.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); + ctx.ExecuteContext.HasPublicDeployment = ctx.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); - ctx.Export.PublicFolderPath = (ctx.Export.HasPublicDeployment ? Path.Combine(HttpRuntime.AppDomainAppPath, PublicFolder) : null); + ctx.ExecuteContext.PublicFolderPath = (ctx.ExecuteContext.HasPublicDeployment ? Path.Combine(HttpRuntime.AppDomainAppPath, PublicFolder) : null); using (var segmenter = CreateSegmenter(ctx)) @@ -2482,59 +2482,59 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) ctx.Log.Information("There are no records to export"); } - while (ctx.Export.Abort == ExportAbortion.None && segmenter.HasData) + while (ctx.ExecuteContext.Abort == ExportAbortion.None && segmenter.HasData) { segmenter.RecordPerSegmentCount = 0; - ctx.Export.RecordsSucceeded = 0; + ctx.ExecuteContext.RecordsSucceeded = 0; try { if (ctx.IsFileBasedExport) { - var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.Export.MaxFileNameLength); + var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); - ctx.Export.FileName = resolvedPattern + ctx.Export.FileExtension; - ctx.Export.FilePath = Path.Combine(ctx.Export.Folder, ctx.Export.FileName); + ctx.ExecuteContext.FileName = resolvedPattern + ctx.ExecuteContext.FileExtension; + ctx.ExecuteContext.FilePath = Path.Combine(ctx.ExecuteContext.Folder, ctx.ExecuteContext.FileName); - if (ctx.Export.HasPublicDeployment) - ctx.Export.PublicFileUrl = ctx.Store.Url.EnsureEndsWith("/") + PublicFolder.EnsureEndsWith("/") + ctx.Export.FileName; + if (ctx.ExecuteContext.HasPublicDeployment) + ctx.ExecuteContext.PublicFileUrl = ctx.Store.Url.EnsureEndsWith("/") + PublicFolder.EnsureEndsWith("/") + ctx.ExecuteContext.FileName; - ctx.Provider.Value.Execute(ctx.Export); + ctx.Provider.Value.Execute(ctx.ExecuteContext); // create info for deployment list in profile edit - if (File.Exists(ctx.Export.FilePath)) + if (File.Exists(ctx.ExecuteContext.FilePath)) { ctx.Result.Files.Add(new DataExportResult.ExportFileInfo { StoreId = ctx.Store.Id, - FileName = ctx.Export.FileName + FileName = ctx.ExecuteContext.FileName }); } } else { - ctx.Provider.Value.Execute(ctx.Export); + ctx.Provider.Value.Execute(ctx.ExecuteContext); } - ctx.Log.Information("Provider reports {0} successful exported record(s)".FormatInvariant(ctx.Export.RecordsSucceeded)); + ctx.Log.Information("Provider reports {0} successful exported record(s)".FormatInvariant(ctx.ExecuteContext.RecordsSucceeded)); } catch (Exception exc) { - ctx.Export.Abort = ExportAbortion.Hard; + ctx.ExecuteContext.Abort = ExportAbortion.Hard; ctx.Log.Error("The provider failed to execute the export: " + exc.ToAllMessages(), exc); ctx.Result.LastError = exc.ToString(); } - if (ctx.Export.IsMaxFailures) + if (ctx.ExecuteContext.IsMaxFailures) ctx.Log.Warning("Export aborted. The maximum number of failures has been reached"); if (ctx.TaskContext.CancellationToken.IsCancellationRequested) ctx.Log.Warning("Export aborted. A cancellation has been requested"); } - if (ctx.Export.Abort != ExportAbortion.Hard) + if (ctx.ExecuteContext.Abort != ExportAbortion.Hard) { - ctx.Provider.Value.OnExecuted(ctx.Export); + ctx.Provider.Value.OnExecuted(ctx.ExecuteContext); } } } @@ -2559,7 +2559,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) } ctx.Log = logger; - ctx.Export.Log = logger; + ctx.ExecuteContext.Log = logger; ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); if (ctx.Profile.ProviderConfigData.HasValue()) @@ -2567,7 +2567,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) var configInfo = ctx.Provider.Value.ConfigurationInfo; if (configInfo != null) { - ctx.Export.ConfigurationData = XmlHelper.Deserialize(ctx.Profile.ProviderConfigData, configInfo.ModelType); + ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(ctx.Profile.ProviderConfigData, configInfo.ModelType); } } @@ -2601,14 +2601,14 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) var stores = Init(ctx); - ctx.Export.Language = ToExpando(ctx, ctx.ContextLanguage); - ctx.Export.Customer = ToExpando(ctx, ctx.ContextCustomer); - ctx.Export.Currency = ToExpando(ctx, ctx.ContextCurrency); + ctx.ExecuteContext.Language = ToExpando(ctx, ctx.ContextLanguage); + ctx.ExecuteContext.Customer = ToExpando(ctx, ctx.ContextCustomer); + ctx.ExecuteContext.Currency = ToExpando(ctx, ctx.ContextCurrency); stores.ForEach(x => ExportCoreInner(ctx, x)); } - if (!ctx.IsPreview && ctx.Export.Abort != ExportAbortion.Hard) + if (!ctx.IsPreview && ctx.ExecuteContext.Abort != ExportAbortion.Hard) { if (ctx.IsFileBasedExport) { @@ -2673,7 +2673,7 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) try { - if (ctx.IsFileBasedExport && ctx.Export.Abort != ExportAbortion.Hard && ctx.Profile.Cleanup) + if (ctx.IsFileBasedExport && ctx.ExecuteContext.Abort != ExportAbortion.Hard && ctx.Profile.Cleanup) { FileSystemHelper.ClearDirectory(ctx.FolderContent, false); } @@ -2691,21 +2691,21 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) ctx.CategoryPathes.Clear(); ctx.Categories.Clear(); ctx.EntityIdsSelected.Clear(); - ctx.DataContextProduct = null; - ctx.DataContextOrder = null; - ctx.DataContextManufacturer = null; - ctx.DataContextCategory = null; - ctx.DataContextCustomer = null; - - ctx.Export.CustomProperties.Clear(); - ctx.Export.Log = null; + ctx.ProductExportContext = null; + ctx.OrderExportContext = null; + ctx.ManufacturerExportContext = null; + ctx.CategoryExportContext = null; + ctx.CustomerExportContext = null; + + ctx.ExecuteContext.CustomProperties.Clear(); + ctx.ExecuteContext.Log = null; ctx.Log = null; } catch { } } } - if (ctx.IsPreview || ctx.Export.Abort == ExportAbortion.Hard) + if (ctx.IsPreview || ctx.ExecuteContext.Abort == ExportAbortion.Hard) return; // post process order entities @@ -2874,7 +2874,7 @@ public DataExportResult Execute( if (customProperties != null) { foreach (var item in customProperties) - ctx.Export.CustomProperties.Add(item.Key, item.Value); + ctx.ExecuteContext.CustomProperties.Add(item.Key, item.Value); } ExportCoreOuter(ctx); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs index 576b1ffc08..8c1286b5d6 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs @@ -62,8 +62,8 @@ public ExportProfileTaskContext( FileFolder = (IsFileBasedExport ? FolderContent : null) }; - Export = new ExportExecuteContext(Result, TaskContext.CancellationToken, FolderContent); - Export.Projection = XmlHelper.Deserialize(profile.Projection); + ExecuteContext = new ExportExecuteContext(Result, TaskContext.CancellationToken, FolderContent); + ExecuteContext.Projection = XmlHelper.Deserialize(profile.Projection); } public List EntityIdsSelected { get; private set; } @@ -141,7 +141,7 @@ public string[] GetDeploymentFiles(ExportDeployment deployment) public HashSet NewsletterSubscriptions { get; set; } // data loaded once per page - public ProductExportContext DataContextProduct + public ProductExportContext ProductExportContext { get { @@ -155,7 +155,8 @@ public ProductExportContext DataContextProduct _productExportContext = value; } } - public OrderExportContext DataContextOrder + + public OrderExportContext OrderExportContext { get { @@ -169,7 +170,8 @@ public OrderExportContext DataContextOrder _orderExportContext = value; } } - public ManufacturerExportContext DataContextManufacturer + + public ManufacturerExportContext ManufacturerExportContext { get { @@ -183,7 +185,8 @@ public ManufacturerExportContext DataContextManufacturer _manufacturerExportContext = value; } } - public CategoryExportContext DataContextCategory + + public CategoryExportContext CategoryExportContext { get { @@ -197,7 +200,7 @@ public CategoryExportContext DataContextCategory _categoryExportContext = value; } } - public CustomerExportContext DataContextCustomer + public CustomerExportContext CustomerExportContext { get { @@ -212,7 +215,7 @@ public CustomerExportContext DataContextCustomer } } - public ExportExecuteContext Export { get; set; } + public ExportExecuteContext ExecuteContext { get; set; } public DataExportResult Result { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs index 6940371341..287927315d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntity.cs @@ -1,11 +1,5 @@ -using SmartStore.ComponentModel; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Dynamic; -using SmartStore.Utilities; +using System.Collections.Generic; +using SmartStore.ComponentModel; namespace SmartStore.Services.DataExchange.Internal { @@ -14,7 +8,7 @@ internal class DynamicEntity : HybridExpando public DynamicEntity(DynamicEntity dynamicEntity) : this(dynamicEntity.WrappedObject) { - base.Properties.Merge(dynamicEntity, true); + MergeRange(dynamicEntity); } public DynamicEntity(object entity) @@ -39,26 +33,6 @@ public void MergeRange(IDictionary other) protected override bool TrySetMemberCore(string name, object value) { - // Virtual navigation properties of entities should not be set as is. - // A previously created ExpandoEntity instance is set instead. - // But assigning the value to the wrapped entity instance would result - // in a type mismatch error, as both types obviously don't match. - // Therefore we save reference values in the local values dictionary. - - var pi = base.GetPropertyInfo(name); - - if (pi != null && (pi.PropertyType.IsPredefinedType() || !pi.GetMethod.IsVirtual)) - { - // Property exists, is NOT complex, or IS complex but NOT virtual - try - { - Fasterflect.PropertyInfoExtensions.Set(pi, WrappedObject, value); - return true; - } - catch { } - } - - // Property does not exist, or is virtual complex Properties[name] = value; return true; } From 2123d82c033d56894346cb9047e8744fa46689f9 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sat, 7 Nov 2015 11:33:03 +0100 Subject: [PATCH 039/732] Refactoring: Cleaner not to have MVC related code in service layer --- .../201509150931528_ExportFramework2.cs | 4 + .../SmartStore.Services/Blogs/BlogService.cs | 51 --------- .../SmartStore.Services/Blogs/IBlogService.cs | 10 -- .../Catalog/IProductService.cs | 9 -- .../Catalog/ProductService.cs | 87 +-------------- .../Forums/ForumService.cs | 103 ------------------ .../Forums/IForumService.cs | 16 --- .../SmartStore.Services/News/INewsService.cs | 10 -- .../SmartStore.Services/News/NewsService.cs | 56 ---------- .../Controllers/BlogController.cs | 43 +++++++- .../Controllers/BoardsController.cs | 76 ++++++++++++- .../Controllers/CatalogController.cs | 58 +++++++++- .../Controllers/NewsController.cs | 39 ++++++- 13 files changed, 212 insertions(+), 350 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 4cf5af7438..174f2d1f44 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -59,6 +59,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Edited payment method '{0}' ({1})", "Zahlungsart '{0}' ({1}) bearbeitet"); + builder.AddOrUpdate("Admin.Configuration.Settings.Blog.ShowHeaderRSSUrl.Hint", + "Check to enable the blog RSS feed link in customers browser address bar.", + "Legt fest, ob der RSS-Feed-Link in der Adressleiste des Browsers angezeigt werden soll."); + builder.AddOrUpdate("Admin.System.SeNames", "SEO Names", "SEO Namen"); builder.Delete("Admin.System.SeNames.DeleteSelected"); diff --git a/src/Libraries/SmartStore.Services/Blogs/BlogService.cs b/src/Libraries/SmartStore.Services/Blogs/BlogService.cs index c616abefbe..f3bb8103bb 100644 --- a/src/Libraries/SmartStore.Services/Blogs/BlogService.cs +++ b/src/Libraries/SmartStore.Services/Blogs/BlogService.cs @@ -1,16 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.ServiceModel.Syndication; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Blogs; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Events; using SmartStore.Services.Localization; -using SmartStore.Services.Seo; -using SmartStore.Utilities; namespace SmartStore.Services.Blogs { @@ -260,53 +256,6 @@ public virtual void UpdateCommentTotals(BlogPost blogPost) UpdateBlogPost(blogPost); } - /// - /// Creates a RSS feed with blog posts - /// - /// UrlHelper to generate URLs - /// Language identifier - /// SmartSyndicationFeed object - public virtual SmartSyndicationFeed CreateRssFeed(UrlHelper urlHelper, int languageId) - { - if (urlHelper == null) - throw new ArgumentNullException("urlHelper"); - - DateTime? maxAge = null; - var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; - var selfLink = urlHelper.RouteUrl("BlogRSS", new { languageId = languageId }, protocol); - var blogLink = urlHelper.RouteUrl("Blog", null, protocol); - - var title = "{0} - Blog".FormatInvariant(_services.StoreContext.CurrentStore.Name); - - if (_blogSettings.MaxAgeInDays > 0) - maxAge = DateTime.UtcNow.Subtract(new TimeSpan(_blogSettings.MaxAgeInDays, 0, 0, 0)); - - var language = _languageService.GetLanguageById(languageId); - var feed = new SmartSyndicationFeed(new Uri(blogLink), title); - - feed.AddNamespaces(false); - feed.Init(selfLink, language); - - if (!_blogSettings.Enabled) - return feed; - - var items = new List(); - var blogPosts = GetAllBlogPosts(_services.StoreContext.CurrentStore.Id, languageId, null, null, 0, int.MaxValue, false, maxAge); - - foreach (var blogPost in blogPosts) - { - var blogPostUrl = urlHelper.RouteUrl("BlogPost", new { SeName = blogPost.GetSeName(blogPost.LanguageId, ensureTwoPublishedLanguages: false) }, "http"); - - var item = feed.CreateItem(blogPost.Title, blogPost.Body, blogPostUrl, blogPost.CreatedOnUtc); - - items.Add(item); - } - - feed.Items = items; - - return feed; - } - #endregion } } diff --git a/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs b/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs index 983932b0bf..682ddbe191 100644 --- a/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs +++ b/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.Blogs; -using SmartStore.Utilities; namespace SmartStore.Services.Blogs { @@ -79,13 +77,5 @@ IPagedList GetAllBlogPostsByTag(int storeId, int languageId, string ta /// /// Blog post void UpdateCommentTotals(BlogPost blogPost); - - /// - /// Creates a RSS feed with blog posts - /// - /// UrlHelper to generate URLs - /// Language identifier - /// SmartSyndicationFeed object - SmartSyndicationFeed CreateRssFeed(UrlHelper urlHelper, int languageId); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductService.cs b/src/Libraries/SmartStore.Services/Catalog/IProductService.cs index 69a8c2254b..eb7dfa9fb9 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductService.cs @@ -2,14 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Web.Mvc; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Orders; -using SmartStore.Utilities; namespace SmartStore.Services.Catalog { @@ -156,13 +154,6 @@ public partial interface IProductService /// Product void UpdateHasDiscountsApplied(Product product); - /// - /// Creates a RSS feed with recently added products - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - SmartSyndicationFeed CreateRecentlyAddedProductsRssFeed(UrlHelper urlHelper); - /// /// Get product tags by product identifiers /// diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs index c18f230bf5..6f273c7bfa 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs @@ -4,8 +4,6 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; -using System.ServiceModel.Syndication; -using System.Web.Mvc; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Caching; @@ -15,17 +13,13 @@ using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Events; using SmartStore.Services.Localization; -using SmartStore.Services.Media; using SmartStore.Services.Messages; using SmartStore.Services.Orders; -using SmartStore.Services.Seo; -using SmartStore.Utilities; namespace SmartStore.Services.Catalog { @@ -64,9 +58,6 @@ public partial class ProductService : IProductService private readonly LocalizationSettings _localizationSettings; private readonly CommonSettings _commonSettings; private readonly ICommonServices _services; - private readonly CatalogSettings _catalogSettings; - private readonly MediaSettings _mediaSettings; - private readonly IPictureService _pictureService; #endregion @@ -117,10 +108,7 @@ public ProductService( ICacheManager cacheManager, LocalizationSettings localizationSettings, CommonSettings commonSettings, - ICommonServices services, - CatalogSettings catalogSettings, - MediaSettings mediaSettings, - IPictureService pictureService) + ICommonServices services) { this._productRepository = productRepository; this._relatedProductRepository = relatedProductRepository; @@ -143,9 +131,6 @@ public ProductService( this._localizationSettings = localizationSettings; this._commonSettings = commonSettings; this._services = services; - this._catalogSettings = catalogSettings; - this._mediaSettings = mediaSettings; - this._pictureService = pictureService; this.QuerySettings = DbQuerySettings.Default; } @@ -1369,76 +1354,6 @@ public virtual void UpdateHasDiscountsApplied(Product product) UpdateProduct(product); } - /// - /// Creates a RSS feed with recently added products - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - public virtual SmartSyndicationFeed CreateRecentlyAddedProductsRssFeed(UrlHelper urlHelper) - { - if (urlHelper == null) - throw new ArgumentNullException("urlHelper"); - - var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; - var selfLink = urlHelper.RouteUrl("RecentlyAddedProductsRSS", null, protocol); - var recentProductsLink = urlHelper.RouteUrl("RecentlyAddedProducts", null, protocol); - - var title = "{0} - {1}".FormatInvariant(_services.StoreContext.CurrentStore.Name, _services.Localization.GetResource("RSS.RecentlyAddedProducts")); - - var feed = new SmartSyndicationFeed(new Uri(recentProductsLink), title, _services.Localization.GetResource("RSS.InformationAboutProducts")); - - feed.AddNamespaces(true); - feed.Init(selfLink, _services.WorkContext.WorkingLanguage); - - if (!_catalogSettings.RecentlyAddedProductsEnabled) - return feed; - - var items = new List(); - var searchContext = new ProductSearchContext - { - LanguageId = _services.WorkContext.WorkingLanguage.Id, - OrderBy = ProductSortingEnum.CreatedOn, - PageSize = _catalogSettings.RecentlyAddedProductsNumber, - StoreId = _services.StoreContext.CurrentStoreIdIfMultiStoreMode, - VisibleIndividuallyOnly = true - }; - - var products = SearchProducts(searchContext); - var storeUrl = _services.StoreContext.CurrentStore.Url; - - foreach (var product in products) - { - string productUrl = urlHelper.RouteUrl("Product", new { SeName = product.GetSeName() }, "http"); - if (productUrl.HasValue()) - { - var item = feed.CreateItem( - product.GetLocalized(x => x.Name), - product.GetLocalized(x => x.ShortDescription), - productUrl, - product.CreatedOnUtc, - product.FullDescription); - - try - { - // we add only the first picture - var picture = product.ProductPictures.OrderBy(x => x.DisplayOrder).Select(x => x.Picture).FirstOrDefault(); - - if (picture != null) - { - feed.AddEnclosue(item, picture, _pictureService.GetPictureUrl(picture, _mediaSettings.ProductDetailsPictureSize, false, storeUrl)); - } - } - catch { } - - items.Add(item); - } - } - - feed.Items = items; - - return feed; - } - public virtual Multimap GetProductTagsByProductIds(int[] productIds) { Guard.ArgumentNotNull(() => productIds); diff --git a/src/Libraries/SmartStore.Services/Forums/ForumService.cs b/src/Libraries/SmartStore.Services/Forums/ForumService.cs index c9f9e63dbd..5871f34ab6 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumService.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumService.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.ServiceModel.Syndication; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; @@ -12,10 +10,7 @@ using SmartStore.Core.Events; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.Localization; using SmartStore.Services.Messages; -using SmartStore.Services.Seo; -using SmartStore.Utilities; namespace SmartStore.Services.Forums { @@ -1477,104 +1472,6 @@ public virtual int CalculateTopicPageIndex(int forumTopicId, int pageSize, int p return pageIndex; } - /// - /// Creates a RSS feed with active discussions - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - public virtual SmartSyndicationFeed CreateActiveDiscussionsRssFeed(UrlHelper urlHelper, int forumId) - { - if (urlHelper == null) - throw new ArgumentNullException("urlHelper"); - - var language = _services.WorkContext.WorkingLanguage; - var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; - var selfLink = urlHelper.Action("ActiveDiscussionsRSS", "Boards", null, protocol); - var discussionLink = urlHelper.Action("ActiveDiscussions", "Boards", null, protocol); - - var title = "{0} - {1}".FormatInvariant(_services.StoreContext.CurrentStore.Name, _services.Localization.GetResource("Forum.ActiveDiscussionsFeedTitle")); - - var feed = new SmartSyndicationFeed(new Uri(discussionLink), title, _services.Localization.GetResource("Forum.ActiveDiscussionsFeedDescription")); - - feed.AddNamespaces(false); - feed.Init(selfLink, language); - - if (!_forumSettings.ActiveDiscussionsFeedEnabled) - return feed; - - var items = new List(); - var topics = GetActiveTopics(forumId, _forumSettings.ActiveDiscussionsFeedCount); - - var viewsText = _services.Localization.GetResource("Forum.Views"); - var repliesText = _services.Localization.GetResource("Forum.Replies"); - - foreach (var topic in topics) - { - string topicUrl = urlHelper.RouteUrl("TopicSlug", new { id = topic.Id, slug = topic.GetSeName() }, "http"); - var synopsis = "{0}: {1}, {2}: {3}".FormatInvariant(repliesText, topic.NumReplies, viewsText, topic.Views); - - var item = feed.CreateItem(topic.Subject, synopsis, topicUrl, topic.LastPostTime ?? topic.UpdatedOnUtc); - - items.Add(item); - } - - feed.Items = items; - - return feed; - } - - /// - /// Creates a RSS feed with forum topics - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - public virtual SmartSyndicationFeed CreateForumRssFeed(UrlHelper urlHelper, int forumId) - { - if (urlHelper == null) - throw new ArgumentNullException("urlHelper"); - - var language = _services.WorkContext.WorkingLanguage; - var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; - var selfLink = urlHelper.Action("ForumRSS", "Boards", null, protocol); - var forumLink = urlHelper.Action("Forum", "Boards", new { id = forumId }, protocol); - - var feed = new SmartSyndicationFeed(new Uri(forumLink), _services.StoreContext.CurrentStore.Name, _services.Localization.GetResource("Forum.ForumFeedDescription")); - - feed.AddNamespaces(false); - feed.Init(selfLink, language); - - if (!_forumSettings.ForumFeedsEnabled) - return feed; - - var forum = GetForumById(forumId); - - if (forum == null) - return feed; - - feed.Title = new TextSyndicationContent("{0} - {1}".FormatInvariant(_services.StoreContext.CurrentStore.Name, forum.GetLocalized(x => x.Name, language.Id))); - - var items = new List(); - var topics = GetAllTopics(forumId, 0, string.Empty, ForumSearchType.All, 0, 0, _forumSettings.ForumFeedCount); - - var viewsText = _services.Localization.GetResource("Forum.Views"); - var repliesText = _services.Localization.GetResource("Forum.Replies"); - - foreach (var topic in topics) - { - string topicUrl = urlHelper.RouteUrl("TopicSlug", new { id = topic.Id, slug = topic.GetSeName() }, "http"); - var synopsis = "{0}: {1}, {2}: {3}".FormatInvariant(repliesText, topic.NumReplies, viewsText, topic.Views); - - var item = feed.CreateItem(topic.Subject, synopsis, topicUrl, topic.LastPostTime ?? topic.UpdatedOnUtc); - - items.Add(item); - } - - feed.Items = items; - - return feed; - } - - #endregion } } diff --git a/src/Libraries/SmartStore.Services/Forums/IForumService.cs b/src/Libraries/SmartStore.Services/Forums/IForumService.cs index efcd21fa32..46e0f2f428 100644 --- a/src/Libraries/SmartStore.Services/Forums/IForumService.cs +++ b/src/Libraries/SmartStore.Services/Forums/IForumService.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Forums; -using SmartStore.Utilities; namespace SmartStore.Services.Forums { @@ -346,19 +344,5 @@ IPagedList GetAllSubscriptions(int customerId, int forumId, /// Post identifier /// Page index int CalculateTopicPageIndex(int forumTopicId, int pageSize, int postId); - - /// - /// Creates a RSS feed with active discussions - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - SmartSyndicationFeed CreateActiveDiscussionsRssFeed(UrlHelper urlHelper, int forumId); - - /// - /// Creates a RSS feed with forum topics - /// - /// UrlHelper to generate URLs - /// SmartSyndicationFeed object - SmartSyndicationFeed CreateForumRssFeed(UrlHelper urlHelper, int forumId); } } diff --git a/src/Libraries/SmartStore.Services/News/INewsService.cs b/src/Libraries/SmartStore.Services/News/INewsService.cs index c07cceb570..d25a8d8afa 100644 --- a/src/Libraries/SmartStore.Services/News/INewsService.cs +++ b/src/Libraries/SmartStore.Services/News/INewsService.cs @@ -1,9 +1,7 @@ using System; using System.Linq; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.News; -using SmartStore.Utilities; namespace SmartStore.Services.News { @@ -61,13 +59,5 @@ public partial interface INewsService /// /// News item void UpdateCommentTotals(NewsItem newsItem); - - /// - /// Creates a RSS feed with news items - /// - /// UrlHelper to generate URLs - /// Language identifier - /// SmartSyndicationFeed object - SmartSyndicationFeed CreateRssFeed(UrlHelper urlHelper, int languageId); } } diff --git a/src/Libraries/SmartStore.Services/News/NewsService.cs b/src/Libraries/SmartStore.Services/News/NewsService.cs index 66adadf90e..743ab7f9f4 100644 --- a/src/Libraries/SmartStore.Services/News/NewsService.cs +++ b/src/Libraries/SmartStore.Services/News/NewsService.cs @@ -1,16 +1,10 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.ServiceModel.Syndication; -using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.News; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Events; -using SmartStore.Services.Localization; -using SmartStore.Services.Seo; -using SmartStore.Utilities; namespace SmartStore.Services.News { @@ -24,7 +18,6 @@ public partial class NewsService : INewsService private readonly IRepository _newsItemRepository; private readonly IRepository _storeMappingRepository; private readonly ICommonServices _services; - private readonly ILanguageService _languageService; private readonly NewsSettings _newsSettings; @@ -35,13 +28,11 @@ public partial class NewsService : INewsService public NewsService(IRepository newsItemRepository, IRepository storeMappingRepository, ICommonServices services, - ILanguageService languageService, NewsSettings newsSettings) { _newsItemRepository = newsItemRepository; _storeMappingRepository = storeMappingRepository; _services = services; - _languageService = languageService; _newsSettings = newsSettings; this.QuerySettings = DbQuerySettings.Default; @@ -211,53 +202,6 @@ public virtual void UpdateCommentTotals(NewsItem newsItem) UpdateNews(newsItem); } - /// - /// Creates a RSS feed with news items - /// - /// UrlHelper to generate URLs - /// Language identifier - /// SmartSyndicationFeed object - public virtual SmartSyndicationFeed CreateRssFeed(UrlHelper urlHelper, int languageId) - { - if (urlHelper == null) - throw new ArgumentNullException("urlHelper"); - - DateTime? maxAge = null; - var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; - var selfLink = urlHelper.Action("rss", "News", new { languageId = languageId }, protocol); - var newsLink = urlHelper.RouteUrl("NewsArchive", null, protocol); - - var title = "{0} - News".FormatInvariant(_services.StoreContext.CurrentStore.Name); - - if (_newsSettings.MaxAgeInDays > 0) - maxAge = DateTime.UtcNow.Subtract(new TimeSpan(_newsSettings.MaxAgeInDays, 0, 0, 0)); - - var language = _languageService.GetLanguageById(languageId); - var feed = new SmartSyndicationFeed(new Uri(newsLink), title); - - feed.AddNamespaces(true); - feed.Init(selfLink, language); - - if (!_newsSettings.Enabled) - return feed; - - var items = new List(); - var newsItems = GetAllNews(languageId, _services.StoreContext.CurrentStore.Id, 0, int.MaxValue, false, maxAge); - - foreach (var news in newsItems) - { - var newsUrl = urlHelper.RouteUrl("NewsItem", new { SeName = news.GetSeName(news.LanguageId, ensureTwoPublishedLanguages: false) }, "http"); - - var item = feed.CreateItem(news.Title, news.Short, newsUrl, news.CreatedOnUtc, news.Full); - - items.Add(item); - } - - feed.Items = items; - - return feed; - } - #endregion } } diff --git a/src/Presentation/SmartStore.Web/Controllers/BlogController.cs b/src/Presentation/SmartStore.Web/Controllers/BlogController.cs index ad683bda9f..88aa37ffba 100644 --- a/src/Presentation/SmartStore.Web/Controllers/BlogController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/BlogController.cs @@ -6,28 +6,27 @@ using System.Web.Routing; using SmartStore.Core; using SmartStore.Core.Caching; -using SmartStore.Core.Domain; using SmartStore.Core.Domain.Blogs; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Logging; using SmartStore.Services.Blogs; using SmartStore.Services.Common; using SmartStore.Services.Customers; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; -using SmartStore.Core.Logging; using SmartStore.Services.Media; using SmartStore.Services.Messages; using SmartStore.Services.Seo; using SmartStore.Services.Stores; -using SmartStore.Web.Framework; +using SmartStore.Utilities; using SmartStore.Web.Framework.Controllers; +using SmartStore.Web.Framework.Mvc; using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.UI.Captcha; using SmartStore.Web.Infrastructure.Cache; using SmartStore.Web.Models.Blogs; -using SmartStore.Web.Framework.Mvc; namespace SmartStore.Web.Controllers { @@ -48,6 +47,7 @@ public partial class BlogController : PublicControllerBase private readonly ICacheManager _cacheManager; private readonly ICustomerActivityService _customerActivityService; private readonly IStoreMappingService _storeMappingService; + private readonly ILanguageService _languageService; private readonly MediaSettings _mediaSettings; private readonly BlogSettings _blogSettings; @@ -71,6 +71,7 @@ public BlogController(IBlogService blogService, ICacheManager cacheManager, ICustomerActivityService customerActivityService, IStoreMappingService storeMappingService, + ILanguageService languageService, MediaSettings mediaSettings, BlogSettings blogSettings, LocalizationSettings localizationSettings, @@ -89,6 +90,7 @@ public BlogController(IBlogService blogService, this._cacheManager = cacheManager; this._customerActivityService = customerActivityService; this._storeMappingService = storeMappingService; + this._languageService = languageService; this._mediaSettings = mediaSettings; this._blogSettings = blogSettings; @@ -232,7 +234,38 @@ public ActionResult BlogByMonth(BlogPagingFilteringModel command) [Compress] public ActionResult ListRss(int languageId) { - var feed = _blogService.CreateRssFeed(Url, languageId); + DateTime? maxAge = null; + var protocol = _webHelper.IsCurrentConnectionSecured() ? "https" : "http"; + var selfLink = Url.RouteUrl("BlogRSS", new { languageId = languageId }, protocol); + var blogLink = Url.RouteUrl("Blog", null, protocol); + + var title = "{0} - Blog".FormatInvariant(_storeContext.CurrentStore.Name); + + if (_blogSettings.MaxAgeInDays > 0) + maxAge = DateTime.UtcNow.Subtract(new TimeSpan(_blogSettings.MaxAgeInDays, 0, 0, 0)); + + var language = _languageService.GetLanguageById(languageId); + var feed = new SmartSyndicationFeed(new Uri(blogLink), title); + + feed.AddNamespaces(false); + feed.Init(selfLink, language); + + if (!_blogSettings.Enabled) + return new RssActionResult { Feed = feed }; + + var items = new List(); + var blogPosts = _blogService.GetAllBlogPosts(_storeContext.CurrentStore.Id, languageId, null, null, 0, int.MaxValue, false, maxAge); + + foreach (var blogPost in blogPosts) + { + var blogPostUrl = Url.RouteUrl("BlogPost", new { SeName = blogPost.GetSeName(blogPost.LanguageId, ensureTwoPublishedLanguages: false) }, "http"); + + var item = feed.CreateItem(blogPost.Title, blogPost.Body, blogPostUrl, blogPost.CreatedOnUtc); + + items.Add(item); + } + + feed.Items = items; return new RssActionResult { Feed = feed }; } diff --git a/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs b/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs index 441d1a6b9d..10d668eb81 100644 --- a/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/BoardsController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.ServiceModel.Syndication; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.Customers; @@ -15,6 +16,7 @@ using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Seo; +using SmartStore.Utilities; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Mvc; @@ -34,6 +36,7 @@ public partial class BoardsController : PublicControllerBase private readonly ICountryService _countryService; private readonly IWebHelper _webHelper; private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; private readonly ForumSettings _forumSettings; private readonly CustomerSettings _customerSettings; private readonly MediaSettings _mediaSettings; @@ -49,6 +52,7 @@ public BoardsController(IForumService forumService, ICountryService countryService, IWebHelper webHelper, IWorkContext workContext, + IStoreContext storeContext, ForumSettings forumSettings, CustomerSettings customerSettings, MediaSettings mediaSettings, @@ -60,6 +64,7 @@ public BoardsController(IForumService forumService, this._countryService = countryService; this._webHelper = webHelper; this._workContext = workContext; + this._storeContext = storeContext; this._forumSettings = forumSettings; this._customerSettings = customerSettings; this._mediaSettings = mediaSettings; @@ -266,7 +271,38 @@ public ActionResult ActiveDiscussionsRss(int forumId = 0) if (!_forumSettings.ForumsEnabled) return HttpNotFound(); - var feed = _forumService.CreateActiveDiscussionsRssFeed(Url, forumId); + var language = _workContext.WorkingLanguage; + var protocol = _webHelper.IsCurrentConnectionSecured() ? "https" : "http"; + var selfLink = Url.Action("ActiveDiscussionsRSS", "Boards", null, protocol); + var discussionLink = Url.Action("ActiveDiscussions", "Boards", null, protocol); + + var title = "{0} - {1}".FormatInvariant(_storeContext.CurrentStore.Name, T("Forum.ActiveDiscussionsFeedTitle")); + + var feed = new SmartSyndicationFeed(new Uri(discussionLink), title, T("Forum.ActiveDiscussionsFeedDescription")); + + feed.AddNamespaces(false); + feed.Init(selfLink, language); + + if (!_forumSettings.ActiveDiscussionsFeedEnabled) + return new RssActionResult { Feed = feed }; + + var items = new List(); + var topics = _forumService.GetActiveTopics(forumId, _forumSettings.ActiveDiscussionsFeedCount); + + var viewsText = T("Forum.Views"); + var repliesText = T("Forum.Replies"); + + foreach (var topic in topics) + { + string topicUrl = Url.RouteUrl("TopicSlug", new { id = topic.Id, slug = topic.GetSeName() }, "http"); + var synopsis = "{0}: {1}, {2}: {3}".FormatInvariant(repliesText, topic.NumReplies, viewsText, topic.Views); + + var item = feed.CreateItem(topic.Subject, synopsis, topicUrl, topic.LastPostTime ?? topic.UpdatedOnUtc); + + items.Add(item); + } + + feed.Items = items; return new RssActionResult { Feed = feed }; } @@ -349,7 +385,43 @@ public ActionResult ForumRss(int id) if (!_forumSettings.ForumsEnabled) return HttpNotFound(); - var feed = _forumService.CreateForumRssFeed(Url, id); + var language = _workContext.WorkingLanguage; + var protocol = _webHelper.IsCurrentConnectionSecured() ? "https" : "http"; + var selfLink = Url.Action("ForumRSS", "Boards", null, protocol); + var forumLink = Url.Action("Forum", "Boards", new { id = id }, protocol); + + var feed = new SmartSyndicationFeed(new Uri(forumLink), _storeContext.CurrentStore.Name, T("Forum.ForumFeedDescription")); + + feed.AddNamespaces(false); + feed.Init(selfLink, language); + + if (!_forumSettings.ForumFeedsEnabled) + return new RssActionResult { Feed = feed }; + + var forum = _forumService.GetForumById(id); + + if (forum == null) + return new RssActionResult { Feed = feed }; + + feed.Title = new TextSyndicationContent("{0} - {1}".FormatInvariant(_storeContext.CurrentStore.Name, forum.GetLocalized(x => x.Name, language.Id))); + + var items = new List(); + var topics = _forumService.GetAllTopics(id, 0, string.Empty, ForumSearchType.All, 0, 0, _forumSettings.ForumFeedCount); + + var viewsText = T("Forum.Views"); + var repliesText = T("Forum.Replies"); + + foreach (var topic in topics) + { + string topicUrl = Url.RouteUrl("TopicSlug", new { id = topic.Id, slug = topic.GetSeName() }, "http"); + var synopsis = "{0}: {1}, {2}: {3}".FormatInvariant(repliesText, topic.NumReplies, viewsText, topic.Views); + + var item = feed.CreateItem(topic.Subject, synopsis, topicUrl, topic.LastPostTime ?? topic.UpdatedOnUtc); + + items.Add(item); + } + + feed.Items = items; return new RssActionResult { Feed = feed }; } diff --git a/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs b/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs index 7c18223245..f5957907fa 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CatalogController.cs @@ -21,6 +21,7 @@ using SmartStore.Services.Security; using SmartStore.Services.Seo; using SmartStore.Services.Stores; +using SmartStore.Utilities; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Mvc; using SmartStore.Web.Framework.Security; @@ -847,7 +848,62 @@ public ActionResult RecentlyAddedProducts(CatalogPagingFilteringModel command) [Compress] public ActionResult RecentlyAddedProductsRss() { - var feed = _productService.CreateRecentlyAddedProductsRssFeed(Url); + var protocol = _services.WebHelper.IsCurrentConnectionSecured() ? "https" : "http"; + var selfLink = Url.RouteUrl("RecentlyAddedProductsRSS", null, protocol); + var recentProductsLink = Url.RouteUrl("RecentlyAddedProducts", null, protocol); + + var title = "{0} - {1}".FormatInvariant(_services.StoreContext.CurrentStore.Name, T("RSS.RecentlyAddedProducts")); + + var feed = new SmartSyndicationFeed(new Uri(recentProductsLink), title, T("RSS.InformationAboutProducts")); + + feed.AddNamespaces(true); + feed.Init(selfLink, _services.WorkContext.WorkingLanguage); + + if (!_catalogSettings.RecentlyAddedProductsEnabled) + return new RssActionResult { Feed = feed }; + + var items = new List(); + var searchContext = new ProductSearchContext + { + LanguageId = _services.WorkContext.WorkingLanguage.Id, + OrderBy = ProductSortingEnum.CreatedOn, + PageSize = _catalogSettings.RecentlyAddedProductsNumber, + StoreId = _services.StoreContext.CurrentStoreIdIfMultiStoreMode, + VisibleIndividuallyOnly = true + }; + + var products = _productService.SearchProducts(searchContext); + var storeUrl = _services.StoreContext.CurrentStore.Url; + + foreach (var product in products) + { + string productUrl = Url.RouteUrl("Product", new { SeName = product.GetSeName() }, "http"); + if (productUrl.HasValue()) + { + var item = feed.CreateItem( + product.GetLocalized(x => x.Name), + product.GetLocalized(x => x.ShortDescription), + productUrl, + product.CreatedOnUtc, + product.FullDescription); + + try + { + // we add only the first picture + var picture = product.ProductPictures.OrderBy(x => x.DisplayOrder).Select(x => x.Picture).FirstOrDefault(); + + if (picture != null) + { + feed.AddEnclosue(item, picture, _pictureService.GetPictureUrl(picture, _mediaSettings.ProductDetailsPictureSize, false, storeUrl)); + } + } + catch { } + + items.Add(item); + } + } + + feed.Items = items; return new RssActionResult { Feed = feed }; } diff --git a/src/Presentation/SmartStore.Web/Controllers/NewsController.cs b/src/Presentation/SmartStore.Web/Controllers/NewsController.cs index 2234247d80..dacb180485 100644 --- a/src/Presentation/SmartStore.Web/Controllers/NewsController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/NewsController.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.ServiceModel.Syndication; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Caching; @@ -17,6 +19,7 @@ using SmartStore.Services.News; using SmartStore.Services.Seo; using SmartStore.Services.Stores; +using SmartStore.Utilities; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Mvc; using SmartStore.Web.Framework.Security; @@ -43,6 +46,7 @@ public partial class NewsController : PublicControllerBase private readonly ICacheManager _cacheManager; private readonly ICustomerActivityService _customerActivityService; private readonly IStoreMappingService _storeMappingService; + private readonly ILanguageService _languageService; private readonly MediaSettings _mediaSettings; private readonly NewsSettings _newsSettings; @@ -61,6 +65,7 @@ public NewsController(INewsService newsService, IWorkflowMessageService workflowMessageService, IWebHelper webHelper, ICacheManager cacheManager, ICustomerActivityService customerActivityService, IStoreMappingService storeMappingService, + ILanguageService languageService, MediaSettings mediaSettings, NewsSettings newsSettings, LocalizationSettings localizationSettings, CustomerSettings customerSettings, CaptchaSettings captchaSettings) @@ -77,6 +82,7 @@ public NewsController(INewsService newsService, this._cacheManager = cacheManager; this._customerActivityService = customerActivityService; this._storeMappingService = storeMappingService; + this._languageService = languageService; this._mediaSettings = mediaSettings; this._newsSettings = newsSettings; @@ -208,7 +214,38 @@ public ActionResult List(NewsPagingFilteringModel command) [ActionName("rss"), Compress] public ActionResult ListRss(int languageId) { - var feed = _newsService.CreateRssFeed(Url, languageId); + DateTime? maxAge = null; + var protocol = _webHelper.IsCurrentConnectionSecured() ? "https" : "http"; + var selfLink = Url.Action("rss", "News", new { languageId = languageId }, protocol); + var newsLink = Url.RouteUrl("NewsArchive", null, protocol); + + var title = "{0} - News".FormatInvariant(_storeContext.CurrentStore.Name); + + if (_newsSettings.MaxAgeInDays > 0) + maxAge = DateTime.UtcNow.Subtract(new TimeSpan(_newsSettings.MaxAgeInDays, 0, 0, 0)); + + var language = _languageService.GetLanguageById(languageId); + var feed = new SmartSyndicationFeed(new Uri(newsLink), title); + + feed.AddNamespaces(true); + feed.Init(selfLink, language); + + if (!_newsSettings.Enabled) + return new RssActionResult { Feed = feed }; + + var items = new List(); + var newsItems = _newsService.GetAllNews(languageId, _storeContext.CurrentStore.Id, 0, int.MaxValue, false, maxAge); + + foreach (var news in newsItems) + { + var newsUrl = Url.RouteUrl("NewsItem", new { SeName = news.GetSeName(news.LanguageId, ensureTwoPublishedLanguages: false) }, "http"); + + var item = feed.CreateItem(news.Title, news.Short, newsUrl, news.CreatedOnUtc, news.Full); + + items.Add(item); + } + + feed.Items = items; return new RssActionResult { Feed = feed }; } From f7382dea1c622148fbc1e3ef1adbe13bd70eb75d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sat, 7 Nov 2015 12:22:39 +0100 Subject: [PATCH 040/732] Added class ExportProviderBase --- .../DataExchange/ExportProviderBase.cs | 47 +++++++++++++++++++ .../DataExchange/IExportProvider.cs | 2 +- .../Providers/CategoryXmlExportProvider.cs | 18 ++----- .../Providers/CustomerXlsxExportProvider.cs | 18 ++----- .../Providers/CustomerXmlExportProvider.cs | 18 ++----- .../ManufacturerXmlExportProvider.cs | 18 ++----- .../Providers/OrderXlsxExportProvider.cs | 18 ++----- .../Providers/OrderXmlExportProvider.cs | 18 ++----- .../Providers/ProductXlsxExportProvider.cs | 18 ++----- .../Providers/ProductXmlExportProvider.cs | 18 ++----- .../Providers/SubscriberCsvExportProvider.cs | 18 ++----- .../SmartStore.Services.csproj | 1 + .../Providers/GmcXmlExportProvider.cs | 18 ++----- 13 files changed, 89 insertions(+), 141 deletions(-) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/ExportProviderBase.cs diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProviderBase.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProviderBase.cs new file mode 100644 index 0000000000..f0dd39a95e --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProviderBase.cs @@ -0,0 +1,47 @@ +using SmartStore.Core.Domain.DataExchange; + +namespace SmartStore.Services.DataExchange +{ + public abstract class ExportProviderBase : IExportProvider + { + /// + /// The exported entity type + /// + public virtual ExportEntityType EntityType + { + get { return ExportEntityType.Product; } + } + + /// + /// File extension of the export files (without dot). Return null for a non file based, on-the-fly export. + /// + public virtual string FileExtension + { + get { return null; } + } + + /// + /// Get provider specific configuration information. Return null when no provider specific configuration is required. + /// + public virtual ExportConfigurationInfo ConfigurationInfo + { + get { return null; } + } + + /// + /// Export data to a file + /// + /// Export execution context + public virtual void Execute(IExportExecuteContext context) + { + } + + /// + /// Called once per store when export execution ended + /// + /// Export execution context + public virtual void OnExecuted(IExportExecuteContext context) + { + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs index b0872ef1eb..450c30ed77 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IExportProvider.cs @@ -6,7 +6,7 @@ namespace SmartStore.Services.DataExchange public partial interface IExportProvider : IProvider, IUserEditable { /// - /// Th exported entity type + /// The exported entity type /// ExportEntityType EntityType { get; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs index 5b72bc9d64..e473a28ad2 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs @@ -16,29 +16,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreCategoryXml")] [FriendlyName("SmartStore XML category export")] [IsHidden(true)] - public class CategoryXmlExportProvider : IExportProvider + public class CategoryXmlExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreCategoryXml"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Category; } } - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -104,10 +99,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs index 22a9d229ec..4ab09bd5c5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs @@ -18,7 +18,7 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreCustomerXlsx")] [FriendlyName("SmartStore Excel customer export")] [IsHidden(true)] - public class CustomerXlsxExportProvider : IExportProvider + public class CustomerXlsxExportProvider : ExportProviderBase { private string[] Properties { @@ -97,22 +97,17 @@ public static string SystemName get { return "Exports.SmartStoreCustomerXlsx"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Customer; } } - public string FileExtension + public override string FileExtension { get { return "XLSX"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var invariantCulture = CultureInfo.InvariantCulture; var path = context.FilePath; @@ -214,10 +209,5 @@ public void Execute(IExportExecuteContext context) xlPackage.Save(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs index b88d3d8cd3..93d9c4462b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs @@ -16,29 +16,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreCustomerXml")] [FriendlyName("SmartStore XML customer export")] [IsHidden(true)] - public class CustomerXmlExportProvider : IExportProvider + public class CustomerXmlExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreCustomerXml"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Customer; } } - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -87,10 +82,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs index edc60a007d..8d26a9ab4e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs @@ -16,29 +16,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreManufacturerXml")] [FriendlyName("SmartStore XML manufacturer export")] [IsHidden(true)] - public class ManufacturerXmlExportProvider : IExportProvider + public class ManufacturerXmlExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreManufacturerXml"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Manufacturer; } } - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -104,10 +99,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs index 9f468e071f..ae6ce4271d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs @@ -15,7 +15,7 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreOrderXlsx")] [FriendlyName("SmartStore Excel order export")] [IsHidden(true)] - public class OrderXlsxExportProvider : IExportProvider + public class OrderXlsxExportProvider : ExportProviderBase { private string[] Properties { @@ -96,22 +96,17 @@ public static string SystemName get { return "Exports.SmartStoreOrderXlsx"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Order; } } - public string FileExtension + public override string FileExtension { get { return "XLSX"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var path = context.FilePath; @@ -250,10 +245,5 @@ public void Execute(IExportExecuteContext context) xlPackage.Save(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs index a21a94d2f9..15d347847f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs @@ -16,29 +16,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreOrderXml")] [FriendlyName("SmartStore XML order export")] [IsHidden(true)] - public class OrderXmlExportProvider : IExportProvider + public class OrderXmlExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreOrderXml"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Order; } } - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -260,10 +255,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs index a433bf5c8d..3da13558c0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs @@ -22,7 +22,7 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreProductXlsx")] [FriendlyName("SmartStore Excel product export")] [IsHidden(true)] - public class ProductXlsxExportProvider : IExportProvider + public class ProductXlsxExportProvider : ExportProviderBase { private readonly ILanguageService _languageService; private readonly IProductService _productService; @@ -165,22 +165,17 @@ public static string SystemName get { return "Exports.SmartStoreProductXlsx"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Product; } } - public string FileExtension + public override string FileExtension { get { return "XLSX"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var path = context.FilePath; @@ -414,10 +409,5 @@ public void Execute(IExportExecuteContext context) // EPPLus had serious memory leak problems in V3. We enforce the garbage collector to release unused memory, it's not perfect, but better than nothing. GC.Collect(); } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs index a0cc291bf1..e4154f4804 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs @@ -16,29 +16,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreProductXml")] [FriendlyName("SmartStore XML product export")] [IsHidden(true)] - public class ProductXmlExportProvider : IExportProvider + public class ProductXmlExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreProductXml"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.Product; } } - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -87,10 +82,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs index 2ca56f48f8..2a7e299b51 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs @@ -13,29 +13,24 @@ namespace SmartStore.Services.DataExchange.Providers [SystemName("Exports.SmartStoreNewsSubscriptionCsv")] [FriendlyName("SmartStore CSV newsletter subscription export")] [IsHidden(true)] - public class SubscriberCsvExportProvider : IExportProvider + public class SubscriberCsvExportProvider : ExportProviderBase { public static string SystemName { get { return "Exports.SmartStoreNewsSubscriptionCsv"; } } - public ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } - - public ExportEntityType EntityType + public override ExportEntityType EntityType { get { return ExportEntityType.NewsLetterSubscription; } } - public string FileExtension + public override string FileExtension { get { return "CSV"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var path = context.FilePath; @@ -73,10 +68,5 @@ public void Execute(IExportExecuteContext context) } } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index f63d565765..7b4dc44e1e 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -171,6 +171,7 @@ + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index 3f1dac0bd2..b1a68d4591 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -28,7 +28,7 @@ namespace SmartStore.GoogleMerchantCenter.Providers ExportFeatures.OffersBrandFallback, ExportFeatures.CanIncludeMainPicture, ExportFeatures.UsesSpecialPrice)] - public class GmcXmlExportProvider : IExportProvider + public class GmcXmlExportProvider : ExportProviderBase { private const string _googleNamespace = "http://base.google.com/ns/1.0"; @@ -132,7 +132,7 @@ public static string Unspecified get { return "__nospec__"; } } - public ExportConfigurationInfo ConfigurationInfo + public override ExportConfigurationInfo ConfigurationInfo { get { @@ -150,17 +150,12 @@ public ExportConfigurationInfo ConfigurationInfo } } - public ExportEntityType EntityType - { - get { return ExportEntityType.Product; } - } - - public string FileExtension + public override string FileExtension { get { return "XML"; } } - public void Execute(IExportExecuteContext context) + public override void Execute(IExportExecuteContext context) { var settings = new XmlWriterSettings { @@ -400,10 +395,5 @@ public void Execute(IExportExecuteContext context) writer.WriteEndDocument(); } } - - public void OnExecuted(IExportExecuteContext context) - { - // nothing to do - } } } \ No newline at end of file From 2289b5c30629e9b4363644042cdfbe3780f09b2b Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sun, 8 Nov 2015 03:19:21 +0100 Subject: [PATCH 041/732] (Perf) Faster reflection with new FastProperty helper class (makes Fasterflect obsolete soon) --- .../ComponentModel/HybridExpando.cs | 72 +-- .../SmartStore.Core/Plugins/PluginManager.cs | 1 - .../SmartStore.Core/SmartStore.Core.csproj | 1 + .../Utilities/Reflection/FastProperty.cs | 553 ++++++++++++++++++ .../Configuration/SettingService.cs | 11 +- .../DependencyRegistrar.cs | 38 +- 6 files changed, 609 insertions(+), 67 deletions(-) create mode 100644 src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs diff --git a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs index 6697e19d0c..eebf907175 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs @@ -37,6 +37,7 @@ using System.Dynamic; using System.Reflection; using System.Collections; +using SmartStore.Utilities.Reflection; namespace SmartStore.ComponentModel { @@ -71,8 +72,6 @@ public class HybridExpando : DynamicObject, IDictionary /// private Type _instanceType; - private IList _instancePropertyInfos; - /// /// String Dictionary that contains the extra dynamic values /// stored on this object/instance @@ -116,16 +115,6 @@ protected object WrappedObject get { return _instance; } } - IList InstancePropertyInfos - { - get - { - if (_instancePropertyInfos == null && _instance != null) - _instancePropertyInfos = Fasterflect.PropertyExtensions.Properties(_instanceType, Fasterflect.Flags.InstancePublic); - return _instancePropertyInfos; - } - } - public override IEnumerable GetDynamicMemberNames() { foreach (var prop in this.GetProperties(false)) @@ -238,25 +227,15 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o /// protected bool GetProperty(object instance, string name, out object result) { - var pi = GetPropertyInfo(name); - if (pi != null) - { - result = Fasterflect.PropertyExtensions.GetPropertyValue(instance ?? this, pi.Name); - return true; - } + var fastProp = _instanceType != null ? FastProperty.GetProperty(_instanceType, name, true) : null; + if (fastProp != null) + { + result = fastProp.GetValue(instance ?? this); + return true; + } - result = null; + result = null; return false; - } - - /// - /// Gets a PropertyInfo instance from the wrapped object - /// - /// Property name - protected PropertyInfo GetPropertyInfo(string name) - { - var pi = Fasterflect.PropertyExtensions.Property(_instanceType, name, Fasterflect.Flags.InstancePublic); - return pi; } /// @@ -268,13 +247,14 @@ protected PropertyInfo GetPropertyInfo(string name) /// protected bool SetProperty(object instance, string name, object value) { - var pi = GetPropertyInfo(name); - if (pi != null) - { - Fasterflect.PropertyInfoExtensions.Set(pi, instance ?? this, value); - return true; + var fastProp = _instanceType != null ? FastProperty.GetProperty(_instanceType, name, true) : null; + if (fastProp != null) + { + fastProp.SetValue(instance ?? this, value); + return true; } - return false; + + return false; } /// @@ -288,10 +268,10 @@ protected bool SetProperty(object instance, string name, object value) protected bool InvokeMethod(object instance, string name, object[] args, out object result) { // Look at the instanceType - var mi = Fasterflect.MethodExtensions.Method(_instanceType, name, Fasterflect.Flags.InstancePublic); + var mi = _instanceType != null ? _instanceType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public) : null; if (mi != null) { - result = Fasterflect.MethodInfoExtensions.Call(mi, instance ?? this, args); + result = mi.Invoke(instance ?? this, args); return true; } @@ -352,11 +332,11 @@ public IEnumerable> GetProperties(bool includeInsta if (includeInstanceProperties && _instance != null) { - foreach (var prop in this.InstancePropertyInfos) + foreach (var prop in FastProperty.GetProperties(_instance).Values) { if (!this.Properties.ContainsKey(prop.Name)) { - yield return new KeyValuePair(prop.Name, prop.GetValue(_instance, null)); + yield return new KeyValuePair(prop.Name, prop.GetValue(_instance)); } } } @@ -390,11 +370,7 @@ public bool Contains(string propertyName, bool includeInstanceProperties = false if (includeInstanceProperties && _instance != null) { - foreach (var prop in this.InstancePropertyInfos) - { - if (prop.Name == propertyName) - return true; - } + return FastProperty.GetProperties(_instance).ContainsKey(propertyName); } return false; @@ -422,7 +398,13 @@ int ICollection>.Count { get { - return Properties.Count + InstancePropertyInfos.Count; + var count = Properties.Count; + if (_instanceType != null) + { + count += FastProperty.GetProperties(_instanceType).Count; + } + + return count; } } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs index dfa85d8ef7..fa6e5e7aa5 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs @@ -178,7 +178,6 @@ public static void Initialize() } IncompatiblePlugins = incompatiblePlugins.AsReadOnly(); - } } diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index b23a0d7b0f..7d39d16e05 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -578,6 +578,7 @@ + diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs new file mode 100644 index 0000000000..27498b5576 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs @@ -0,0 +1,553 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace SmartStore.Utilities.Reflection +{ + + public class FastProperty + { + // Delegate type for a by-ref property getter + private delegate TValue ByRefFunc(ref TDeclaringType arg); + + private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter)); + private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference)); + private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter)); + private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference)); + private static readonly MethodInfo CallPropertySetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter)); + + private static readonly ConcurrentDictionary SinglePropertiesCache = new ConcurrentDictionary(); + + // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. + private static readonly ConcurrentDictionary> PropertiesCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> VisiblePropertiesCache = new ConcurrentDictionary>(); + + private Action _valueSetter; + + /// + /// Initializes a fast . + /// This constructor does not cache the helper. For caching, use . + /// + public FastProperty(PropertyInfo property) + { + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + Property = property; + Name = property.Name; + ValueGetter = MakeFastPropertyGetter(property); + } + + /// + /// Gets the backing . + /// + public PropertyInfo Property { get; } + + /// + /// Gets (or sets in derived types) the property name. + /// + public virtual string Name { get; protected set; } + + /// + /// Gets the property value getter. + /// + public Func ValueGetter { get; } + + /// + /// Gets the property value setter. + /// + public Action ValueSetter + { + get + { + if (_valueSetter == null) + { + // We'll allow safe races here. + _valueSetter = MakeFastPropertySetter(Property); + } + + return _valueSetter; + } + } + + /// + /// Returns the property value for the specified . + /// + /// The object whose property value will be returned. + /// The property value. + public object GetValue(object instance) + { + return ValueGetter(instance); + } + + /// + /// Sets the property value for the specified . + /// + /// The object whose property value will be set. + /// The property value. + public void SetValue(object instance, object value) + { + ValueSetter(instance, value); + } + + /// + /// Creates and caches fast property helpers that expose getters for every public get property on the + /// underlying type. + /// + /// the instance to extract property accessors for. + /// a cached array of all public property getters from the underlying type of target instance. + /// + public static IReadOnlyDictionary GetProperties(object instance) + { + return GetProperties(instance.GetType()); + } + + /// + /// Creates and caches fast property helpers that expose getters for every public get property on the + /// specified type. + /// + /// the type to extract property accessors for. + /// a cached array of all public property getters from the type of target instance. + /// + public static IReadOnlyDictionary GetProperties(Type type) + { + return (IReadOnlyDictionary)GetProperties(type, CreateInstance, PropertiesCache); + } + + /// + /// + /// Creates and caches fast property helpers that expose getters for every non-hidden get property + /// on the specified type. + /// + /// + /// excludes properties defined on base types that have been + /// hidden by definitions using the new keyword. + /// + /// + /// The instance to extract property accessors for. + /// + /// A cached array of all public property getters from the instance's type. + /// + public static IReadOnlyDictionary GetVisibleProperties(object instance) + { + return (IReadOnlyDictionary)GetVisibleProperties(instance.GetType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); + } + + /// + /// + /// Creates and caches fast property helpers that expose getters for every non-hidden get property + /// on the specified type. + /// + /// + /// excludes properties defined on base types that have been + /// hidden by definitions using the new keyword. + /// + /// + /// The type to extract property accessors for. + /// + /// A cached array of all public property getters from the type. + /// + public static IReadOnlyDictionary GetVisibleProperties(Type type) + { + return (IReadOnlyDictionary)GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); + } + + public static FastProperty GetProperty(Type type, string propertyName, bool cacheAllProperties = false) + { + FastProperty fastProperty = null; + IDictionary allProperties; + + if (cacheAllProperties) + { + allProperties = (IDictionary)GetProperties(type); + allProperties.TryGetValue(propertyName, out fastProperty); + return fastProperty; + } + + if (PropertiesCache.TryGetValue(type, out allProperties)) + { + allProperties.TryGetValue(propertyName, out fastProperty); + return fastProperty; + } + + var key = new PropertyKey(type, propertyName); + if (!SinglePropertiesCache.TryGetValue(key, out fastProperty)) + { + var pi = GetPropertyCandidates(type).Where(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + //var pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (pi != null) + { + fastProperty = CreateInstance(pi); + SinglePropertiesCache.TryAdd(key, fastProperty); + } + } + + return fastProperty; + } + + /// + /// Creates a single fast property getter. The result is not cached. + /// + /// propertyInfo to extract the getter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. + /// + public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallPropertyGetterOpenGenericMethod, + CallPropertyGetterByReferenceOpenGenericMethod); + } + + /// + /// Creates a single fast property getter which is safe for a null input object. The result is not cached. + /// + /// propertyInfo to extract the getter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. + /// + public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallNullSafePropertyGetterOpenGenericMethod, + CallNullSafePropertyGetterByReferenceOpenGenericMethod); + } + + private static Func MakeFastPropertyGetter( + PropertyInfo propertyInfo, + MethodInfo propertyGetterWrapperMethod, + MethodInfo propertyGetterByRefWrapperMethod) + { + Debug.Assert(propertyInfo != null); + + // Must be a generic method with a Func<,> parameter + Debug.Assert(propertyGetterWrapperMethod != null); + Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); + + // Must be a generic method with a ByRefFunc<,> parameter + Debug.Assert(propertyGetterByRefWrapperMethod != null); + Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); + + var getMethod = propertyInfo.GetMethod; + Debug.Assert(getMethod != null); + Debug.Assert(!getMethod.IsStatic); + Debug.Assert(getMethod.GetParameters().Length == 0); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + if (getMethod.DeclaringType.GetTypeInfo().IsValueType) + { + // Create a delegate (ref TDeclaringType) -> TValue + return MakeFastPropertyGetter( + typeof(ByRefFunc<,>), + getMethod, + propertyGetterByRefWrapperMethod); + } + else + { + // Create a delegate TDeclaringType -> TValue + return MakeFastPropertyGetter( + typeof(Func<,>), + getMethod, + propertyGetterWrapperMethod); + } + } + + private static Func MakeFastPropertyGetter( + Type openGenericDelegateType, + MethodInfo propertyGetMethod, + MethodInfo openGenericWrapperMethod) + { + var typeInput = propertyGetMethod.DeclaringType; + var typeOutput = propertyGetMethod.ReturnType; + + var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); + var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); + + var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); + var accessorDelegate = wrapperDelegateMethod.CreateDelegate( + typeof(Func), + propertyGetterDelegate); + + return (Func)accessorDelegate; + } + + /// + /// Creates a single fast property setter for reference types. The result is not cached. + /// + /// propertyInfo to extract the setter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. This only works for reference types. + /// + public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); + + var setMethod = propertyInfo.SetMethod; + Debug.Assert(setMethod != null); + Debug.Assert(!setMethod.IsStatic); + Debug.Assert(setMethod.ReturnType == typeof(void)); + var parameters = setMethod.GetParameters(); + Debug.Assert(parameters.Length == 1); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + var typeInput = setMethod.DeclaringType; + var parameterType = parameters[0].ParameterType; + + // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } + var propertySetterAsAction = + setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); + var callPropertySetterClosedGenericMethod = + CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); + var callPropertySetterDelegate = + callPropertySetterClosedGenericMethod.CreateDelegate( + typeof(Action), propertySetterAsAction); + + return (Action)callPropertySetterDelegate; + } + + /// + /// Given an object, adds each instance property with a public get method as a key and its + /// associated value to a dictionary. + /// + /// If the object is already an instance, then a copy + /// is returned. + /// + /// + /// The implementation of PropertyHelper will cache the property accessors per-type. This is + /// faster when the the same type is used multiple times with ObjectToDictionary. + /// + public static IDictionary ObjectToDictionary(object value) + { + var dictionary = value as IDictionary; + if (dictionary != null) + { + return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); + } + + dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (value != null) + { + foreach (var helper in GetProperties(value).Values) + { + dictionary[helper.Name] = helper.GetValue(value); + } + } + + return dictionary; + } + + private static FastProperty CreateInstance(PropertyInfo property) + { + return new FastProperty(property); + } + + // Called via reflection + private static object CallPropertyGetter( + Func getter, + object target) + { + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallPropertyGetterByReference( + ByRefFunc getter, + object target) + { + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + // Called via reflection + private static object CallNullSafePropertyGetter( + Func getter, + object target) + { + if (target == null) + { + return null; + } + + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallNullSafePropertyGetterByReference( + ByRefFunc getter, + object target) + { + if (target == null) + { + return null; + } + + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + private static void CallPropertySetter( + Action setter, + object target, + object value) + { + setter((TDeclaringType)target, (TValue)value); + } + + protected static IDictionary GetVisibleProperties( + Type type, + Func createPropertyHelper, + ConcurrentDictionary> allPropertiesCache, + ConcurrentDictionary> visiblePropertiesCache) + { + IDictionary result; + if (visiblePropertiesCache.TryGetValue(type, out result)) + { + return result; + } + + // The simple and common case, this is normal POCO object - no need to allocate. + var allPropertiesDefinedOnType = true; + var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache); + foreach (var propertyHelper in allProperties.Values) + { + if (propertyHelper.Property.DeclaringType != type) + { + allPropertiesDefinedOnType = false; + break; + } + } + + if (allPropertiesDefinedOnType) + { + result = allProperties; + visiblePropertiesCache.TryAdd(type, result); + return result; + } + + // There's some inherited properties here, so we need to check for hiding via 'new'. + var filteredProperties = new List(allProperties.Count); + foreach (var propertyHelper in allProperties.Values) + { + var declaringType = propertyHelper.Property.DeclaringType; + if (declaringType == type) + { + filteredProperties.Add(propertyHelper); + continue; + } + + // If this property was declared on a base type then look for the definition closest to the + // the type to see if we should include it. + var ignoreProperty = false; + + // Walk up the hierarchy until we find the type that actally declares this + // PropertyInfo. + var currentTypeInfo = type.GetTypeInfo(); + var declaringTypeInfo = declaringType.GetTypeInfo(); + while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) + { + // We've found a 'more proximal' public definition + var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name); + if (declaredProperty != null) + { + ignoreProperty = true; + break; + } + + currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo(); + } + + if (!ignoreProperty) + { + filteredProperties.Add(propertyHelper); + } + } + + result = filteredProperties.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + visiblePropertiesCache.TryAdd(type, result); + return result; + } + + protected static IDictionary GetProperties( + Type type, + Func createPropertyHelper, + ConcurrentDictionary> cache) + { + // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be + // part of the sequence of properties returned by this method. + type = Nullable.GetUnderlyingType(type) ?? type; + + IDictionary fastProperties; + if (!cache.TryGetValue(type, out fastProperties)) + { + var candidates = GetPropertyCandidates(type); + fastProperties = candidates.Select(p => createPropertyHelper(p)).ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + cache.TryAdd(type, fastProperties); + } + + return fastProperties; + } + + private static IEnumerable GetPropertyCandidates(Type type) + { + // We avoid loading indexed properties using the Where statement. + var properties = type.GetRuntimeProperties().Where(IsCandidateProperty); + + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsInterface) + { + // Reflection does not return information about inherited properties on the interface itself. + properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( + interfaceType => interfaceType.GetRuntimeProperties().Where(IsCandidateProperty))); + } + + return properties; + } + + // Indexed properties are not useful (or valid) for grabbing properties off an object. + private static bool IsCandidateProperty(PropertyInfo property) + { + return property.GetIndexParameters().Length == 0 && + property.GetMethod != null && + property.GetMethod.IsPublic && + !property.GetMethod.IsStatic; + } + + class PropertyKey : Tuple + { + public PropertyKey(Type type, string propertyName) + : base(type, propertyName) + { + } + public Type Type { get { return base.Item1; } } + public string PropertyName { get { return base.Item2; } } + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs index 9bdcecbfcb..c439ee40b5 100644 --- a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs +++ b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs @@ -15,6 +15,7 @@ using SmartStore.Core.Plugins; using System.ComponentModel; using SmartStore.Utilities; +using SmartStore.Utilities.Reflection; namespace SmartStore.Services.Configuration { @@ -303,10 +304,12 @@ public virtual bool SettingExists(T settings, var settings = Activator.CreateInstance(); - foreach (var prop in typeof(T).GetProperties()) + foreach (var fastProp in FastProperty.GetProperties(typeof(T)).Values) { + var prop = fastProp.Property; + // get properties we can read and write to - if (!prop.CanRead || !prop.CanWrite) + if (!prop.CanWrite) continue; var key = typeof(T).Name + "." + prop.Name; @@ -330,7 +333,7 @@ public virtual bool SettingExists(T settings, if (list != null) { - prop.SetValue(settings, list, null); + fastProp.SetValue(settings, list); } } @@ -348,7 +351,7 @@ public virtual bool SettingExists(T settings, object value = converter.ConvertFromInvariantString(setting); //set property - prop.SetValue(settings, value, null); + fastProp.SetValue(settings, value); } return settings; diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 1daa71bd03..3820f326d8 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -75,6 +75,7 @@ using SmartStore.Web.Framework.WebApi.Configuration; using Module = Autofac.Module; using SmartStore.Core.Domain.DataExchange; +using SmartStore.Utilities.Reflection; namespace SmartStore.Web.Framework { @@ -327,12 +328,14 @@ protected override void AttachToComponentRegistration(IComponentRegistry compone if (querySettingsProperty == null) return; + var fastProperty = new FastProperty(querySettingsProperty); + registration.Activated += (sender, e) => { if (DataSettings.DatabaseIsInstalled()) { var querySettings = e.Context.Resolve(); - querySettingsProperty.SetValue(e.Instance, querySettings, null); + fastProperty.SetValue(e.Instance, querySettings); } }; } @@ -390,18 +393,17 @@ private IEnumerable> BuildLoggerInjectors(Type }) .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer - .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set + .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set + .Select(x => new FastProperty(x.PropertyInfo)); // Return an array of actions that resolve a logger and assign the property - foreach (var entry in loggerProperties) + foreach (var prop in loggerProperties) { - var propertyInfo = entry.PropertyInfo; - yield return (ctx, instance) => { string component = componentType.ToString(); var logger = ctx.Resolve(); - propertyInfo.SetValue(instance, logger, null); + prop.SetValue(instance, logger); }; } } @@ -433,12 +435,14 @@ protected override void AttachToComponentRegistration(IComponentRegistry compone if (userProperty == null) return; + var fastProperty = new FastProperty(userProperty); + registration.Activated += (sender, e) => { if (DataSettings.DatabaseIsInstalled()) { Localizer localizer = e.Context.Resolve().Get; - userProperty.SetValue(e.Instance, localizer, null); + fastProperty.SetValue(e.Instance, localizer); } }; } @@ -1016,16 +1020,16 @@ public IEnumerable RegistrationsFor( var ts = service as TypedService; if (ts != null && typeof(ISettings).IsAssignableFrom(ts.ServiceType)) { - //var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); - //yield return (IComponentRegistration)buildMethod.Invoke(null, null); - - // Perf with Fasterflect - yield return (IComponentRegistration)Fasterflect.TryInvokeWithValuesExtensions.TryCallMethodWithValues( - typeof(SettingsSource), - null, - "BuildRegistration", - new Type[] { ts.ServiceType }, - BindingFlags.Static | BindingFlags.NonPublic); + var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); + yield return (IComponentRegistration)buildMethod.Invoke(null, null); + + //// Perf with Fasterflect + //yield return (IComponentRegistration)Fasterflect.TryInvokeWithValuesExtensions.TryCallMethodWithValues( + // typeof(SettingsSource), + // null, + // "BuildRegistration", + // new Type[] { ts.ServiceType }, + // BindingFlags.Static | BindingFlags.NonPublic); } } From e910adace963cef2360e75a6afd79f170186de7b Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Sun, 8 Nov 2015 04:43:14 +0100 Subject: [PATCH 042/732] (Perf) Replaced Fasterflect with custom reflection utilities (much faster & less mem usage) --- .../ComponentModel/SerializationUtils.cs | 9 +- .../Extensions/DictionaryExtensions.cs | 3 +- .../Extensions/TypeExtensions.cs | 58 ------------ .../Infrastructure/ComparableObject.cs | 7 +- .../Infrastructure/DateRange.cs | 70 --------------- .../SmartStore.Core/Infrastructure/Error.cs | 2 - .../Infrastructure/ValueObject.cs | 90 ------------------- .../SmartStore.Core/SmartStore.Core.csproj | 7 +- .../Utilities/CollectionHelper.cs | 21 +---- .../Utilities/DictionaryConverter.cs | 48 +++++----- .../Utilities/Reflection/FastProperty.cs | 4 +- .../SmartStore.Core/Utilities/TypeHelper.cs | 11 --- src/Libraries/SmartStore.Core/packages.config | 1 - src/Libraries/SmartStore.Data/app.config | 12 +-- .../Configuration/SettingService.cs | 17 ++-- .../ExportImport/DataSegmenter.cs | 17 +++- .../Localization/LocalizationExtentions.cs | 11 ++- .../SmartStore.Services.csproj | 4 - .../SmartStore.Services/packages.config | 1 - src/Plugins/SmartStore.Clickatell/web.config | 2 +- src/Plugins/SmartStore.DevTools/Web.config | 2 +- .../SmartStore.DiscountRules/web.config | 2 +- .../SmartStore.FacebookAuth/web.config | 2 +- .../SmartStore.GoogleAnalytics/web.config | 2 +- .../web.config | 2 +- .../SmartStore.OfflinePayment/web.config | 2 +- src/Plugins/SmartStore.Shipping/web.config | 2 +- .../SmartStore.ShippingByWeight/web.config | 2 +- src/Plugins/SmartStore.Tax/web.config | 2 +- src/Plugins/SmartStore.WebApi/web.config | 2 +- .../Settings/StoreDependingSettingHelper.cs | 10 +-- .../SmartStore.Web.Framework.csproj | 4 - .../UI/Components/ComponentBuilder.cs | 2 +- .../WebApi/WebApiEntityController.cs | 19 ++-- .../SmartStore.Web.Framework/packages.config | 1 - .../SmartStore.Web/Administration/Web.config | 2 +- src/Tools/SmartStore.Packager/App.config | 18 ++-- 37 files changed, 113 insertions(+), 358 deletions(-) delete mode 100644 src/Libraries/SmartStore.Core/Infrastructure/DateRange.cs delete mode 100644 src/Libraries/SmartStore.Core/Infrastructure/ValueObject.cs diff --git a/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs b/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs index 263e516ef5..26d6f8b079 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs @@ -35,7 +35,6 @@ using System.IO; using System.Text; //using System.Reflection; -using Fasterflect; using System.Xml; using System.Xml.Serialization; @@ -419,17 +418,17 @@ public static object DeSerializeObject(byte[] buffer, Type objectType, bool thro /// public static string ObjectToString(object instance, string separator, ObjectToStringTypes type) { - var fi = instance.GetType().Fields(); + var fi = instance.GetType().GetFields(); string output = string.Empty; if (type == ObjectToStringTypes.Properties || type == ObjectToStringTypes.PropertiesAndFields) { - foreach (var property in instance.GetType().Properties()) + foreach (var property in instance.GetType().GetProperties()) { try { - output += property.Name + ":" + instance.GetPropertyValue(property.Name).ToString() + separator; + output += property.Name + ":" + property.GetValue(instance, null).ToString() + separator; } catch { @@ -444,7 +443,7 @@ public static string ObjectToString(object instance, string separator, ObjectToS { try { - output = output + field.Name + ": " + instance.GetFieldValue(field.Name).ToString() + separator; + output = output + field.Name + ": " + field.GetValue(instance).ToString() + separator; } catch { diff --git a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs index 8cfb8c1142..d0dde45343 100644 --- a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs @@ -5,6 +5,7 @@ using System.Web.Routing; using System.Globalization; using System.Dynamic; +using SmartStore.Utilities; namespace SmartStore { @@ -34,7 +35,7 @@ public static void Merge(this IDictionary instance, string key, public static void Merge(this IDictionary instance, object values, bool replaceExisting = true) { - instance.Merge(new RouteValueDictionary(values), replaceExisting); + instance.Merge(CollectionHelper.ObjectToDictionary(values), replaceExisting); } public static void Merge(this IDictionary instance, IDictionary from, bool replaceExisting = true) diff --git a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs index d2ca8d9df9..007022ae53 100644 --- a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Collections; using System.Diagnostics; -using Fasterflect; namespace SmartStore { @@ -393,63 +392,6 @@ public static bool IsGenericDictionary(this Type type) // return false; //} - /// - /// Gets the member's value on the object. - /// - /// The member. - /// The target object. - /// The member's value on the object. - public static object GetValue(this MemberInfo member, object target) - { - Guard.ArgumentNotNull(member, "member"); - Guard.ArgumentNotNull(target, "target"); - - var type = target.GetType(); - - switch (member.MemberType) - { - case MemberTypes.Field: - return target.GetFieldValue(member.Name); - //return ((FieldInfo)member).GetValue(target); - case MemberTypes.Property: - return target.GetPropertyValue(member.Name); - default: - throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatInvariant(member.Name), "member"); - } - } - - /// - /// Sets the member's value on the target object. - /// - /// The member. - /// The target. - /// The value. - public static void SetValue(this MemberInfo member, object target, object value) - { - Guard.ArgumentNotNull(member, "member"); - Guard.ArgumentNotNull(target, "target"); - - switch (member.MemberType) - { - case MemberTypes.Field: - target.SetFieldValue(member.Name, value); - break; - //return ((FieldInfo)member).GetValue(target); - case MemberTypes.Property: - try - { - target.SetPropertyValue(member.Name, value); - } - catch (TargetParameterCountException e) - { - throw new ArgumentException("PropertyInfo '{0}' has index parameters".FormatInvariant(member.Name), "member", e); - } - break; - default: - throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatInvariant(member.Name), "member"); - } - } - /// /// Gets the underlying type of a type. /// diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs index 7a0c3d9dbb..32b4a6a1e2 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs @@ -4,7 +4,6 @@ using System.Linq.Expressions; using System.Reflection; using System.ComponentModel; -using Fasterflect; namespace SmartStore { @@ -67,7 +66,7 @@ public override int GetHashCode() foreach (var pi in signatureProperties) { - object value = this.GetPropertyValue(pi.Name); // pi.GetValue(this); + object value = pi.GetValue(this); if (value != null) hashCode = (hashCode * HashMultiplier) ^ value.GetHashCode(); @@ -102,8 +101,8 @@ protected virtual bool HasSameSignatureAs(ComparableObject compareTo) foreach (var pi in signatureProperties) { - object thisValue = this.GetPropertyValue(pi.Name); - object thatValue = compareTo.GetPropertyValue(pi.Name); + object thisValue = pi.GetValue(this); + object thatValue = pi.GetValue(compareTo); if (thisValue == null && thatValue == null) continue; diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DateRange.cs b/src/Libraries/SmartStore.Core/Infrastructure/DateRange.cs deleted file mode 100644 index bb331223be..0000000000 --- a/src/Libraries/SmartStore.Core/Infrastructure/DateRange.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SmartStore -{ - - public class DateRange : ValueObject, ICloneable - { - - public DateRange() - { - } - - public DateRange(DateTime to) - : this(default(DateTime), to) - { - } - - public DateRange(DateTime from, DateTime to) - { - DateFrom = from; - DateTo = to; - } - - [ObjectSignature] - public DateTime? DateFrom { get; set; } - - [ObjectSignature] - public DateTime? DateTo { get; set; } - - public bool IsDefined - { - get { return DateFrom.HasValue || DateTo.HasValue; } - } - - //public override int GetHashCode() - //{ - // return SystemUtil.GetHashCode(this.DateFrom, this.DateTo); - //} - - //public override bool Equals(object obj) - //{ - // if (ReferenceEquals(null, obj)) return false; - // if (ReferenceEquals(this, obj)) return true; - - // DateRange other = (DateRange)obj; - // return this.DateFrom.Equals(other.DateFrom) && this.DateTo.Equals(other.DateTo); - //} - - #region ICloneable Members - - public DateRange Clone() - { - return new DateRange() - { - DateFrom = this.DateFrom, - DateTo = this.DateTo - }; - } - - object ICloneable.Clone() - { - return this.Clone(); - } - - #endregion - - } - -} diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Error.cs b/src/Libraries/SmartStore.Core/Infrastructure/Error.cs index d8752d8169..a83cc11464 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Error.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Error.cs @@ -2,8 +2,6 @@ using System.Globalization; using System.Diagnostics; -using Fasterflect; - namespace SmartStore { public static class Error diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ValueObject.cs b/src/Libraries/SmartStore.Core/Infrastructure/ValueObject.cs deleted file mode 100644 index 066c47f810..0000000000 --- a/src/Libraries/SmartStore.Core/Infrastructure/ValueObject.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Reflection; -using Fasterflect; - -namespace SmartStore -{ - - /// - /// Base class for complex value objects, which do not have - /// identifiers. - /// - /// Type of the entity, which is the value obect. - public abstract class ValueObject : ComparableObject> - where T : ValueObject - { - - /// - /// Registers all properties of the subclass as signature properties. - /// - protected void RegisterProperties() - { - RegisterProperties((pd) => true); - } - - /// - /// Registers any properties of the subclass matching - /// the given as signature properties. - /// - protected void RegisterProperties(Func filter) - { - foreach (var pi in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) - { - if (filter(pi)) - RegisterSignatureProperty(pi); - } - } - - public override bool Equals(object obj) - { - return base.Equals(obj); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - public override string ToString() - { - var sb = new StringBuilder(); - var properties = GetSignatureProperties(); - int propsCount = properties.Count(); - - int i = 1; - - foreach (var pi in properties) - { - object value = this.GetPropertyValue(pi.Name); - if (value == null) - continue; - - sb.Append(pi.Name + ": " + value.ToString()); - if (i < propsCount) - sb.Append(", "); - - i++; - } - - return sb.ToString(); - } - - public static bool operator ==(ValueObject x, ValueObject y) - { - if ((object)x == null) - return (object)y == null; - - return x.Equals(y); - } - - public static bool operator !=(ValueObject x, ValueObject y) - { - return !(x == y); - } - - } - -} diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 7d39d16e05..8dc531e3be 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -77,10 +77,6 @@ False ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - - False - ..\..\packages\fasterflect.2.1.3\lib\net40\Fasterflect.dll - True ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -441,7 +437,6 @@ - @@ -451,7 +446,6 @@ - @@ -599,6 +593,7 @@ + diff --git a/src/Libraries/SmartStore.Core/Utilities/CollectionHelper.cs b/src/Libraries/SmartStore.Core/Utilities/CollectionHelper.cs index 2497ef9349..f5dcfae895 100644 --- a/src/Libraries/SmartStore.Core/Utilities/CollectionHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/CollectionHelper.cs @@ -6,10 +6,7 @@ using System.Text; using System.Collections; using System.Globalization; -using System.Collections.Specialized; -using System.ComponentModel; - -using Fasterflect; +using SmartStore.Utilities.Reflection; namespace SmartStore.Utilities { @@ -221,24 +218,12 @@ public static IDictionary ObjectToDictionary(object obj) Type t = obj.GetType(); - return t.GetProperties(BindingFlags.Instance | BindingFlags.Public) + return FastProperty.GetProperties(t).Values .ToDictionary(k => k.Name.Replace("_", "-"), - v => obj.GetPropertyValue(v.Name), + v => v.GetValue(obj), StringComparer.OrdinalIgnoreCase); } - public static NameValueCollection ObjectToNameValueCollection(object obj) - { - var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase); - foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj)) - { - object value = descriptor.GetValue(obj); - result.Add(descriptor.Name, value.ToString()); - } - - return result; - } - /// /// Converts the type to an /// compatible object. diff --git a/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs b/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs index bcb4706ad6..34f4e74782 100644 --- a/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs +++ b/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.ComponentModel; using System.Text; using System.Collections; using System.Reflection; using System.Runtime.Serialization; -using Fasterflect; +using SmartStore.Utilities.Reflection; namespace SmartStore.Utilities { @@ -93,7 +92,7 @@ public static object CreateAndPopulate(Type targetType, IDictionary targetType); - var target = targetType.CreateInstance(); //Activator.CreateInstance(targetType); + var target = Activator.CreateInstance(targetType); Populate(source, target, out problems); @@ -141,15 +140,14 @@ public static void Populate(IDictionary source, object target, o if (source != null) { - // TODO: Metadaten aus einem TypeCache ziehen zwecks Performance! - foreach (var pi in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) + foreach (var fastProp in FastProperty.GetProperties(t).Values) { - object value; + var pi = fastProp.Property; if (!pi.PropertyType.IsPredefinedSimpleType() && source.TryGetValue(pi.Name, out value) && value is IDictionary) { - var nestedValue = target.GetPropertyValue(pi.Name); + var nestedValue = fastProp.GetValue(target); ICollection nestedProblems; populated = populated.Concat(new object[] { target }).ToArray(); @@ -159,17 +157,17 @@ public static void Populate(IDictionary source, object target, o { problems.AddRange(nestedProblems); } - WriteToProperty(target, pi, nestedValue, problems); + WriteToProperty(target, fastProp, nestedValue, problems); } else if (pi.PropertyType.IsArray && !source.ContainsKey(pi.Name)) { - WriteToProperty(target, pi, RetrieveArrayValues(pi, source, problems), problems); + WriteToProperty(target, fastProp, RetrieveArrayValues(pi, source, problems), problems); } else { if (source.TryGetValue(pi.Name, out value)) { - WriteToProperty(target, pi, value, problems); + WriteToProperty(target, fastProp, value, problems); } } } @@ -182,21 +180,19 @@ private static object RetrieveArrayValues(PropertyInfo arrayProp, IDictionary).MakeGenericType(elemType)); - var elements = (IList)typeof(List<>).MakeGenericType(elemType).CreateInstance(); + var elements = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elemType)); - // TODO: Metadaten aus einem TypeCache ziehen zwecks Performance! - var properties = elemType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var properties = FastProperty.GetProperties(elemType); while (anyValuesFound) { object curElement = null; anyValuesFound = false; // false until proven otherwise - foreach (var pi in properties) + foreach (var prop in properties.Values) { //var key = string.Format("_{0}{1}_{2}", idx, arrayProp.Name, pd.Name); - var key = string.Format("{0}[{1}].{2}", arrayProp.Name, idx, pi.Name); + var key = string.Format("{0}[{1}].{2}", arrayProp.Name, idx, prop.Name); object value; if (source.TryGetValue(key, out value)) @@ -205,11 +201,11 @@ private static object RetrieveArrayValues(PropertyInfo arrayProp, IDictionary problems) + private static void SetPropFromValue(object value, object item, FastProperty prop, ICollection problems) { - WriteToProperty(item, pi, value, problems); + WriteToProperty(item, prop, value, problems); } - private static void WriteToProperty(object item, PropertyInfo pi, object value, ICollection problems) + private static void WriteToProperty(object item, FastProperty prop, object value, ICollection problems) { - if (!pi.CanWrite) + var pi = prop.Property; + + if (!pi.CanWrite) return; try @@ -246,8 +244,7 @@ private static void WriteToProperty(object item, PropertyInfo pi, object value, if (pi.PropertyType.IsAssignableFrom(value.GetType())) { - //pi.SetValue(item, value); - item.SetPropertyValue(pi.Name, value); + prop.SetValue(item, value); return; } @@ -256,8 +253,7 @@ private static void WriteToProperty(object item, PropertyInfo pi, object value, destType = pi.PropertyType.GetGenericArguments()[0]; } - //pi.SetValue(item, value.Convert(destType)); - item.SetPropertyValue(pi.Name, value.Convert(destType)); + prop.SetValue(item, value.Convert(destType)); } } catch (Exception ex) diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs index 27498b5576..e72bbafcd9 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs @@ -355,9 +355,9 @@ public static IDictionary ObjectToDictionary(object value) if (value != null) { - foreach (var helper in GetProperties(value).Values) + foreach (var prop in GetProperties(value).Values) { - dictionary[helper.Name] = helper.GetValue(value); + dictionary[prop.Name] = prop.GetValue(value); } } diff --git a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs index 34dbf27b18..482b1ed9bd 100644 --- a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Globalization; using System.IO; -using Fasterflect; namespace SmartStore.Utilities { @@ -332,16 +331,6 @@ public static void RegisterTypeConverter() where TC : TypeConverter TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC))); } - public static T CreateInstance(params object[] parameters) where T : class - { - return (T)typeof(T).CreateInstance(parameters); - } - - public static object CreateInstance(Type type, params object[] parameters) - { - return type.CreateInstance(parameters); - } - } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/packages.config b/src/Libraries/SmartStore.Core/packages.config index 923c1183a2..78f9872f99 100644 --- a/src/Libraries/SmartStore.Core/packages.config +++ b/src/Libraries/SmartStore.Core/packages.config @@ -3,7 +3,6 @@ - diff --git a/src/Libraries/SmartStore.Data/app.config b/src/Libraries/SmartStore.Data/app.config index 4f592fbcd8..7808ca6f28 100644 --- a/src/Libraries/SmartStore.Data/app.config +++ b/src/Libraries/SmartStore.Data/app.config @@ -1,15 +1,15 @@ - + - - + + - - + + - + diff --git a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs index c439ee40b5..ae83128abb 100644 --- a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs +++ b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs @@ -9,7 +9,7 @@ using SmartStore.Core.Domain.Configuration; using SmartStore.Core.Infrastructure; using SmartStore.Core.Events; -using Fasterflect; +//using Fasterflect; using System.Linq.Expressions; using System.Reflection; using SmartStore.Core.Plugins; @@ -413,18 +413,20 @@ public virtual void SetSetting(string key, T value, int storeId = 0, bool cle /* We do not clear cache after each setting update. * This behavior can increase performance because cached settings will not be cleared * and loaded from database after each update */ - foreach (var prop in typeof(T).GetProperties()) + foreach (var prop in FastProperty.GetProperties(typeof(T)).Values) { + var pi = prop.Property; + // get properties we can read and write to - if (!prop.CanRead || !prop.CanWrite) + if (!pi.CanRead || !pi.CanWrite) continue; - if (!CommonHelper.GetTypeConverter(prop.PropertyType).CanConvertFrom(typeof(string))) + if (!CommonHelper.GetTypeConverter(pi.PropertyType).CanConvertFrom(typeof(string))) continue; string key = typeof(T).Name + "." + prop.Name; //Duck typing is not supported in C#. That's why we're using dynamic type - dynamic value = settings.TryGetPropertyValue(prop.Name); + dynamic value = prop.GetValue(settings); SetSetting(key, value ?? "", storeId, false); } @@ -462,8 +464,9 @@ public virtual void SaveSetting(T settings, } string key = typeof(T).Name + "." + propInfo.Name; - //Duck typing is not supported in C#. That's why we're using dynamic type - dynamic value = settings.TryGetPropertyValue(propInfo.Name); + // Duck typing is not supported in C#. That's why we're using dynamic type + var fastProp = FastProperty.GetProperty(settings.GetType(), propInfo.Name, true); + dynamic value = fastProp.GetValue(settings); SetSetting(key, value ?? "", storeId, false); } diff --git a/src/Libraries/SmartStore.Services/ExportImport/DataSegmenter.cs b/src/Libraries/SmartStore.Services/ExportImport/DataSegmenter.cs index 303f12a2c5..6bb3693cd1 100644 --- a/src/Libraries/SmartStore.Services/ExportImport/DataSegmenter.cs +++ b/src/Libraries/SmartStore.Services/ExportImport/DataSegmenter.cs @@ -5,10 +5,10 @@ using System.Linq.Expressions; using System.Reflection; using System.Text; -using Fasterflect; using OfficeOpenXml; using SmartStore.Core; using SmartStore.Core.Data; +using SmartStore.Utilities.Reflection; namespace SmartStore.Services.ExportImport { @@ -277,6 +277,12 @@ public int Position get { return _position; } } + public object Fastproperty + { + get; + private set; + } + public TProp GetValue(string columnName) { object value; @@ -298,6 +304,8 @@ public TProp GetValue(string columnName) try { + var fastProp = FastProperty.GetProperty(typeof(T), propName, true); + object value; if (this.TryGetValue(propName, out value)) { @@ -319,7 +327,9 @@ public TProp GetValue(string columnName) converted = value.Convert(); } } - return target.TrySetPropertyValue(propName, converted); + + fastProp.SetValue(target, converted); + return true; } else { @@ -328,7 +338,8 @@ public TProp GetValue(string columnName) { // ...but the entity is new. In this case // set the default value if given. - return target.TrySetPropertyValue(propName, defaultValue); + fastProp.SetValue(target, defaultValue); + return true; } } } diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs index b73e7660ed..0256ef1ac8 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationExtentions.cs @@ -5,12 +5,13 @@ using SmartStore.Core.Domain.Localization; using SmartStore.Core.Infrastructure; using SmartStore.Core.Plugins; -using Fasterflect; +//using Fasterflect; using System.Xml; using SmartStore.Core.Data; using SmartStore.Utilities; using System.Collections.Concurrent; using SmartStore.Core.ComponentModel; +using SmartStore.Utilities.Reflection; namespace SmartStore.Services.Localization { @@ -324,7 +325,13 @@ public static string GetLocalizedValue(this PluginDescriptor descriptor, ILocali string result = localizationService.GetResource(resourceName, languageId, false, "", true); if (String.IsNullOrEmpty(result) && returnDefaultValue) - result = descriptor.TryGetPropertyValue(propertyName) as string; + { + var fastProp = FastProperty.GetProperty(descriptor.GetType(), propertyName); + if (fastProp != null) + { + result = fastProp.GetValue(descriptor) as string; + } + } return result; } diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index f63d565765..76cfeb1040 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -80,10 +80,6 @@ False ..\..\packages\EPPlus.4.0.3\lib\net20\EPPlus.dll - - False - ..\..\packages\fasterflect.2.1.3\lib\net40\Fasterflect.dll - False ..\..\packages\ImageResizer.3.4.2\lib\ImageResizer.dll diff --git a/src/Libraries/SmartStore.Services/packages.config b/src/Libraries/SmartStore.Services/packages.config index 2419041a2c..2efca85a84 100644 --- a/src/Libraries/SmartStore.Services/packages.config +++ b/src/Libraries/SmartStore.Services/packages.config @@ -4,7 +4,6 @@ - diff --git a/src/Plugins/SmartStore.Clickatell/web.config b/src/Plugins/SmartStore.Clickatell/web.config index 46b8ba77d4..deaeafe833 100644 --- a/src/Plugins/SmartStore.Clickatell/web.config +++ b/src/Plugins/SmartStore.Clickatell/web.config @@ -106,7 +106,7 @@ - + diff --git a/src/Plugins/SmartStore.DevTools/Web.config b/src/Plugins/SmartStore.DevTools/Web.config index e0593e522a..58d0f26ce4 100644 --- a/src/Plugins/SmartStore.DevTools/Web.config +++ b/src/Plugins/SmartStore.DevTools/Web.config @@ -114,7 +114,7 @@ - + diff --git a/src/Plugins/SmartStore.DiscountRules/web.config b/src/Plugins/SmartStore.DiscountRules/web.config index 46b8ba77d4..deaeafe833 100644 --- a/src/Plugins/SmartStore.DiscountRules/web.config +++ b/src/Plugins/SmartStore.DiscountRules/web.config @@ -106,7 +106,7 @@ - + diff --git a/src/Plugins/SmartStore.FacebookAuth/web.config b/src/Plugins/SmartStore.FacebookAuth/web.config index 10ad5c862a..b294e19325 100644 --- a/src/Plugins/SmartStore.FacebookAuth/web.config +++ b/src/Plugins/SmartStore.FacebookAuth/web.config @@ -110,7 +110,7 @@ - + diff --git a/src/Plugins/SmartStore.GoogleAnalytics/web.config b/src/Plugins/SmartStore.GoogleAnalytics/web.config index ad003b3e26..d4330046b2 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/web.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/web.config @@ -105,7 +105,7 @@ - + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config index 46b8ba77d4..deaeafe833 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config @@ -106,7 +106,7 @@ - + diff --git a/src/Plugins/SmartStore.OfflinePayment/web.config b/src/Plugins/SmartStore.OfflinePayment/web.config index 46b8ba77d4..deaeafe833 100644 --- a/src/Plugins/SmartStore.OfflinePayment/web.config +++ b/src/Plugins/SmartStore.OfflinePayment/web.config @@ -106,7 +106,7 @@ - + diff --git a/src/Plugins/SmartStore.Shipping/web.config b/src/Plugins/SmartStore.Shipping/web.config index ad003b3e26..d4330046b2 100644 --- a/src/Plugins/SmartStore.Shipping/web.config +++ b/src/Plugins/SmartStore.Shipping/web.config @@ -105,7 +105,7 @@ - + diff --git a/src/Plugins/SmartStore.ShippingByWeight/web.config b/src/Plugins/SmartStore.ShippingByWeight/web.config index ad003b3e26..d4330046b2 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/web.config +++ b/src/Plugins/SmartStore.ShippingByWeight/web.config @@ -105,7 +105,7 @@ - + diff --git a/src/Plugins/SmartStore.Tax/web.config b/src/Plugins/SmartStore.Tax/web.config index 46b8ba77d4..deaeafe833 100644 --- a/src/Plugins/SmartStore.Tax/web.config +++ b/src/Plugins/SmartStore.Tax/web.config @@ -106,7 +106,7 @@ - + diff --git a/src/Plugins/SmartStore.WebApi/web.config b/src/Plugins/SmartStore.WebApi/web.config index 0219bc2ade..8c7ae2efce 100644 --- a/src/Plugins/SmartStore.WebApi/web.config +++ b/src/Plugins/SmartStore.WebApi/web.config @@ -78,7 +78,7 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs index 802fea0798..7f0f2e0096 100644 --- a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Web.Mvc; -using Fasterflect; using SmartStore.Core.Infrastructure; using SmartStore.Services.Configuration; using SmartStore.Services.Localization; using SmartStore.Web.Framework.Localization; +using SmartStore.Utilities.Reflection; namespace SmartStore.Web.Framework.Settings { @@ -139,7 +139,7 @@ public void GetOverrideKeysLocalized(object settings, object model, int storeId, public void UpdateSettings(object settings, FormCollection form, int storeId, ISettingService settingService) { var settingName = settings.GetType().Name; - var properties = settings.GetType().GetProperties(); + var properties = FastProperty.GetProperties(settings.GetType()).Values; foreach (var prop in properties) { @@ -148,7 +148,7 @@ public void UpdateSettings(object settings, FormCollection form, int storeId, IS if (storeId == 0 || IsOverrideChecked(key, form)) { - dynamic value = settings.TryGetPropertyValue(name); + dynamic value = prop.GetValue(settings); settingService.SetSetting(key, value == null ? "" : value, storeId, false); } else if (storeId > 0) @@ -161,7 +161,7 @@ public void UpdateSettings(object settings, FormCollection form, int storeId, IS public void UpdateLocalizedSettings(object settings, FormCollection form, int storeId, ISettingService settingService, ILocalizedModelLocal localized) { var settingName = settings.GetType().Name; - var properties = localized.GetType().GetProperties(); + var properties = FastProperty.GetProperties(localized.GetType()).Values; foreach (var prop in properties) { @@ -170,7 +170,7 @@ public void UpdateLocalizedSettings(object settings, FormCollection form, int st if (storeId == 0 || IsOverrideChecked(key, form)) { - dynamic value = settings.TryGetPropertyValue(name); + dynamic value = prop.GetValue(settings); settingService.SetSetting(key, value == null ? "" : value, storeId, false); } else if (storeId > 0) diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index dba5ac1aaf..b70e4658a1 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -101,10 +101,6 @@ False ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - - False - ..\..\packages\fasterflect.2.1.3\lib\net40\Fasterflect.dll - False ..\..\packages\FluentValidation.5.0.0.1\lib\Net40\FluentValidation.dll diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs index e315ebacd9..b45a50dfae 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs @@ -88,7 +88,7 @@ public TBuilder WithRenderer(Type rendererType) { Guard.ArgumentNotNull(() => rendererType); Guard.Implements>(rendererType); - var renderer = TypeHelper.CreateInstance(rendererType) as ComponentRenderer; + var renderer = Activator.CreateInstance(rendererType) as ComponentRenderer; if (renderer != null) { this.Renderer = renderer; diff --git a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs index 4354426a9f..3f48b374d2 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebApi/WebApiEntityController.cs @@ -16,6 +16,7 @@ using SmartStore.Services.Directory; using SmartStore.Services.Localization; using System.Collections.Generic; +using SmartStore.Utilities.Reflection; namespace SmartStore.Web.Framework.WebApi { @@ -77,7 +78,7 @@ protected virtual internal HttpResponseMessage UnmappedGetProperty(ODataPath oda if (entity == null) return Request.CreateErrorResponse(HttpStatusCode.NotFound, WebApiGlobal.Error.EntityNotFound.FormatInvariant(key)); - PropertyInfo pi = null; + FastProperty prop = null; string propertyName = null; var lastSegment = odataPath.Segments.Last(); var propertySegment = (lastSegment as PropertyAccessPathSegment); @@ -88,14 +89,14 @@ protected virtual internal HttpResponseMessage UnmappedGetProperty(ODataPath oda propertyName = propertySegment.PropertyName; if (propertyName.HasValue()) - pi = entity.GetType().GetProperty(propertyName); + prop = FastProperty.GetProperty(entity.GetType(), propertyName); - if (pi == null) + if (prop == null) return UnmappedGetProperty(entity, propertyName ?? ""); - var propertyValue = pi.GetValue(entity, null); + var propertyValue = prop.GetValue(entity); - return Request.CreateResponse(HttpStatusCode.OK, pi.PropertyType, propertyValue); + return Request.CreateResponse(HttpStatusCode.OK, prop.Property.PropertyType, propertyValue); } protected virtual internal HttpResponseMessage UnmappedGetProperty(TEntity entity, string propertyName) @@ -465,17 +466,17 @@ protected internal virtual TEntity FulfillPropertiesOn(TEntity entity) if (propertyName.HasValue() && queryValue.HasValue()) { - var pi = entity.GetType().GetProperty(propertyName); - if (pi != null) + var prop = FastProperty.GetProperty(entity.GetType(), propertyName); + if (prop != null) { - var propertyValue = pi.GetValue(entity, null); + var propertyValue = prop.GetValue(entity); if (propertyValue == null) { object value = FulfillPropertyOn(entity, propertyName, queryValue); if (value != null) // there's no requirement to set a property value of null { - pi.SetValue(entity, value); + prop.SetValue(entity, value); } } } diff --git a/src/Presentation/SmartStore.Web.Framework/packages.config b/src/Presentation/SmartStore.Web.Framework/packages.config index 2f40cf1830..1688ceb9e7 100644 --- a/src/Presentation/SmartStore.Web.Framework/packages.config +++ b/src/Presentation/SmartStore.Web.Framework/packages.config @@ -9,7 +9,6 @@ - diff --git a/src/Presentation/SmartStore.Web/Administration/Web.config b/src/Presentation/SmartStore.Web/Administration/Web.config index 7d3a217dd0..82ce6dbca5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Web.config +++ b/src/Presentation/SmartStore.Web/Administration/Web.config @@ -100,7 +100,7 @@ - + diff --git a/src/Tools/SmartStore.Packager/App.config b/src/Tools/SmartStore.Packager/App.config index 30622f85af..59552e8d6b 100644 --- a/src/Tools/SmartStore.Packager/App.config +++ b/src/Tools/SmartStore.Packager/App.config @@ -1,32 +1,32 @@ - + -
+
- + - + - + - - + + - - + + From adbce646e1586e37330c0e0178b23c9b7605e1a1 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 8 Nov 2015 11:40:45 +0100 Subject: [PATCH 043/732] ExportXmlHelper creates own XmlWriter --- .../ExportTask/ExportProfileTask.cs | 2 + .../DataExchange/ExportXmlHelper.cs | 39 ++- .../Providers/CategoryXmlExportProvider.cs | 49 +-- .../Providers/CustomerXmlExportProvider.cs | 27 +- .../ManufacturerXmlExportProvider.cs | 49 +-- .../Providers/OrderXmlExportProvider.cs | 308 +++++++++--------- .../Providers/ProductXmlExportProvider.cs | 27 +- .../Controllers/ExportController.cs | 3 +- 8 files changed, 238 insertions(+), 266 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 86c909fc27..1a52e264ea 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -132,6 +132,8 @@ private void InitDependencies(TaskExecutionContext context) _urlRecordService = context.Resolve>(); _genericAttributeService = context.Resolve>(); _subscriptionRepository = context.Resolve>>(); + + T = NullLocalizer.Instance; } public Localizer T { get; set; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs index 8191f0d76e..e5837578d6 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs @@ -1,20 +1,53 @@ using System; using System.Globalization; +using System.IO; +using System.Text; using System.Xml; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.DataExchange; namespace SmartStore.Services.DataExchange { - public class ExportXmlHelper + public class ExportXmlHelper : IDisposable { private XmlWriter _writer; private CultureInfo _culture; + private bool _doNotDispose; - public ExportXmlHelper(XmlWriter writer, CultureInfo culture) + public ExportXmlHelper(XmlWriter writer, bool doNotDispose = false, CultureInfo culture = null) { _writer = writer; - _culture = culture; + _doNotDispose = doNotDispose; + _culture = (culture == null ? CultureInfo.InvariantCulture : culture); + } + public ExportXmlHelper(Stream stream, XmlWriterSettings settings = null, CultureInfo culture = null) + { + if (settings == null) + { + settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CheckCharacters = false, + Indent = true, + IndentChars = "\t" + }; + } + + _writer = XmlWriter.Create(stream, settings); + _culture = (culture == null ? CultureInfo.InvariantCulture : culture); + } + + public XmlWriter Writer + { + get { return _writer; } + } + + public void Dispose() + { + if (_writer != null && !_doNotDispose) + { + _writer.Dispose(); + } } public void WriteLocalized(dynamic parentNode) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs index e473a28ad2..2b74231730 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs @@ -1,8 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Text; -using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -35,26 +32,16 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - var path = context.FilePath; context.Log.Information("Creating file " + path); using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var helper = new ExportXmlHelper(stream)) { - var xmlHelper = new ExportXmlHelper(writer, CultureInfo.InvariantCulture); - - writer.WriteStartDocument(); - writer.WriteStartElement("Categories"); - writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); + helper.Writer.WriteStartDocument(); + helper.Writer.WriteStartElement("Categories"); + helper.Writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { @@ -65,24 +52,24 @@ public override void Execute(IExportExecuteContext context) if (context.Abort != ExportAbortion.None) break; - writer.WriteStartElement("Category"); + helper.Writer.WriteStartElement("Category"); try { - xmlHelper.WriteCategory(category, null); + helper.WriteCategory(category, null); - writer.WriteStartElement("ProductCategories"); + helper.Writer.WriteStartElement("ProductCategories"); foreach (dynamic productCategory in category.ProductCategories) { - writer.WriteStartElement("ProductCategory"); - writer.Write("Id", ((int)productCategory.Id).ToString()); - writer.Write("ProductId", ((int)productCategory.ProductId).ToString()); - writer.Write("DisplayOrder", ((int)productCategory.DisplayOrder).ToString()); - writer.Write("IsFeaturedProduct", ((bool)productCategory.IsFeaturedProduct).ToString()); - writer.Write("CategoryId", ((int)productCategory.CategoryId).ToString()); - writer.WriteEndElement(); // ProductCategory + helper.Writer.WriteStartElement("ProductCategory"); + helper.Writer.Write("Id", ((int)productCategory.Id).ToString()); + helper.Writer.Write("ProductId", ((int)productCategory.ProductId).ToString()); + helper.Writer.Write("DisplayOrder", ((int)productCategory.DisplayOrder).ToString()); + helper.Writer.Write("IsFeaturedProduct", ((bool)productCategory.IsFeaturedProduct).ToString()); + helper.Writer.Write("CategoryId", ((int)productCategory.CategoryId).ToString()); + helper.Writer.WriteEndElement(); // ProductCategory } - writer.WriteEndElement(); // ProductCategories + helper.Writer.WriteEndElement(); // ProductCategories ++context.RecordsSucceeded; } @@ -91,12 +78,12 @@ public override void Execute(IExportExecuteContext context) context.RecordException(exc, (int)category.Id); } - writer.WriteEndElement(); // Category + helper.Writer.WriteEndElement(); // Category } } - writer.WriteEndElement(); // Categories - writer.WriteEndDocument(); + helper.Writer.WriteEndElement(); // Categories + helper.Writer.WriteEndDocument(); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs index 93d9c4462b..4103b412a5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs @@ -1,8 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Text; -using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -35,26 +32,16 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - var path = context.FilePath; context.Log.Information("Creating file " + path); using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var helper = new ExportXmlHelper(stream)) { - var xmlHelper = new ExportXmlHelper(writer, CultureInfo.InvariantCulture); - - writer.WriteStartDocument(); - writer.WriteStartElement("Customers"); - writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); + helper.Writer.WriteStartDocument(); + helper.Writer.WriteStartElement("Customers"); + helper.Writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { @@ -67,7 +54,7 @@ public override void Execute(IExportExecuteContext context) try { - xmlHelper.WriteCustomer(customer, "Customer"); + helper.WriteCustomer(customer, "Customer"); ++context.RecordsSucceeded; } @@ -78,8 +65,8 @@ public override void Execute(IExportExecuteContext context) } } - writer.WriteEndElement(); // Customers - writer.WriteEndDocument(); + helper.Writer.WriteEndElement(); // Customers + helper.Writer.WriteEndDocument(); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs index 8d26a9ab4e..3c5d6afa53 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs @@ -1,8 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Text; -using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -35,26 +32,16 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - var path = context.FilePath; context.Log.Information("Creating file " + path); using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var helper = new ExportXmlHelper(stream)) { - var xmlHelper = new ExportXmlHelper(writer, CultureInfo.InvariantCulture); - - writer.WriteStartDocument(); - writer.WriteStartElement("Manufacturers"); - writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); + helper.Writer.WriteStartDocument(); + helper.Writer.WriteStartElement("Manufacturers"); + helper.Writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { @@ -65,24 +52,24 @@ public override void Execute(IExportExecuteContext context) if (context.Abort != ExportAbortion.None) break; - writer.WriteStartElement("Manufacturer"); + helper.Writer.WriteStartElement("Manufacturer"); try { - xmlHelper.WriteManufacturer(manufacturer, null); + helper.WriteManufacturer(manufacturer, null); - writer.WriteStartElement("ProductManufacturers"); + helper.Writer.WriteStartElement("ProductManufacturers"); foreach (dynamic productManu in manufacturer.ProductManufacturers) { - writer.WriteStartElement("ProductManufacturer"); - writer.Write("Id", ((int)productManu.Id).ToString()); - writer.Write("ProductId", ((int)productManu.ProductId).ToString()); - writer.Write("DisplayOrder", ((int)productManu.DisplayOrder).ToString()); - writer.Write("IsFeaturedProduct", ((bool)productManu.IsFeaturedProduct).ToString()); - writer.Write("ManufacturerId", ((int)productManu.ManufacturerId).ToString()); - writer.WriteEndElement(); // ProductManufacturer + helper.Writer.WriteStartElement("ProductManufacturer"); + helper.Writer.Write("Id", ((int)productManu.Id).ToString()); + helper.Writer.Write("ProductId", ((int)productManu.ProductId).ToString()); + helper.Writer.Write("DisplayOrder", ((int)productManu.DisplayOrder).ToString()); + helper.Writer.Write("IsFeaturedProduct", ((bool)productManu.IsFeaturedProduct).ToString()); + helper.Writer.Write("ManufacturerId", ((int)productManu.ManufacturerId).ToString()); + helper.Writer.WriteEndElement(); // ProductManufacturer } - writer.WriteEndElement(); // ProductManufacturers + helper.Writer.WriteEndElement(); // ProductManufacturers ++context.RecordsSucceeded; } @@ -91,12 +78,12 @@ public override void Execute(IExportExecuteContext context) context.RecordException(exc, (int)manufacturer.Id); } - writer.WriteEndElement(); // Manufacturer + helper.Writer.WriteEndElement(); // Manufacturer } } - writer.WriteEndElement(); // Manufacturers - writer.WriteEndDocument(); + helper.Writer.WriteEndElement(); // Manufacturers + helper.Writer.WriteEndDocument(); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs index 15d347847f..470155603a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; using System.IO; -using System.Text; -using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -35,27 +33,17 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - var path = context.FilePath; var invariantCulture = CultureInfo.InvariantCulture; context.Log.Information("Creating file " + path); using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var helper = new ExportXmlHelper(stream)) { - var xmlHelper = new ExportXmlHelper(writer, invariantCulture); - - writer.WriteStartDocument(); - writer.WriteStartElement("Orders"); - writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); + helper.Writer.WriteStartDocument(); + helper.Writer.WriteStartElement("Orders"); + helper.Writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { @@ -66,7 +54,7 @@ public override void Execute(IExportExecuteContext context) if (context.Abort != ExportAbortion.None) break; - writer.WriteStartElement("Order"); + helper.Writer.WriteStartElement("Order"); try { @@ -75,170 +63,170 @@ public override void Execute(IExportExecuteContext context) DateTime? paidDateUtc = order.PaidDateUtc; int? rewardPointsRemaining = order.RewardPointsRemaining; - writer.Write("Id", ((int)order.Id).ToString()); - writer.Write("OrderNumber", (string)order.OrderNumber); - writer.Write("OrderGuid", ((Guid)order.OrderGuid).ToString()); - writer.Write("StoreId", ((int)order.StoreId).ToString()); - writer.Write("CustomerId", ((int)order.CustomerId).ToString()); - writer.Write("BillingAddressId", ((int)order.BillingAddressId).ToString()); - writer.Write("ShippingAddressId", shippingAddressId.HasValue ? shippingAddressId.Value.ToString() : ""); - writer.Write("OrderStatusId", ((int)order.OrderStatusId).ToString()); - writer.Write("ShippingStatusId", ((int)order.ShippingStatusId).ToString()); - writer.Write("PaymentStatusId", ((int)order.PaymentStatusId).ToString()); - writer.Write("PaymentMethodSystemName", (string)order.PaymentMethodSystemName); - writer.Write("CustomerCurrencyCode", (string)order.CustomerCurrencyCode); - writer.Write("CurrencyRate", ((decimal)order.CurrencyRate).ToString(invariantCulture)); - writer.Write("CustomerTaxDisplayTypeId", ((int)order.CustomerTaxDisplayTypeId).ToString()); - writer.Write("VatNumber", (string)order.VatNumber); - writer.Write("OrderSubtotalInclTax", ((decimal)order.OrderSubtotalInclTax).ToString(invariantCulture)); - writer.Write("OrderSubtotalExclTax", ((decimal)order.OrderSubtotalExclTax).ToString(invariantCulture)); - writer.Write("OrderSubTotalDiscountInclTax", ((decimal)order.OrderSubTotalDiscountInclTax).ToString(invariantCulture)); - writer.Write("OrderSubTotalDiscountExclTax", ((decimal)order.OrderSubTotalDiscountExclTax).ToString(invariantCulture)); - writer.Write("OrderShippingInclTax", ((decimal)order.OrderShippingInclTax).ToString(invariantCulture)); - writer.Write("OrderShippingExclTax", ((decimal)order.OrderShippingExclTax).ToString(invariantCulture)); - writer.Write("OrderShippingTaxRate", ((decimal)order.OrderShippingTaxRate).ToString(invariantCulture)); - writer.Write("PaymentMethodAdditionalFeeInclTax", ((decimal)order.PaymentMethodAdditionalFeeInclTax).ToString(invariantCulture)); - writer.Write("PaymentMethodAdditionalFeeExclTax", ((decimal)order.PaymentMethodAdditionalFeeExclTax).ToString(invariantCulture)); - writer.Write("PaymentMethodAdditionalFeeTaxRate", ((decimal)order.PaymentMethodAdditionalFeeTaxRate).ToString(invariantCulture)); - writer.Write("TaxRates", (string)order.TaxRates); - writer.Write("OrderTax", ((decimal)order.OrderTax).ToString(invariantCulture)); - writer.Write("OrderDiscount", ((decimal)order.OrderDiscount).ToString(invariantCulture)); - writer.Write("OrderTotal", ((decimal)order.OrderTotal).ToString(invariantCulture)); - writer.Write("RefundedAmount", ((decimal)order.RefundedAmount).ToString(invariantCulture)); - writer.Write("RewardPointsWereAdded", ((bool)order.RewardPointsWereAdded).ToString()); - writer.Write("CheckoutAttributeDescription", (string)order.CheckoutAttributeDescription); - writer.Write("CheckoutAttributesXml", (string)order.CheckoutAttributesXml); - writer.Write("CustomerLanguageId", ((int)order.CustomerLanguageId).ToString()); - writer.Write("AffiliateId", ((int)order.AffiliateId).ToString()); - writer.Write("CustomerIp", (string)order.CustomerIp); - writer.Write("AllowStoringCreditCardNumber", ((bool)order.AllowStoringCreditCardNumber).ToString()); - writer.Write("CardType", (string)order.CardType); - writer.Write("CardName", (string)order.CardName); - writer.Write("CardNumber", (string)order.CardNumber); - writer.Write("MaskedCreditCardNumber", (string)order.MaskedCreditCardNumber); - writer.Write("CardCvv2", (string)order.CardCvv2); - writer.Write("CardExpirationMonth", (string)order.CardExpirationMonth); - writer.Write("CardExpirationYear", (string)order.CardExpirationYear); - writer.Write("AllowStoringDirectDebit", ((bool)order.AllowStoringDirectDebit).ToString()); - writer.Write("DirectDebitAccountHolder", (string)order.DirectDebitAccountHolder); - writer.Write("DirectDebitAccountNumber", (string)order.DirectDebitAccountNumber); - writer.Write("DirectDebitBankCode", (string)order.DirectDebitBankCode); - writer.Write("DirectDebitBankName", (string)order.DirectDebitBankName); - writer.Write("DirectDebitBIC", (string)order.DirectDebitBIC); - writer.Write("DirectDebitCountry", (string)order.DirectDebitCountry); - writer.Write("DirectDebitIban", (string)order.DirectDebitIban); - writer.Write("CustomerOrderComment", (string)order.CustomerOrderComment); - writer.Write("AuthorizationTransactionId", (string)order.AuthorizationTransactionId); - writer.Write("AuthorizationTransactionCode", (string)order.AuthorizationTransactionCode); - writer.Write("AuthorizationTransactionResult", (string)order.AuthorizationTransactionResult); - writer.Write("CaptureTransactionId", (string)order.CaptureTransactionId); - writer.Write("CaptureTransactionResult", (string)order.CaptureTransactionResult); - writer.Write("SubscriptionTransactionId", (string)order.SubscriptionTransactionId); - writer.Write("PurchaseOrderNumber", (string)order.PurchaseOrderNumber); - writer.Write("PaidDateUtc", paidDateUtc.HasValue ? paidDateUtc.Value.ToString(invariantCulture) : ""); - writer.Write("ShippingMethod", (string)order.ShippingMethod); - writer.Write("ShippingRateComputationMethodSystemName", (string)order.ShippingRateComputationMethodSystemName); - writer.Write("Deleted", ((bool)order.Deleted).ToString()); - writer.Write("CreatedOnUtc", ((DateTime)order.CreatedOnUtc).ToString(invariantCulture)); - writer.Write("UpdatedOnUtc", ((DateTime)order.UpdatedOnUtc).ToString(invariantCulture)); - writer.Write("RewardPointsRemaining", rewardPointsRemaining.HasValue ? rewardPointsRemaining.Value.ToString() : ""); - writer.Write("HasNewPaymentNotification", ((bool)order.HasNewPaymentNotification).ToString()); - writer.Write("OrderStatus", (string)order.OrderStatus); - writer.Write("PaymentStatus", (string)order.PaymentStatus); - writer.Write("ShippingStatus", (string)order.ShippingStatus); - - xmlHelper.WriteCustomer(order.Customer, "Customer"); - - xmlHelper.WriteAddress(order.BillingAddress, "BillingAddress"); - xmlHelper.WriteAddress(order.ShippingAddress, "ShippingAddress"); + helper.Writer.Write("Id", ((int)order.Id).ToString()); + helper.Writer.Write("OrderNumber", (string)order.OrderNumber); + helper.Writer.Write("OrderGuid", ((Guid)order.OrderGuid).ToString()); + helper.Writer.Write("StoreId", ((int)order.StoreId).ToString()); + helper.Writer.Write("CustomerId", ((int)order.CustomerId).ToString()); + helper.Writer.Write("BillingAddressId", ((int)order.BillingAddressId).ToString()); + helper.Writer.Write("ShippingAddressId", shippingAddressId.HasValue ? shippingAddressId.Value.ToString() : ""); + helper.Writer.Write("OrderStatusId", ((int)order.OrderStatusId).ToString()); + helper.Writer.Write("ShippingStatusId", ((int)order.ShippingStatusId).ToString()); + helper.Writer.Write("PaymentStatusId", ((int)order.PaymentStatusId).ToString()); + helper.Writer.Write("PaymentMethodSystemName", (string)order.PaymentMethodSystemName); + helper.Writer.Write("CustomerCurrencyCode", (string)order.CustomerCurrencyCode); + helper.Writer.Write("CurrencyRate", ((decimal)order.CurrencyRate).ToString(invariantCulture)); + helper.Writer.Write("CustomerTaxDisplayTypeId", ((int)order.CustomerTaxDisplayTypeId).ToString()); + helper.Writer.Write("VatNumber", (string)order.VatNumber); + helper.Writer.Write("OrderSubtotalInclTax", ((decimal)order.OrderSubtotalInclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderSubtotalExclTax", ((decimal)order.OrderSubtotalExclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderSubTotalDiscountInclTax", ((decimal)order.OrderSubTotalDiscountInclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderSubTotalDiscountExclTax", ((decimal)order.OrderSubTotalDiscountExclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderShippingInclTax", ((decimal)order.OrderShippingInclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderShippingExclTax", ((decimal)order.OrderShippingExclTax).ToString(invariantCulture)); + helper.Writer.Write("OrderShippingTaxRate", ((decimal)order.OrderShippingTaxRate).ToString(invariantCulture)); + helper.Writer.Write("PaymentMethodAdditionalFeeInclTax", ((decimal)order.PaymentMethodAdditionalFeeInclTax).ToString(invariantCulture)); + helper.Writer.Write("PaymentMethodAdditionalFeeExclTax", ((decimal)order.PaymentMethodAdditionalFeeExclTax).ToString(invariantCulture)); + helper.Writer.Write("PaymentMethodAdditionalFeeTaxRate", ((decimal)order.PaymentMethodAdditionalFeeTaxRate).ToString(invariantCulture)); + helper.Writer.Write("TaxRates", (string)order.TaxRates); + helper.Writer.Write("OrderTax", ((decimal)order.OrderTax).ToString(invariantCulture)); + helper.Writer.Write("OrderDiscount", ((decimal)order.OrderDiscount).ToString(invariantCulture)); + helper.Writer.Write("OrderTotal", ((decimal)order.OrderTotal).ToString(invariantCulture)); + helper.Writer.Write("RefundedAmount", ((decimal)order.RefundedAmount).ToString(invariantCulture)); + helper.Writer.Write("RewardPointsWereAdded", ((bool)order.RewardPointsWereAdded).ToString()); + helper.Writer.Write("CheckoutAttributeDescription", (string)order.CheckoutAttributeDescription); + helper.Writer.Write("CheckoutAttributesXml", (string)order.CheckoutAttributesXml); + helper.Writer.Write("CustomerLanguageId", ((int)order.CustomerLanguageId).ToString()); + helper.Writer.Write("AffiliateId", ((int)order.AffiliateId).ToString()); + helper.Writer.Write("CustomerIp", (string)order.CustomerIp); + helper.Writer.Write("AllowStoringCreditCardNumber", ((bool)order.AllowStoringCreditCardNumber).ToString()); + helper.Writer.Write("CardType", (string)order.CardType); + helper.Writer.Write("CardName", (string)order.CardName); + helper.Writer.Write("CardNumber", (string)order.CardNumber); + helper.Writer.Write("MaskedCreditCardNumber", (string)order.MaskedCreditCardNumber); + helper.Writer.Write("CardCvv2", (string)order.CardCvv2); + helper.Writer.Write("CardExpirationMonth", (string)order.CardExpirationMonth); + helper.Writer.Write("CardExpirationYear", (string)order.CardExpirationYear); + helper.Writer.Write("AllowStoringDirectDebit", ((bool)order.AllowStoringDirectDebit).ToString()); + helper.Writer.Write("DirectDebitAccountHolder", (string)order.DirectDebitAccountHolder); + helper.Writer.Write("DirectDebitAccountNumber", (string)order.DirectDebitAccountNumber); + helper.Writer.Write("DirectDebitBankCode", (string)order.DirectDebitBankCode); + helper.Writer.Write("DirectDebitBankName", (string)order.DirectDebitBankName); + helper.Writer.Write("DirectDebitBIC", (string)order.DirectDebitBIC); + helper.Writer.Write("DirectDebitCountry", (string)order.DirectDebitCountry); + helper.Writer.Write("DirectDebitIban", (string)order.DirectDebitIban); + helper.Writer.Write("CustomerOrderComment", (string)order.CustomerOrderComment); + helper.Writer.Write("AuthorizationTransactionId", (string)order.AuthorizationTransactionId); + helper.Writer.Write("AuthorizationTransactionCode", (string)order.AuthorizationTransactionCode); + helper.Writer.Write("AuthorizationTransactionResult", (string)order.AuthorizationTransactionResult); + helper.Writer.Write("CaptureTransactionId", (string)order.CaptureTransactionId); + helper.Writer.Write("CaptureTransactionResult", (string)order.CaptureTransactionResult); + helper.Writer.Write("SubscriptionTransactionId", (string)order.SubscriptionTransactionId); + helper.Writer.Write("PurchaseOrderNumber", (string)order.PurchaseOrderNumber); + helper.Writer.Write("PaidDateUtc", paidDateUtc.HasValue ? paidDateUtc.Value.ToString(invariantCulture) : ""); + helper.Writer.Write("ShippingMethod", (string)order.ShippingMethod); + helper.Writer.Write("ShippingRateComputationMethodSystemName", (string)order.ShippingRateComputationMethodSystemName); + helper.Writer.Write("Deleted", ((bool)order.Deleted).ToString()); + helper.Writer.Write("CreatedOnUtc", ((DateTime)order.CreatedOnUtc).ToString(invariantCulture)); + helper.Writer.Write("UpdatedOnUtc", ((DateTime)order.UpdatedOnUtc).ToString(invariantCulture)); + helper.Writer.Write("RewardPointsRemaining", rewardPointsRemaining.HasValue ? rewardPointsRemaining.Value.ToString() : ""); + helper.Writer.Write("HasNewPaymentNotification", ((bool)order.HasNewPaymentNotification).ToString()); + helper.Writer.Write("OrderStatus", (string)order.OrderStatus); + helper.Writer.Write("PaymentStatus", (string)order.PaymentStatus); + helper.Writer.Write("ShippingStatus", (string)order.ShippingStatus); + + helper.WriteCustomer(order.Customer, "Customer"); + + helper.WriteAddress(order.BillingAddress, "BillingAddress"); + helper.WriteAddress(order.ShippingAddress, "ShippingAddress"); if (store != null) { - writer.WriteStartElement("Store"); - writer.Write("Id", ((int)store.Id).ToString()); - writer.Write("Name", (string)store.Name); - writer.Write("Url", (string)store.Url); - writer.Write("SslEnabled", ((bool)store.SslEnabled).ToString()); - writer.Write("SecureUrl", (string)store.SecureUrl); - writer.Write("Hosts", (string)store.Hosts); - writer.Write("LogoPictureId", ((int)store.LogoPictureId).ToString()); - writer.Write("DisplayOrder", ((int)store.DisplayOrder).ToString()); - writer.Write("HtmlBodyId", (string)store.HtmlBodyId); - writer.Write("ContentDeliveryNetwork", (string)store.ContentDeliveryNetwork); - writer.Write("PrimaryStoreCurrencyId", ((int)store.PrimaryStoreCurrencyId).ToString()); - writer.Write("PrimaryExchangeRateCurrencyId", ((int)store.PrimaryExchangeRateCurrencyId).ToString()); - - xmlHelper.WriteCurrency(store.PrimaryStoreCurrency, "PrimaryStoreCurrency"); - xmlHelper.WriteCurrency(store.PrimaryExchangeRateCurrency, "PrimaryExchangeRateCurrency"); - - writer.WriteEndElement(); // Store + helper.Writer.WriteStartElement("Store"); + helper.Writer.Write("Id", ((int)store.Id).ToString()); + helper.Writer.Write("Name", (string)store.Name); + helper.Writer.Write("Url", (string)store.Url); + helper.Writer.Write("SslEnabled", ((bool)store.SslEnabled).ToString()); + helper.Writer.Write("SecureUrl", (string)store.SecureUrl); + helper.Writer.Write("Hosts", (string)store.Hosts); + helper.Writer.Write("LogoPictureId", ((int)store.LogoPictureId).ToString()); + helper.Writer.Write("DisplayOrder", ((int)store.DisplayOrder).ToString()); + helper.Writer.Write("HtmlBodyId", (string)store.HtmlBodyId); + helper.Writer.Write("ContentDeliveryNetwork", (string)store.ContentDeliveryNetwork); + helper.Writer.Write("PrimaryStoreCurrencyId", ((int)store.PrimaryStoreCurrencyId).ToString()); + helper.Writer.Write("PrimaryExchangeRateCurrencyId", ((int)store.PrimaryExchangeRateCurrencyId).ToString()); + + helper.WriteCurrency(store.PrimaryStoreCurrency, "PrimaryStoreCurrency"); + helper.WriteCurrency(store.PrimaryExchangeRateCurrency, "PrimaryExchangeRateCurrency"); + + helper.Writer.WriteEndElement(); // Store } - writer.WriteStartElement("OrderItems"); + helper.Writer.WriteStartElement("OrderItems"); foreach (dynamic orderItem in order.OrderItems) { int? licenseDownloadId = orderItem.LicenseDownloadId; decimal? itemWeight = orderItem.ItemWeight; - writer.WriteStartElement("OrderItem"); - writer.Write("Id", ((int)orderItem.Id).ToString()); - writer.Write("OrderItemGuid", ((Guid)orderItem.OrderItemGuid).ToString()); - writer.Write("OrderId", ((int)orderItem.OrderId).ToString()); - writer.Write("ProductId", ((int)orderItem.ProductId).ToString()); - writer.Write("Quantity", ((int)orderItem.Quantity).ToString()); - writer.Write("UnitPriceInclTax", ((decimal)orderItem.UnitPriceInclTax).ToString(invariantCulture)); - writer.Write("UnitPriceExclTax", ((decimal)orderItem.UnitPriceExclTax).ToString(invariantCulture)); - writer.Write("PriceInclTax", ((decimal)orderItem.PriceInclTax).ToString(invariantCulture)); - writer.Write("PriceExclTax", ((decimal)orderItem.PriceExclTax).ToString(invariantCulture)); - writer.Write("TaxRate", ((decimal)orderItem.TaxRate).ToString(invariantCulture)); - writer.Write("DiscountAmountInclTax", ((decimal)orderItem.DiscountAmountInclTax).ToString(invariantCulture)); - writer.Write("DiscountAmountExclTax", ((decimal)orderItem.DiscountAmountExclTax).ToString(invariantCulture)); - writer.Write("AttributeDescription", (string)orderItem.AttributeDescription); - writer.Write("AttributesXml", (string)orderItem.AttributesXml); - writer.Write("DownloadCount", ((int)orderItem.DownloadCount).ToString()); - writer.Write("IsDownloadActivated", ((bool)orderItem.IsDownloadActivated).ToString()); - writer.Write("LicenseDownloadId", licenseDownloadId.HasValue ? licenseDownloadId.Value.ToString() : ""); - writer.Write("ItemWeight", itemWeight.HasValue ? itemWeight.Value.ToString(invariantCulture) : ""); - writer.Write("BundleData", (string)orderItem.BundleData); - writer.Write("ProductCost", ((decimal)orderItem.ProductCost).ToString(invariantCulture)); - - xmlHelper.WriteProduct(orderItem.Product, "Product"); - - writer.WriteEndElement(); // OrderItem + helper.Writer.WriteStartElement("OrderItem"); + helper.Writer.Write("Id", ((int)orderItem.Id).ToString()); + helper.Writer.Write("OrderItemGuid", ((Guid)orderItem.OrderItemGuid).ToString()); + helper.Writer.Write("OrderId", ((int)orderItem.OrderId).ToString()); + helper.Writer.Write("ProductId", ((int)orderItem.ProductId).ToString()); + helper.Writer.Write("Quantity", ((int)orderItem.Quantity).ToString()); + helper.Writer.Write("UnitPriceInclTax", ((decimal)orderItem.UnitPriceInclTax).ToString(invariantCulture)); + helper.Writer.Write("UnitPriceExclTax", ((decimal)orderItem.UnitPriceExclTax).ToString(invariantCulture)); + helper.Writer.Write("PriceInclTax", ((decimal)orderItem.PriceInclTax).ToString(invariantCulture)); + helper.Writer.Write("PriceExclTax", ((decimal)orderItem.PriceExclTax).ToString(invariantCulture)); + helper.Writer.Write("TaxRate", ((decimal)orderItem.TaxRate).ToString(invariantCulture)); + helper.Writer.Write("DiscountAmountInclTax", ((decimal)orderItem.DiscountAmountInclTax).ToString(invariantCulture)); + helper.Writer.Write("DiscountAmountExclTax", ((decimal)orderItem.DiscountAmountExclTax).ToString(invariantCulture)); + helper.Writer.Write("AttributeDescription", (string)orderItem.AttributeDescription); + helper.Writer.Write("AttributesXml", (string)orderItem.AttributesXml); + helper.Writer.Write("DownloadCount", ((int)orderItem.DownloadCount).ToString()); + helper.Writer.Write("IsDownloadActivated", ((bool)orderItem.IsDownloadActivated).ToString()); + helper.Writer.Write("LicenseDownloadId", licenseDownloadId.HasValue ? licenseDownloadId.Value.ToString() : ""); + helper.Writer.Write("ItemWeight", itemWeight.HasValue ? itemWeight.Value.ToString(invariantCulture) : ""); + helper.Writer.Write("BundleData", (string)orderItem.BundleData); + helper.Writer.Write("ProductCost", ((decimal)orderItem.ProductCost).ToString(invariantCulture)); + + helper.WriteProduct(orderItem.Product, "Product"); + + helper.Writer.WriteEndElement(); // OrderItem } - writer.WriteEndElement(); // OrderItems + helper.Writer.WriteEndElement(); // OrderItems - writer.WriteStartElement("Shipments"); + helper.Writer.WriteStartElement("Shipments"); foreach (dynamic shipment in order.Shipments) { decimal? totalWeight = shipment.TotalWeight; DateTime? shippedDateUtc = shipment.ShippedDateUtc; DateTime? deliveryDateUtc = shipment.DeliveryDateUtc; - writer.WriteStartElement("Shipment"); - writer.Write("Id", ((int)shipment.Id).ToString()); - writer.Write("OrderId", ((int)shipment.OrderId).ToString()); - writer.Write("TrackingNumber", (string)shipment.TrackingNumber); - writer.Write("TotalWeight", totalWeight.HasValue ? totalWeight.Value.ToString(invariantCulture) : ""); - writer.Write("ShippedDateUtc", shippedDateUtc.HasValue ? shippedDateUtc.Value.ToString(invariantCulture) : ""); - writer.Write("DeliveryDateUtc", deliveryDateUtc.HasValue ? deliveryDateUtc.Value.ToString(invariantCulture) : ""); - writer.Write("CreatedOnUtc", ((DateTime)shipment.CreatedOnUtc).ToString(invariantCulture)); + helper.Writer.WriteStartElement("Shipment"); + helper.Writer.Write("Id", ((int)shipment.Id).ToString()); + helper.Writer.Write("OrderId", ((int)shipment.OrderId).ToString()); + helper.Writer.Write("TrackingNumber", (string)shipment.TrackingNumber); + helper.Writer.Write("TotalWeight", totalWeight.HasValue ? totalWeight.Value.ToString(invariantCulture) : ""); + helper.Writer.Write("ShippedDateUtc", shippedDateUtc.HasValue ? shippedDateUtc.Value.ToString(invariantCulture) : ""); + helper.Writer.Write("DeliveryDateUtc", deliveryDateUtc.HasValue ? deliveryDateUtc.Value.ToString(invariantCulture) : ""); + helper.Writer.Write("CreatedOnUtc", ((DateTime)shipment.CreatedOnUtc).ToString(invariantCulture)); - writer.WriteStartElement("ShipmentItems"); + helper.Writer.WriteStartElement("ShipmentItems"); foreach (dynamic shipmentItem in shipment.ShipmentItems) { - writer.WriteStartElement("ShipmentItem"); - writer.Write("Id", ((int)shipmentItem.Id).ToString()); - writer.Write("ShipmentId", ((int)shipmentItem.ShipmentId).ToString()); - writer.Write("OrderItemId", ((int)shipmentItem.OrderItemId).ToString()); - writer.Write("Quantity", ((int)shipmentItem.Quantity).ToString()); - writer.WriteEndElement(); // ShipmentItem + helper.Writer.WriteStartElement("ShipmentItem"); + helper.Writer.Write("Id", ((int)shipmentItem.Id).ToString()); + helper.Writer.Write("ShipmentId", ((int)shipmentItem.ShipmentId).ToString()); + helper.Writer.Write("OrderItemId", ((int)shipmentItem.OrderItemId).ToString()); + helper.Writer.Write("Quantity", ((int)shipmentItem.Quantity).ToString()); + helper.Writer.WriteEndElement(); // ShipmentItem } - writer.WriteEndElement(); // ShipmentItems + helper.Writer.WriteEndElement(); // ShipmentItems - writer.WriteEndElement(); // Shipment + helper.Writer.WriteEndElement(); // Shipment } - writer.WriteEndElement(); // Shipments + helper.Writer.WriteEndElement(); // Shipments ++context.RecordsSucceeded; } @@ -247,12 +235,12 @@ public override void Execute(IExportExecuteContext context) context.RecordException(exc, (int)order.Id); } - writer.WriteEndElement(); // Order + helper.Writer.WriteEndElement(); // Order } } - writer.WriteEndElement(); // Orders - writer.WriteEndDocument(); + helper.Writer.WriteEndElement(); // Orders + helper.Writer.WriteEndDocument(); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs index e4154f4804..a3480bdb3a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs @@ -1,8 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Text; -using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -35,26 +32,16 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - var path = context.FilePath; context.Log.Information("Creating file " + path); using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var helper = new ExportXmlHelper(stream)) { - var xmlHelper = new ExportXmlHelper(writer, CultureInfo.InvariantCulture); - - writer.WriteStartDocument(); - writer.WriteStartElement("Products"); - writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); + helper.Writer.WriteStartDocument(); + helper.Writer.WriteStartElement("Products"); + helper.Writer.WriteAttributeString("Version", SmartStoreVersion.CurrentVersion); while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { @@ -67,7 +54,7 @@ public override void Execute(IExportExecuteContext context) try { - xmlHelper.WriteProduct(product, "Product"); + helper.WriteProduct(product, "Product"); ++context.RecordsSucceeded; } @@ -78,8 +65,8 @@ public override void Execute(IExportExecuteContext context) } } - writer.WriteEndElement(); // Products - writer.WriteEndDocument(); + helper.Writer.WriteEndElement(); // Products + helper.Writer.WriteEndDocument(); } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 984f3641a1..29c4c040b9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Net.Mime; +using System.Text; using System.Web; using System.Web.Mvc; using Autofac; @@ -878,7 +879,7 @@ public ActionResult DownloadLogFile(int id) var path = profile.GetExportLogFilePath(); var stream = new FileStream(path, FileMode.Open); - var result = new FileStreamResult(stream, MediaTypeNames.Text.Plain); + var result = new FileStreamResult(stream, "text/plain; charset=utf-8"); result.FileDownloadName = profile.Name.ToValidFileName() + "-log.txt"; return result; From 451dd5e9460a20fc8e9e010bcf9bce4a510748a2 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 8 Nov 2015 18:58:53 +0100 Subject: [PATCH 044/732] Export framework rather than provider should create export data files --- .../Extensions/XmlWriterExtensions.cs | 10 -- .../DataExchange/ExportExecuteContext.cs | 54 ++++++-- .../ExportTask/ExportProfileTask.cs | 122 +++++++++++++----- .../DataExchange/ExportXmlHelper.cs | 16 ++- .../Providers/CategoryXmlExportProvider.cs | 9 +- .../Providers/CustomerXlsxExportProvider.cs | 8 +- .../Providers/CustomerXmlExportProvider.cs | 9 +- .../ManufacturerXmlExportProvider.cs | 9 +- .../Providers/OrderXlsxExportProvider.cs | 9 +- .../Providers/OrderXmlExportProvider.cs | 8 +- .../Providers/ProductXlsxExportProvider.cs | 9 +- .../Providers/ProductXmlExportProvider.cs | 9 +- .../Providers/SubscriberCsvExportProvider.cs | 8 +- .../Providers/GmcXmlExportProvider.cs | 14 +- 14 files changed, 153 insertions(+), 141 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs index 5ead943146..ca4e1c2c71 100644 --- a/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs @@ -21,16 +21,6 @@ public static void WriteCData(this XmlWriter writer, string name, string value, } } - public static void WriteNode(this XmlWriter writer, string name, Action content) - { - if (name.HasValue() && content != null) - { - writer.WriteStartElement(name); - content(); - writer.WriteEndElement(); - } - } - /// /// Created a simple or CData node element /// diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs index 17ae53a8bf..d4a6286570 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExecuteContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; @@ -50,14 +51,25 @@ public interface IExportExecuteContext /// - /// The maximum allowed file name length + /// Identifier of current data stream. Can be null. /// - int MaxFileNameLength { get; } + string DataStreamId { get; set; } /// - /// The path of the export content folder + /// Stream used to write data to /// - string Folder { get; } + Stream DataStream { get; } + + /// + /// List with extra data streams required by provider + /// + List ExtraDataStreams { get; set; } + + + /// + /// The maximum allowed file name length + /// + int MaxFileNameLength { get; } /// /// The name of the current export file @@ -65,9 +77,9 @@ public interface IExportExecuteContext string FileName { get; } /// - /// The path of the current export file + /// The path of the export content folder /// - string FilePath { get; } + string Folder { get; } /// @@ -114,6 +126,25 @@ public interface IExportExecuteContext } + public class ExportExtraStreams + { + /// + /// Your Id to identify this stream within a list of streams + /// + public string Id { get; set; } + + /// + /// Stream used to write data to + /// + public Stream DataStream { get; internal set; } + + /// + /// The name of the file to be created + /// + public string FileName { get; set; } + } + + public class ExportExecuteContext : IExportExecuteContext { private DataExportResult _result; @@ -125,7 +156,7 @@ internal ExportExecuteContext(DataExportResult result, CancellationToken cancell _result = result; _cancellation = cancellation; Folder = folder; - + ExtraDataStreams = new List(); CustomProperties = new Dictionary(); } @@ -159,12 +190,13 @@ public bool IsMaxFailures get { return RecordsFailed > 11; } } - public int MaxFileNameLength { get; internal set; } + public string DataStreamId { get; set; } + public Stream DataStream { get; internal set; } + public List ExtraDataStreams { get; set; } - public string Folder { get; private set; } + public int MaxFileNameLength { get; internal set; } public string FileName { get; internal set; } - public string FileExtension { get; internal set; } - public string FilePath { get; internal set; } + public string Folder { get; private set; } public bool HasPublicDeployment { get; internal set; } public string PublicFolderPath { get; internal set; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 1a52e264ea..8218acffb0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -133,7 +133,7 @@ private void InitDependencies(TaskExecutionContext context) _genericAttributeService = context.Resolve>(); _subscriptionRepository = context.Resolve>>(); - T = NullLocalizer.Instance; + T = NullLocalizer.Instance; // TODO: resolve } public Localizer T { get; set; } @@ -316,6 +316,61 @@ private IExportDataSegmenterProvider CreateSegmenter(ExportProfileTaskContext ct return ctx.ExecuteContext.Segmenter as IExportDataSegmenterProvider; } + private bool CallProvider(ExportProfileTaskContext ctx, string streamId, string method, string path) + { + if (method != "Execute" && method != "OnExecuted") + throw new SmartException("Unknown export method {0}".FormatInvariant(method.NaIfEmpty())); + + try + { + ctx.ExecuteContext.DataStreamId = streamId; + + using (ctx.ExecuteContext.DataStream = new MemoryStream()) + { + if (method == "Execute") + { + ctx.Provider.Value.Execute(ctx.ExecuteContext); + } + else if (method == "OnExecuted") + { + ctx.Provider.Value.OnExecuted(ctx.ExecuteContext); + } + + if (ctx.IsFileBasedExport && path.HasValue()) + { + if (!ctx.ExecuteContext.DataStream.CanSeek) + ctx.Log.Warning("Data stream seems to be closed!"); + + ctx.ExecuteContext.DataStream.Seek(0, SeekOrigin.Begin); + + using (_rwLock.GetWriteLock()) + using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + ctx.Log.Information("Creating file " + path); + + ctx.ExecuteContext.DataStream.CopyTo(fileStream); + } + } + } + } + catch (Exception exc) + { + ctx.ExecuteContext.Abort = ExportAbortion.Hard; + ctx.Log.Error("The provider failed at the {0} method: {1}".FormatInvariant(method, exc.ToAllMessages()), exc); + ctx.Result.LastError = exc.ToString(); + } + finally + { + if (ctx.ExecuteContext.DataStream != null) + { + ctx.ExecuteContext.DataStream.Dispose(); + ctx.ExecuteContext.DataStream = null; + } + } + + return (ctx.ExecuteContext.Abort != ExportAbortion.Hard); + } + private void PrepareProductDescription(ExportProfileTaskContext ctx, dynamic expando, Product product) { try @@ -2465,12 +2520,12 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) ctx.ExecuteContext.MaxFileNameLength = _dataExchangeSettings.Value.MaxFileNameLength; - ctx.ExecuteContext.FileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); - ctx.ExecuteContext.HasPublicDeployment = ctx.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); ctx.ExecuteContext.PublicFolderPath = (ctx.ExecuteContext.HasPublicDeployment ? Path.Combine(HttpRuntime.AppDomainAppPath, PublicFolder) : null); + var fileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); + using (var segmenter = CreateSegmenter(ctx)) { @@ -2489,42 +2544,32 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) segmenter.RecordPerSegmentCount = 0; ctx.ExecuteContext.RecordsSucceeded = 0; - try + string path = null; + + if (ctx.IsFileBasedExport) { - if (ctx.IsFileBasedExport) - { - var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); + var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); - ctx.ExecuteContext.FileName = resolvedPattern + ctx.ExecuteContext.FileExtension; - ctx.ExecuteContext.FilePath = Path.Combine(ctx.ExecuteContext.Folder, ctx.ExecuteContext.FileName); + ctx.ExecuteContext.FileName = resolvedPattern + fileExtension; + path = Path.Combine(ctx.ExecuteContext.Folder, ctx.ExecuteContext.FileName); - if (ctx.ExecuteContext.HasPublicDeployment) - ctx.ExecuteContext.PublicFileUrl = ctx.Store.Url.EnsureEndsWith("/") + PublicFolder.EnsureEndsWith("/") + ctx.ExecuteContext.FileName; + if (ctx.ExecuteContext.HasPublicDeployment) + ctx.ExecuteContext.PublicFileUrl = ctx.Store.Url.EnsureEndsWith("/") + PublicFolder.EnsureEndsWith("/") + ctx.ExecuteContext.FileName; + } - ctx.Provider.Value.Execute(ctx.ExecuteContext); + if (CallProvider(ctx, null, "Execute", path)) + { + ctx.Log.Information("Provider reports {0} successful exported record(s)".FormatInvariant(ctx.ExecuteContext.RecordsSucceeded)); - // create info for deployment list in profile edit - if (File.Exists(ctx.ExecuteContext.FilePath)) - { - ctx.Result.Files.Add(new DataExportResult.ExportFileInfo - { - StoreId = ctx.Store.Id, - FileName = ctx.ExecuteContext.FileName - }); - } - } - else + // create info for deployment list in profile edit + if (ctx.IsFileBasedExport) { - ctx.Provider.Value.Execute(ctx.ExecuteContext); + ctx.Result.Files.Add(new DataExportResult.ExportFileInfo + { + StoreId = ctx.Store.Id, + FileName = ctx.ExecuteContext.FileName + }); } - - ctx.Log.Information("Provider reports {0} successful exported record(s)".FormatInvariant(ctx.ExecuteContext.RecordsSucceeded)); - } - catch (Exception exc) - { - ctx.ExecuteContext.Abort = ExportAbortion.Hard; - ctx.Log.Error("The provider failed to execute the export: " + exc.ToAllMessages(), exc); - ctx.Result.LastError = exc.ToString(); } if (ctx.ExecuteContext.IsMaxFailures) @@ -2536,7 +2581,17 @@ private void ExportCoreInner(ExportProfileTaskContext ctx, Store store) if (ctx.ExecuteContext.Abort != ExportAbortion.Hard) { - ctx.Provider.Value.OnExecuted(ctx.ExecuteContext); + if (ctx.ExecuteContext.ExtraDataStreams.Count == 0) + ctx.ExecuteContext.ExtraDataStreams.Add(new ExportExtraStreams()); + + ctx.ExecuteContext.ExtraDataStreams.ForEach(x => + { + var path = (x.FileName.HasValue() ? Path.Combine(ctx.ExecuteContext.Folder, x.FileName) : null); + + CallProvider(ctx, x.Id, "OnExecuted", path); + }); + + ctx.ExecuteContext.ExtraDataStreams.Clear(); } } } @@ -2550,7 +2605,6 @@ private void ExportCoreOuter(ExportProfileTaskContext ctx) FileSystemHelper.ClearDirectory(ctx.FolderContent, false); FileSystemHelper.Delete(ctx.ZipPath); - using (_rwLock.GetWriteLock()) using (var logger = new TraceLogger(ctx.LogPath)) { try diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs index e5837578d6..d58c724eac 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportXmlHelper.cs @@ -24,7 +24,18 @@ public ExportXmlHelper(Stream stream, XmlWriterSettings settings = null, Culture { if (settings == null) { - settings = new XmlWriterSettings + settings = DefaultSettings; + } + + _writer = XmlWriter.Create(stream, settings); + _culture = (culture == null ? CultureInfo.InvariantCulture : culture); + } + + public static XmlWriterSettings DefaultSettings + { + get + { + return new XmlWriterSettings { Encoding = Encoding.UTF8, CheckCharacters = false, @@ -32,9 +43,6 @@ public ExportXmlHelper(Stream stream, XmlWriterSettings settings = null, Culture IndentChars = "\t" }; } - - _writer = XmlWriter.Create(stream, settings); - _culture = (culture == null ? CultureInfo.InvariantCulture : culture); } public XmlWriter Writer diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs index 2b74231730..1aecac8667 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -32,12 +30,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var helper = new ExportXmlHelper(stream)) + using (var helper = new ExportXmlHelper(context.DataStream)) { helper.Writer.WriteStartDocument(); helper.Writer.WriteStartElement("Categories"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs index 4ab09bd5c5..72bda50969 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs @@ -1,12 +1,10 @@ using System; using System.Drawing; using System.Globalization; -using System.IO; using OfficeOpenXml; using OfficeOpenXml.Style; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services.Customers; @@ -110,12 +108,8 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { var invariantCulture = CultureInfo.InvariantCulture; - var path = context.FilePath; - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var xlPackage = new ExcelPackage(stream)) + using (var xlPackage = new ExcelPackage(context.DataStream)) { // uncomment this line if you want the XML written out to the outputDir //xlPackage.DebugMode = true; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs index 4103b412a5..4963380787 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -32,12 +30,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var helper = new ExportXmlHelper(stream)) + using (var helper = new ExportXmlHelper(context.DataStream)) { helper.Writer.WriteStartDocument(); helper.Writer.WriteStartElement("Customers"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs index 3c5d6afa53..af672aeed8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -32,12 +30,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var helper = new ExportXmlHelper(stream)) + using (var helper = new ExportXmlHelper(context.DataStream)) { helper.Writer.WriteStartDocument(); helper.Writer.WriteStartElement("Manufacturers"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs index ae6ce4271d..0520409914 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs @@ -1,10 +1,8 @@ using System; using System.Drawing; -using System.IO; using OfficeOpenXml; using OfficeOpenXml.Style; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -108,12 +106,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var xlPackage = new ExcelPackage(stream)) + using (var xlPackage = new ExcelPackage(context.DataStream)) { // uncomment this line if you want the XML written out to the outputDir //xlPackage.DebugMode = true; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs index 470155603a..366dcd15d0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs @@ -1,9 +1,7 @@ using System; using System.Globalization; -using System.IO; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -33,13 +31,9 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; var invariantCulture = CultureInfo.InvariantCulture; - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var helper = new ExportXmlHelper(stream)) + using (var helper = new ExportXmlHelper(context.DataStream)) { helper.Writer.WriteStartDocument(); helper.Writer.WriteStartElement("Orders"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs index 3da13558c0..d2e877b7f8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.IO; using System.Linq; using OfficeOpenXml; using OfficeOpenXml.Style; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services.Catalog; using SmartStore.Services.Localization; @@ -177,12 +175,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var xlPackage = new ExcelPackage(stream)) + using (var xlPackage = new ExcelPackage(context.DataStream)) { // uncomment this line if you want the XML written out to the outputDir //xlPackage.DebugMode = true; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs index a3480bdb3a..63d5dc4122 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -32,12 +30,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var helper = new ExportXmlHelper(stream)) + using (var helper = new ExportXmlHelper(context.DataStream)) { helper.Writer.WriteStartDocument(); helper.Writer.WriteStartElement("Products"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs index 2a7e299b51..be904fa6c7 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs @@ -2,7 +2,6 @@ using System.IO; using System.Text; using SmartStore.Core.Domain.DataExchange; -using SmartStore.Core.Logging; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange.Providers @@ -32,12 +31,7 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var path = context.FilePath; - - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = new StreamWriter(stream, Encoding.UTF8)) + using (var writer = new StreamWriter(context.DataStream, Encoding.UTF8, 1024, true)) { while (context.Abort == ExportAbortion.None && context.Segmenter.ReadNextSegment()) { diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index b1a68d4591..a58b4b98b8 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -1,7 +1,5 @@ using System; -using System.IO; using System.Linq; -using System.Text; using System.Xml; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.DataExchange; @@ -157,13 +155,6 @@ public override string FileExtension public override void Execute(IExportExecuteContext context) { - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false - }; - - var path = context.FilePath; dynamic currency = context.Currency; string measureWeightSystemKey = ""; @@ -174,10 +165,7 @@ public override void Execute(IExportExecuteContext context) var config = (context.ConfigurationData as ProfileConfigurationModel) ?? new ProfileConfigurationModel(); - context.Log.Information("Creating file " + path); - - using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - using (var writer = XmlWriter.Create(stream, settings)) + using (var writer = XmlWriter.Create(context.DataStream, ExportXmlHelper.DefaultSettings)) { writer.WriteStartDocument(); writer.WriteStartElement("rss"); From 738052a5be9af329f02181d7d2d9fdbd3839ce76 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 8 Nov 2015 19:47:39 +0100 Subject: [PATCH 045/732] ExportFeature becomes an enum of flags --- .../Domain/DataExchange/ExportEnums.cs | 32 +++++++++++-------- .../Plugins/Providers/ProviderMetadata.cs | 4 +-- .../DataExchange/ExportExtensions.cs | 16 ---------- .../DataExchange/ExportFeaturesAttribute.cs | 7 +--- .../DataExchange/ExportProfileService.cs | 4 +-- .../ExportTask/ExportProfileTask.cs | 18 +++++------ .../ExportTask/ExportProfileTaskContext.cs | 9 ++---- .../Providers/GmcXmlExportProvider.cs | 18 +++++------ .../DependencyRegistrar.cs | 8 ++--- .../Controllers/ExportController.cs | 2 +- .../Models/DataExchange/ExportProfileModel.cs | 2 +- .../Views/Export/_Tab.Projection.cshtml | 14 ++++---- 12 files changed, 56 insertions(+), 78 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs index b7f041fed5..90cb107353 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs @@ -1,4 +1,5 @@  +using System; namespace SmartStore.Core.Domain.DataExchange { /// @@ -59,64 +60,67 @@ public enum ExportAttributeValueMerging } /// - /// Controls data processing and projection items supported by export provider + /// Controls data processing and projection items supported by an export provider /// - public enum ExportFeatures + [Flags] + public enum ExportFeature { + None = 0, + /// /// Whether to automatically create a file based public deployment when an export profile is created /// - CreatesInitialPublicDeployment = 0, + CreatesInitialPublicDeployment = 1, /// /// Whether to offer option to include\exclude grouped products /// - CanOmitGroupedProducts, + CanOmitGroupedProducts = 1 << 2, /// /// Whether to offer option to export attribute combinations as products /// - CanProjectAttributeCombinations, + CanProjectAttributeCombinations = 1 << 3, /// /// Whether to offer further options to manipulate the product description /// - CanProjectDescription, + CanProjectDescription = 1 << 4, /// /// Whether to offer option to enter a brand fallback /// - OffersBrandFallback, + OffersBrandFallback = 1 << 5, /// /// Whether to offer option to set a picture size and to get the URL of the main image /// - CanIncludeMainPicture, + CanIncludeMainPicture = 1 << 6, /// /// Whether to use SKU as manufacturer part number if MPN is empty /// - UsesSkuAsMpnFallback, - + UsesSkuAsMpnFallback = 1 << 7, + /// /// Whether to offer option to enter a shipping time fallback /// - OffersShippingTimeFallback, + OffersShippingTimeFallback = 1 << 8, /// /// Whether to offer option to enter a shipping costs fallback and a free shipping threshold /// - OffersShippingCostsFallback, + OffersShippingCostsFallback = 1 << 9, /// /// Whether to get the calculated old product price /// - UsesOldPrice, + UsesOldPrice = 1 << 10, /// /// Whether to get the calculated special and regular price (ignoring special offers) /// - UsesSpecialPrice + UsesSpecialPrice = 1 << 11 } /// diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs index 31d98942d4..05ef601ecc 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs @@ -74,9 +74,9 @@ public class ProviderMetadata public bool IsHidden { get; set; } /// - /// Gets or sets an array of values that reflects what export data processing is supported by a provider + /// Gets or sets flags that reflects what features of export data processing is supported by a provider /// - public ExportFeatures[] ExportSupport { get; set; } + public ExportFeature ExportFeature { get; set; } /// /// Gets or sets an array of widget system names, which depend on the current provider diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index ae5b8ff6ce..a7a8e5c5b9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -1,10 +1,8 @@ using System; using System.IO; -using System.Linq; using System.Web; using System.Web.Caching; using SmartStore.Core.Domain; -using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Plugins; using SmartStore.Services.Localization; @@ -24,20 +22,6 @@ public static bool IsValid(this Provider provider) return provider != null; } - /// - /// Returns a value indicating whether the export provider supports a projection type - /// - /// Export provider - /// The feature to check - /// true provider supports type, false provider does not support type. - public static bool Supports(this Provider provider, ExportFeatures feature) - { - if (provider != null) - return provider.Metadata.ExportSupport.Contains(feature); - - return false; - } - /// /// Gets the localized friendly name or the system name as fallback /// diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs index d29f5641d3..7f803715f4 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportFeaturesAttribute.cs @@ -10,11 +10,6 @@ namespace SmartStore.Services.DataExchange [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class ExportFeaturesAttribute : Attribute { - public ExportFeaturesAttribute(params ExportFeatures[] features) - { - Features = features; - } - - public ExportFeatures[] Features { get; set; } + public ExportFeature Features { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index dac3287770..95302de285 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -116,7 +116,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi RemoveCriticalCharacters = true, CriticalCharacters = "¼,½,¾", PriceType = PriceDisplayType.PreSelectedPrice, - NoGroupedProducts = (provider.Supports(ExportFeatures.CanOmitGroupedProducts) ? true : false) + NoGroupedProducts = (provider.Metadata.ExportFeature.HasFlag(ExportFeature.CanOmitGroupedProducts) ? true : false) }; var filter = new ExportFilter @@ -150,7 +150,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi { if (cloneProfile == null) { - if (provider.Supports(ExportFeatures.CreatesInitialPublicDeployment)) + if (provider.Metadata.ExportFeature.HasFlag(ExportFeature.CreatesInitialPublicDeployment)) { profile.Deployments.Add(new ExportDeployment { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 8218acffb0..b796715059 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -1539,7 +1539,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro var productTemplate = ctx.ProductTemplates.FirstOrDefault(x => x.Key == product.ProductTemplateId); var pictureSize = _mediaSettings.Value.ProductDetailsPictureSize; - if (ctx.SupportedFeatures[ExportFeatures.CanIncludeMainPicture] && ctx.Projection.PictureSize > 0) + if (ctx.Supports(ExportFeature.CanIncludeMainPicture) && ctx.Projection.PictureSize > 0) pictureSize = ctx.Projection.PictureSize; var perfLoadId = (ctx.IsPreview ? 0 : product.Id); // perf preview (it's a compromise) @@ -1745,12 +1745,12 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro #region more attribute controlled data - if (ctx.Supports(ExportFeatures.CanProjectDescription)) + if (ctx.Supports(ExportFeature.CanProjectDescription)) { PrepareProductDescription(ctx, expando, product); } - if (ctx.Supports(ExportFeatures.OffersBrandFallback)) + if (ctx.Supports(ExportFeature.OffersBrandFallback)) { string brand = null; var productManus = ctx.ProductExportContext.ProductManufacturers.Load(perfLoadId); @@ -1764,7 +1764,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro expando._Brand = brand; } - if (ctx.Supports(ExportFeatures.CanIncludeMainPicture)) + if (ctx.Supports(ExportFeature.CanIncludeMainPicture)) { if (productPictures != null && productPictures.Any()) expando._MainPictureUrl = _pictureService.Value.GetPictureUrl(productPictures.First().Picture, ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); @@ -1806,18 +1806,18 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro // navigation properties GetDeliveryTimeAndQuantityUnit(ctx, exp, product.DeliveryTimeId, product.QuantityUnitId); - if (ctx.Supports(ExportFeatures.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty()) + if (ctx.Supports(ExportFeature.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty()) { exp.ManufacturerPartNumber = product.Sku; } - if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback)) + if (ctx.Supports(ExportFeature.OffersShippingTimeFallback)) { dynamic deliveryTime = exp.DeliveryTime; exp._ShippingTime = (deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name); } - if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback)) + if (ctx.Supports(ExportFeature.OffersShippingCostsFallback)) { exp._FreeShippingThreshold = ctx.Projection.FreeShippingThreshold; @@ -1827,7 +1827,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro exp._ShippingCosts = ctx.Projection.ShippingCosts; } - if (ctx.Supports(ExportFeatures.UsesOldPrice)) + if (ctx.Supports(ExportFeature.UsesOldPrice)) { if (product.OldPrice != decimal.Zero && product.OldPrice != (decimal)exp.Price && !(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) { @@ -1847,7 +1847,7 @@ private List ConvertToExpando(ExportProfileTaskContext ctx, Product pro } } - if (ctx.Supports(ExportFeatures.UsesSpecialPrice)) + if (ctx.Supports(ExportFeature.UsesSpecialPrice)) { exp._SpecialPrice = null; exp._RegularPrice = null; // price if a special price would not exist diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs index 8c1286b5d6..ae14972e42 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTaskContext.cs @@ -41,10 +41,6 @@ public ExportProfileTaskContext( EntityIdsSelected = selectedIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); PreviewData = previewData; - SupportedFeatures = Enum.GetValues(typeof(ExportFeatures)) - .Cast() - .ToDictionary(x => x, x => Provider.Supports(x)); - FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(profile.FolderName)); FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; @@ -84,10 +80,9 @@ public bool IsPreview public ExportProfile Profile { get; private set; } public Provider Provider { get; private set; } - public Dictionary SupportedFeatures { get; private set; } - public bool Supports(ExportFeatures feature) + public bool Supports(ExportFeature feature) { - return (!IsPreview && SupportedFeatures[feature]); + return (!IsPreview && Provider.Metadata.ExportFeature.HasFlag(feature)); } public ExportFilter Filter { get; private set; } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index a58b4b98b8..852f8eb430 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -17,15 +17,15 @@ namespace SmartStore.GoogleMerchantCenter.Providers [SystemName("Feeds.GoogleMerchantCenterProductXml")] [FriendlyName("Google Merchant Center XML product feed")] [DisplayOrder(1)] - [ExportFeatures( - ExportFeatures.CreatesInitialPublicDeployment, - ExportFeatures.CanOmitGroupedProducts, - ExportFeatures.CanProjectAttributeCombinations, - ExportFeatures.CanProjectDescription, - ExportFeatures.UsesSkuAsMpnFallback, - ExportFeatures.OffersBrandFallback, - ExportFeatures.CanIncludeMainPicture, - ExportFeatures.UsesSpecialPrice)] + [ExportFeatures(Features = + ExportFeature.CreatesInitialPublicDeployment | + ExportFeature.CanOmitGroupedProducts | + ExportFeature.CanProjectAttributeCombinations | + ExportFeature.CanProjectDescription | + ExportFeature.UsesSkuAsMpnFallback | + ExportFeature.OffersBrandFallback | + ExportFeature.CanIncludeMainPicture | + ExportFeature.UsesSpecialPrice)] public class GmcXmlExportProvider : ExportProviderBase { private const string _googleNamespace = "http://base.google.com/ns/1.0"; diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 1daa71bd03..2b11924761 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -768,7 +768,7 @@ protected override void Load(ContainerBuilder builder) var isConfigurable = typeof(IConfigurable).IsAssignableFrom(type); var isEditable = typeof(IUserEditable).IsAssignableFrom(type); var isHidden = GetIsHidden(type); - var exportSupport = GetExportFeatures(type); + var exportFeature = GetExportFeature(type); var registration = builder.RegisterType(type).Named(systemName).InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.None); registration.WithMetadata(m => @@ -785,7 +785,7 @@ protected override void Load(ContainerBuilder builder) m.For(em => em.IsConfigurable, isConfigurable); m.For(em => em.IsEditable, isEditable); m.For(em => em.IsHidden, isHidden); - m.For(em => em.ExportSupport, exportSupport); + m.For(em => em.ExportFeature, exportFeature); }); // register specific provider type @@ -863,7 +863,7 @@ private bool GetIsHidden(Type type) return false; } - private ExportFeatures[] GetExportFeatures(Type type) + private ExportFeature GetExportFeature(Type type) { var attr = type.GetAttribute(false); @@ -872,7 +872,7 @@ private ExportFeatures[] GetExportFeatures(Type type) return attr.Features; } - return new ExportFeatures[0]; + return ExportFeature.None; } private Tuple GetFriendlyName(Type type, PluginDescriptor descriptor) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 29c4c040b9..8690b4507c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -279,7 +279,7 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile if (provider != null) { - model.Provider.Supporting = provider.Metadata.ExportSupport; + model.Provider.Feature = provider.Metadata.ExportFeature; if (model.Provider.EntityType == ExportEntityType.Product) { diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index 3ebf4350d9..ee44a44113 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -94,7 +94,7 @@ public class ProviderModel public Type ConfigDataType { get; set; } public object ConfigData { get; set; } - public ExportFeatures[] Supporting { get; set; } + public ExportFeature Feature { get; set; } [SmartResourceDisplayName("Common.Image")] public string ThumbnailUrl { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml index 4815fba951..3d02c0a230 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Projection.cshtml @@ -42,7 +42,7 @@ @if (Model.Provider.EntityType == ExportEntityType.Product) { - if (Model.Provider.Supporting.Contains(ExportFeatures.CanProjectAttributeCombinations)) + if (Model.Provider.Feature.HasFlag(ExportFeature.CanProjectAttributeCombinations)) {
@@ -71,7 +71,7 @@
@@ -110,7 +110,7 @@
@@ -123,7 +123,7 @@
@@ -145,7 +145,7 @@
@@ -158,7 +158,7 @@
@@ -171,7 +171,7 @@
From 6d1e4ad7a7e7f9e1c6cb5723b7c1d292c1e231ad Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 9 Nov 2015 11:01:35 +0100 Subject: [PATCH 046/732] Fixed issues due to incomplete renaming --- .../FeedGoogleMerchantCenter/Configure.cshtml | 2 +- .../ProfileConfiguration.cshtml | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml index 8d01b7baeb..6312648112 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml @@ -22,7 +22,7 @@ @Html.Raw(@T("Plugins.Feed.Froogle.AdminInstruction"))
- @Html.Action("InfoProfile", "Export", new { systemName = ProductExportXmlProvider.SystemName, returnUrl = Request.RawUrl, area = "admin" }) + @Html.Action("InfoProfile", "Export", new { systemName = GmcXmlExportProvider.SystemName, returnUrl = Request.RawUrl, area = "admin" })
diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml index 57b1c7b693..edf7fecb7e 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml @@ -39,9 +39,10 @@ @Html.SmartLabelFor(m => m.Condition)
- @Html.DropDownList("Condition", new List { + @Html.DropDownList("Condition", new List + { new SelectListItem { Text = T("Common.Auto"), Value = "" }, - new SelectListItem { Text = T("Common.Unspecified"), Value = ProductExportXmlProvider.Unspecified }, + new SelectListItem { Text = T("Common.Unspecified"), Value = GmcXmlExportProvider.Unspecified }, new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionNew"), Value = "new" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionUsed"), Value = "used" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionRefurbished"), Value = "refurbished" } @@ -54,9 +55,10 @@ @Html.SmartLabelFor(m => m.Availability) - @Html.DropDownList("Availability", new List { + @Html.DropDownList("Availability", new List + { new SelectListItem { Text = T("Common.Auto"), Value = "" }, - new SelectListItem { Text = T("Common.Unspecified"), Value = ProductExportXmlProvider.Unspecified }, + new SelectListItem { Text = T("Common.Unspecified"), Value = GmcXmlExportProvider.Unspecified }, new SelectListItem { Text = T("Plugins.Feed.Froogle.AvailabilityInStock"), Value = "in stock" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.AvailabilityOutOfStock"), Value = "out of stock" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.AvailabilityPreorder"), Value = "preorder" } @@ -71,7 +73,7 @@ @Html.DropDownList("Gender", new List { new SelectListItem { Text = T("Common.Auto"), Value = "" }, - new SelectListItem { Text = T("Common.Unspecified"), Value = ProductExportXmlProvider.Unspecified }, + new SelectListItem { Text = T("Common.Unspecified"), Value = GmcXmlExportProvider.Unspecified }, new SelectListItem { Text = T("Plugins.Feed.Froogle.GenderMale"), Value = "male" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.GenderFemale"), Value = "female" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.GenderUnisex"), Value = "unisex" } @@ -86,7 +88,7 @@ @Html.DropDownList("AgeGroup", new List { new SelectListItem { Text = T("Common.Auto"), Value = "" }, - new SelectListItem { Text = T("Common.Unspecified"), Value = ProductExportXmlProvider.Unspecified }, + new SelectListItem { Text = T("Common.Unspecified"), Value = GmcXmlExportProvider.Unspecified }, new SelectListItem { Text = T("Plugins.Feed.Froogle.AgeGroupAdult"), Value = "adult" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.AgeGroupKids"), Value = "kids" } }) From d75613c885bf853b4e647b0e3f9ce8cf1aaad01d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 9 Nov 2015 12:50:37 +0100 Subject: [PATCH 047/732] Improved pattern resolving of export file names --- .../201509021536425_ExportFramework1.cs | 4 +- .../201509150931528_ExportFramework2.cs | 5 ++ .../DataExchange/ExportExtensions.cs | 33 ++++++--- .../DataExchange/ExportProfileService.cs | 2 +- .../Controllers/ExportController.cs | 1 + .../Models/DataExchange/ExportProfileModel.cs | 1 + .../Views/Export/_Tab.Deployment.cshtml | 70 ++++++++++++++++++- 7 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs index d8e9b8ab9d..ed09b5e8d3 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs @@ -100,8 +100,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Legt den Namen des Ordners fest, in den die Daten exportiert werden."); builder.AddOrUpdate("Admin.DataExchange.Export.FolderAndFileName.Validate", - "Please enter a valid folder and file name. Example for file names: %Misc.FileNumber%-%ExportProfile.Id%-gmc-%Store.Name%", - "Bitte einen gltigen Ordner- und Dateinamen eingeben. Beispiel fr Dateinamen: %Misc.FileNumber%-%ExportProfile.Id%-gmc-%Store.Name%"); + "Please enter a valid folder and file name. Example for file names: %File.Index%-%Profile.Id%-gmc-%Store.Name%", + "Bitte einen gltigen Ordner- und Dateinamen eingeben. Beispiel fr Dateinamen: %File.Index%-%Profile.Id%-gmc-%Store.Name%"); builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.CreateZip", diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 174f2d1f44..ccc5a52b37 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -41,6 +41,7 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Common.ShowAll", "Show all", "Alle anzeigen"); builder.AddOrUpdate("Admin.Common.Selected", "Selected", "Ausgewhlte"); builder.AddOrUpdate("Admin.Common.Entity", "Entity", "Entitt"); + builder.AddOrUpdate("Admin.Common.Placeholder", "Placeholder", "Platzhalter"); builder.AddOrUpdate("Admin.Common.FilesDeleted", @@ -108,6 +109,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Pro Sprache darf nur ein aktiver SEO Name festgelegt werden."); + builder.AddOrUpdate("Admin.DataExchange.Export.FileNamePatternDescriptions", + "ID of export profil;Folder name of export profil;SEO name of export profil;Store ID;SEO name of store;One based file index;Random number;UTC timestamp", + "ID des Exportprofils;Ordername des Exportprofils;SEO Name des Exportprofils;Shop ID;SEO Name des Shops;Mit 1 beginnender Dateiindex;Zufallszahl;UTC Zeitstempel"); + builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", "This option is not taken into account in the preview.", "Diese Option wird in der Vorschau nicht bercksichtigt."); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index a7a8e5c5b9..8a0cdc959d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -1,5 +1,7 @@ using System; +using System.Globalization; using System.IO; +using System.Text; using System.Web; using System.Web.Caching; using SmartStore.Core.Domain; @@ -64,18 +66,31 @@ public static string GetExportLogFilePath(this ExportProfile profile) /// /// Export profile /// Store - /// File number + /// One based file index /// The maximum length of the file name /// Resolved file name pattern - public static string ResolveFileNamePattern(this ExportProfile profile, Store store, int fileNumber, int maxFileNameLength) + public static string ResolveFileNamePattern(this ExportProfile profile, Store store, int fileIndex, int maxFileNameLength) { - var result = profile.FileNamePattern - .Replace("%ExportProfile.Id%", profile.Id.ToString()) - .Replace("%ExportProfile.SeoName%", SeoHelper.GetSeName(profile.Name, true, false).Replace("/", "").Replace("-", "")) - .Replace("%ExportProfile.FolderName%", profile.FolderName) - .Replace("%Store.Id%", store.Id.ToString()) - .Replace("%Store.SeoName%", profile.PerStore ? SeoHelper.GetSeName(store.Name, true, false) : "allstores") - .Replace("%Misc.FileNumber%", fileNumber.ToString("D4")) + var sb = new StringBuilder(profile.FileNamePattern); + + sb.Replace("%Profile.Id%", profile.Id.ToString()); + sb.Replace("%Profile.FolderName%", profile.FolderName); + sb.Replace("%Store.Id%", store.Id.ToString()); + sb.Replace("%File.Index%", fileIndex.ToString("D4")); + + if (profile.FileNamePattern.Contains("%Profile.SeoName%")) + sb.Replace("%Profile.SeoName%", SeoHelper.GetSeName(profile.Name, true, false).Replace("/", "").Replace("-", "")); + + if (profile.FileNamePattern.Contains("%Store.SeoName%")) + sb.Replace("%Store.SeoName%", profile.PerStore ? SeoHelper.GetSeName(store.Name, true, false) : "allstores"); + + if (profile.FileNamePattern.Contains("%Random.Number%")) + sb.Replace("%Random.Number%", CommonHelper.GenerateRandomInteger().ToString()); + + if (profile.FileNamePattern.Contains("%Timestamp%")) + sb.Replace("%Timestamp%", DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture)); + + var result = sb.ToString() .ToValidFileName("") .Truncate(maxFileNameLength); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index 95302de285..465b97a3c2 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -18,7 +18,7 @@ namespace SmartStore.Services.DataExchange { public partial class ExportProfileService : IExportProfileService { - private const string _defaultFileNamePattern = "%Store.Id%-%ExportProfile.Id%-%Misc.FileNumber%-%ExportProfile.SeoName%"; + private const string _defaultFileNamePattern = "%Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%"; private readonly IRepository _exportProfileRepository; private readonly IRepository _exportDeploymentRepository; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 8690b4507c..0aa83791c2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -113,6 +113,7 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile model.IsTaskEnabled = profile.ScheduleTask.Enabled; model.LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()); model.HasActiveProvider = (provider != null); + model.FileNamePatternDescriptions = T("Admin.DataExchange.Export.FileNamePatternDescriptions").Text.SplitSafe(";"); model.Provider = new ExportProfileModel.ProviderModel(); model.Provider.ThumbnailUrl = GetThumbnailUrl(provider); diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index ee44a44113..a863d55d3f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -19,6 +19,7 @@ public partial class ExportProfileModel : EntityModelBase public string UnspecifiedString { get; set; } public bool LogFileExists { get; set; } public bool HasActiveProvider { get; set; } + public string[] FileNamePatternDescriptions { get; set; } [SmartResourceDisplayName("Admin.DataExchange.Export.Name")] public string Name { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml index 3e8d295898..17bfd46a84 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml @@ -23,6 +23,16 @@ @Html.TextBoxFor(x => x.FileNamePattern, new { @class = "input-large" }) @Html.ValidationMessageFor(x => x.FileNamePattern) + +
+
+ @T("Admin.Common.Show")... +
+ @FileNamePatternDescription() +
+
+ +
@@ -71,7 +71,7 @@
@@ -110,7 +110,7 @@
@@ -123,7 +123,7 @@
@@ -145,7 +145,7 @@
@@ -158,7 +158,7 @@
@@ -171,7 +171,7 @@
From c951a1d29ae8f9521365078a6b98dd073dbc0f86 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 11 Nov 2015 12:55:53 +0100 Subject: [PATCH 062/732] Further IDataExporter implementation --- .../DataExchange/IDataExporter.cs | 29 +- .../DataExchange/Internal/DataExportTask.cs | 12 +- .../DataExchange/Internal/DataExporter.cs | 917 +++++++++++++++++- .../Internal/DataExporterContext.cs | 218 +++++ .../Internal/DynamicEntityHelper.cs | 130 +-- .../SmartStore.Services.csproj | 1 + 6 files changed, 1191 insertions(+), 116 deletions(-) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs diff --git a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs index 82820238d8..4a96e5c515 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs @@ -6,7 +6,8 @@ namespace SmartStore.Services.DataExchange { - public delegate void ProgressSetter(int value, int maximum, string message); + public delegate void ProgressValueSetter(int value, int maximum, string message); + public delegate void ProgressMessageSetter(string message); public interface IDataExporter { @@ -23,24 +24,31 @@ public interface IDataExporter public class DataExportRequest { - private readonly static ProgressSetter _voidProgressSetter = DataExportRequest.SetProgress; + private readonly static ProgressValueSetter _voidProgressValueSetter = DataExportRequest.SetProgress; + private readonly static ProgressMessageSetter _voidProgressMessageSetter = DataExportRequest.SetProgress; - public DataExportRequest(ExportProfile profile) + public DataExportRequest(ExportProfile profile, Provider provider) { Guard.ArgumentNotNull(() => profile); + Guard.ArgumentNotNull(() => provider); - CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); - ProgressSetter = _voidProgressSetter; Profile = profile; - } + Provider = provider; + + ProgressValueSetter = _voidProgressValueSetter; + ProgressMessageSetter = _voidProgressMessageSetter; + + CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); + } public ExportProfile Profile { get; private set; } - public Provider Provider { get; set; } + public Provider Provider { get; private set; } public IEnumerable EntitiesToExport { get; set; } - public ProgressSetter ProgressSetter { get; set; } + public ProgressValueSetter ProgressValueSetter { get; set; } + public ProgressMessageSetter ProgressMessageSetter { get; set; } public IDictionary CustomData { get; private set; } @@ -49,5 +57,10 @@ private static void SetProgress(int val, int max, string msg) { // do nothing } + + private static void SetProgress(string msg) + { + // do nothing + } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs index ea3c676587..b1e3d31f54 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs @@ -37,10 +37,16 @@ public void Execute(TaskExecutionContext ctx) throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); // build export request - var request = new DataExportRequest(profile); - request.ProgressSetter = delegate(int val, int max, string msg) + var request = new DataExportRequest(profile, provider); + + request.ProgressValueSetter = delegate(int val, int max, string msg) + { + ctx.SetProgress(val, max, msg, true); + }; + + request.ProgressMessageSetter = delegate(string msg) { - ctx.SetProgress(val, max, msg); + ctx.SetProgress(null, msg, true); }; if (selectedEntityIds.HasValue()) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs index c0c44e0304..391b210db6 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs @@ -1,21 +1,38 @@ using System; using System.Collections.Generic; +using System.IO; +using System.IO.Compression; using System.Linq; +using System.Text; using System.Threading; using System.Web; +using SmartStore.Core; +using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Messages; +using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Email; using SmartStore.Core.Localization; +using SmartStore.Core.Logging; using SmartStore.Services.Catalog; +using SmartStore.Services.Common; +using SmartStore.Services.Customers; using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; using SmartStore.Services.Media; +using SmartStore.Services.Messages; +using SmartStore.Services.Orders; using SmartStore.Services.Seo; +using SmartStore.Services.Shipping; using SmartStore.Services.Tasks; using SmartStore.Services.Tax; +using SmartStore.Utilities; +using SmartStore.Utilities.Threading; namespace SmartStore.Services.DataExchange.Internal { @@ -23,57 +40,121 @@ public partial class DataExporter : IDataExporter { private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + #region Dependencies + private readonly ICommonServices _services; - private readonly IUrlRecordService _urlRecordService; - private readonly ILocalizedEntityService _localizedEntityService; - private readonly IPictureService _pictureService; - private readonly IPriceCalculationService _priceCalculationService; - private readonly ICurrencyService _currencyService; - private readonly ITaxService _taxService; - private readonly IPriceFormatter _priceFormatter; - private readonly ICategoryService _categoryService; - private readonly IProductAttributeParser _productAttributeParser; - private readonly IProductService _productService; - private readonly IDateTimeHelper _dateTimeHelper; - - private MediaSettings _mediaSettings; + private readonly Lazy _priceFormatter; + private readonly Lazy _dateTimeHelper; + private readonly Lazy _exportProfileService; + private readonly Lazy _localizedEntityService; + private readonly Lazy _languageService; + private readonly Lazy _urlRecordService; + private readonly Lazy _pictureService; + private readonly Lazy _priceCalculationService; + private readonly Lazy _currencyService; + private readonly Lazy _taxService; + private readonly Lazy _categoryService; + private readonly Lazy _productAttributeParser; + private readonly Lazy _productAttributeService; + private readonly Lazy _productTemplateService; + private readonly Lazy _productService; + private readonly Lazy _orderService; + private readonly Lazy _manufacturerService; + private readonly Lazy _customerService; + private readonly Lazy _addressService; + private readonly Lazy _countryService; + private readonly Lazy _shipmentService; + private readonly Lazy _genericAttributeService; + private readonly Lazy _emailAccountService; + private readonly Lazy _emailSender; + private readonly Lazy _deliveryTimeService; + private readonly Lazy _quantityUnitService; + + private readonly Lazy>_customerRepository; + private readonly Lazy> _subscriptionRepository; + + private Lazy _dataExchangeSettings; + private Lazy _mediaSettings; public DataExporter( ICommonServices services, - IUrlRecordService urlRecordService, - ILocalizedEntityService localizedEntityService, - IPictureService pictureService, - IPriceCalculationService priceCalculationService, - ICurrencyService currencyService, - ITaxService taxService, - IPriceFormatter priceFormatter, - ICategoryService categoryService, - IProductAttributeParser productAttributeParser, - IProductService productService, - IDateTimeHelper dateTimeHelper, - MediaSettings mediaSettings) + Lazy priceFormatter, + Lazy dateTimeHelper, + Lazy exportProfileService, + Lazy localizedEntityService, + Lazy languageService, + Lazy urlRecordService, + Lazy pictureService, + Lazy priceCalculationService, + Lazy currencyService, + Lazy taxService, + Lazy categoryService, + Lazy productAttributeParser, + Lazy productAttributeService, + Lazy productTemplateService, + Lazy productService, + Lazy orderService, + Lazy manufacturerService, + Lazy customerService, + Lazy addressService, + Lazy countryService, + Lazy shipmentService, + Lazy genericAttributeService, + Lazy emailAccountService, + Lazy emailSender, + Lazy deliveryTimeService, + Lazy quantityUnitService, + Lazy> customerRepository, + Lazy> subscriptionRepository, + Lazy dataExchangeSettings, + Lazy mediaSettings) { + _priceFormatter = priceFormatter; + _dateTimeHelper = dateTimeHelper; _services = services; - _urlRecordService = urlRecordService; + _exportProfileService = exportProfileService; _localizedEntityService = localizedEntityService; + _languageService = languageService; + _urlRecordService = urlRecordService; _pictureService = pictureService; _priceCalculationService = priceCalculationService; _currencyService = currencyService; _taxService = taxService; - _priceFormatter = priceFormatter; _categoryService = categoryService; _productAttributeParser = productAttributeParser; + _productAttributeService = productAttributeService; + _productTemplateService = productTemplateService; _productService = productService; - _dateTimeHelper = dateTimeHelper; + _orderService = orderService; + _manufacturerService = manufacturerService; + _customerService = customerService; + _addressService = addressService; + _countryService = countryService; + _shipmentService = shipmentService; + _genericAttributeService = genericAttributeService; + _emailAccountService = emailAccountService; + _emailSender = emailSender; + _deliveryTimeService = deliveryTimeService; + _quantityUnitService = quantityUnitService; + _customerRepository = customerRepository; + _subscriptionRepository = subscriptionRepository; + + _dataExchangeSettings = dataExchangeSettings; _mediaSettings = mediaSettings; + + T = NullLocalizer.Instance; } + public Localizer T { get; set; } + + #endregion + #region Utilities - private void SetProgress(DataExportTaskContext ctx, int loadedRecords) + private void SetProgress(DataExporterContext ctx, int loadedRecords) { - if (!ctx.IsPreview && ctx.TaskContext.ScheduleTask != null && loadedRecords > 0) + if (!ctx.IsPreview && loadedRecords > 0) { int totalRecords = ctx.RecordsPerStore.Sum(x => x.Value); @@ -84,23 +165,219 @@ private void SetProgress(DataExportTaskContext ctx, int loadedRecords) var msg = ctx.ProgressInfo.FormatInvariant(ctx.RecordCount, totalRecords); - ctx.TaskContext.SetProgress(ctx.RecordCount, totalRecords, msg, true); + ctx.Request.ProgressValueSetter.Invoke(ctx.RecordCount, totalRecords, msg); + } + } + + private void SetProgress(DataExporterContext ctx, string message) + { + if (!ctx.IsPreview && message.HasValue()) + { + ctx.Request.ProgressMessageSetter.Invoke(message); } } - private void SetProgress(DataExportTaskContext ctx, string message) + private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx, int pageIndex = 0) { - if (!ctx.IsPreview && ctx.TaskContext.ScheduleTask != null && message.HasValue()) + var offset = ctx.Profile.Offset + (pageIndex * PageSize); + + var limit = (ctx.IsPreview ? PageSize : ctx.Profile.Limit); + + var recordsPerSegment = (ctx.IsPreview ? 0 : ctx.Profile.BatchSize); + + var totalCount = ctx.Profile.Offset + ctx.RecordsPerStore.First(x => x.Key == ctx.Store.Id).Value; + + switch (ctx.Provider.Value.EntityType) { - ctx.TaskContext.SetProgress(null, message, true); + case ExportEntityType.Product: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetProducts(ctx, skip), + entities => + { + // load data behind navigation properties for current queue in one go + ctx.ProductExportContext = new ProductExportContext(entities, + x => _productAttributeService.Value.GetProductVariantAttributesByProductIds(x, null), + x => _productAttributeService.Value.GetProductVariantAttributeCombinations(x), + x => _productService.Value.GetTierPricesByProductIds(x, (ctx.Projection.CurrencyId ?? 0) != 0 ? ctx.ContextCustomer : null, ctx.Store.Id), + x => _categoryService.Value.GetProductCategoriesByProductIds(x), + x => _manufacturerService.Value.GetProductManufacturersByProductIds(x), + x => _productService.Value.GetProductPicturesByProductIds(x), + x => _productService.Value.GetProductTagsByProductIds(x), + x => _productService.Value.GetAppliedDiscountsByProductIds(x), + x => _productService.Value.GetProductSpecificationAttributesByProductIds(x), + x => _productService.Value.GetBundleItemsByProductIds(x, true) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + case ExportEntityType.Order: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetOrders(ctx, skip), + entities => + { + ctx.OrderExportContext = new OrderExportContext(entities, + x => _customerService.Value.GetCustomersByIds(x), + x => _customerService.Value.GetRewardPointsHistoriesByCustomerIds(x), + x => _addressService.Value.GetAddressByIds(x), + x => _orderService.Value.GetOrderItemsByOrderIds(x), + x => _shipmentService.Value.GetShipmentsByOrderIds(x) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + case ExportEntityType.Manufacturer: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetManufacturers(ctx, skip), + entities => + { + ctx.ManufacturerExportContext = new ManufacturerExportContext(entities, + x => _manufacturerService.Value.GetProductManufacturersByManufacturerIds(x), + x => _pictureService.Value.GetPicturesByIds(x) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + case ExportEntityType.Category: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetCategories(ctx, skip), + entities => + { + ctx.CategoryExportContext = new CategoryExportContext(entities, + x => _categoryService.Value.GetProductCategoriesByCategoryIds(x), + x => _pictureService.Value.GetPicturesByIds(x) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + case ExportEntityType.Customer: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetCustomers(ctx, skip), + entities => + { + ctx.CustomerExportContext = new CustomerExportContext(entities, + x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer") + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + case ExportEntityType.NewsLetterSubscription: + ctx.ExecuteContext.Segmenter = new ExportDataSegmenter + ( + skip => GetNewsLetterSubscriptions(ctx, skip), + null, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount + ); + break; + + default: + ctx.ExecuteContext.Segmenter = null; + break; } + + return ctx.ExecuteContext.Segmenter as IExportDataSegmenterProvider; + } + + private bool CallProvider(DataExporterContext ctx, string streamId, string method, string path) + { + if (method != "Execute" && method != "OnExecuted") + throw new SmartException("Unknown export method {0}".FormatInvariant(method.NaIfEmpty())); + + try + { + ctx.ExecuteContext.DataStreamId = streamId; + + using (ctx.ExecuteContext.DataStream = new MemoryStream()) + { + if (method == "Execute") + { + ctx.Provider.Value.Execute(ctx.ExecuteContext); + } + else if (method == "OnExecuted") + { + ctx.Provider.Value.OnExecuted(ctx.ExecuteContext); + } + + if (ctx.IsFileBasedExport && path.HasValue()) + { + if (!ctx.ExecuteContext.DataStream.CanSeek) + ctx.Log.Warning("Data stream seems to be closed!"); + + ctx.ExecuteContext.DataStream.Seek(0, SeekOrigin.Begin); + + using (_rwLock.GetWriteLock()) + using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + ctx.Log.Information("Creating file " + path); + + ctx.ExecuteContext.DataStream.CopyTo(fileStream); + } + } + } + } + catch (Exception exc) + { + ctx.ExecuteContext.Abort = ExportAbortion.Hard; + ctx.Log.Error("The provider failed at the {0} method: {1}".FormatInvariant(method, exc.ToAllMessages()), exc); + ctx.Result.LastError = exc.ToString(); + } + finally + { + if (ctx.ExecuteContext.DataStream != null) + { + ctx.ExecuteContext.DataStream.Dispose(); + ctx.ExecuteContext.DataStream = null; + } + } + + return (ctx.ExecuteContext.Abort != ExportAbortion.Hard); + } + + private void SendCompletionEmail(DataExporterContext ctx) + { + var emailAccount = _emailAccountService.Value.GetEmailAccountById(ctx.Profile.EmailAccountId); + var smtpContext = new SmtpContext(emailAccount); + var message = new EmailMessage(); + + var storeInfo = "{0} ({1})".FormatInvariant(ctx.Store.Name, ctx.Store.Url); + + message.To.AddRange(ctx.Profile.CompletedEmailAddresses.SplitSafe(",").Where(x => x.IsEmail()).Select(x => new EmailAddress(x))); + message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); + + message.Subject = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Subject", ctx.Projection.LanguageId ?? 0) + .FormatInvariant(ctx.Profile.Name); + + message.Body = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Body", ctx.Projection.LanguageId ?? 0) + .FormatInvariant(storeInfo); + + _emailSender.Value.SendEmail(smtpContext, message); } #endregion #region Getting data - private IQueryable GetProductQuery(DataExportTaskContext ctx, int skip, int take) + private IQueryable GetProductQuery(DataExporterContext ctx, int skip, int take) { IQueryable query = null; @@ -134,12 +411,12 @@ private IQueryable GetProductQuery(DataExportTaskContext ctx, int skip, searchContext.CategoryIds = ctx.Filter.CategoryIds.ToList(); if (ctx.Filter.CreatedFrom.HasValue) - searchContext.CreatedFromUtc = _dateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _dateTimeHelper.CurrentTimeZone); + searchContext.CreatedFromUtc = _dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _dateTimeHelper.Value.CurrentTimeZone); if (ctx.Filter.CreatedTo.HasValue) - searchContext.CreatedToUtc = _dateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _dateTimeHelper.CurrentTimeZone); + searchContext.CreatedToUtc = _dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _dateTimeHelper.Value.CurrentTimeZone); - query = _productService.PrepareProductSearchQuery(searchContext); + query = _productService.Value.PrepareProductSearchQuery(searchContext); query = query.OrderByDescending(x => x.CreatedOnUtc); } @@ -157,7 +434,7 @@ private IQueryable GetProductQuery(DataExportTaskContext ctx, int skip, return query; } - private List GetProducts(DataExportTaskContext ctx, int skip) + private List GetProducts(DataExporterContext ctx, int skip) { var result = new List(); @@ -182,7 +459,7 @@ private List GetProducts(DataExportTaskContext ctx, int skip) ParentGroupedProductId = product.Id }; - foreach (var associatedProduct in _productService.SearchProducts(associatedSearchContext)) + foreach (var associatedProduct in _productService.Value.SearchProducts(associatedSearchContext)) { result.Add(associatedProduct); } @@ -205,8 +482,568 @@ private List GetProducts(DataExportTaskContext ctx, int skip) return result; } + private IQueryable GetOrderQuery(DataExporterContext ctx, int skip, int take) + { + var query = _orderService.Value.GetOrders( + ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, + ctx.Projection.CustomerId ?? 0, + ctx.Filter.CreatedFrom.HasValue ? (DateTime?)_dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _dateTimeHelper.Value.CurrentTimeZone) : null, + ctx.Filter.CreatedTo.HasValue ? (DateTime?)_dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _dateTimeHelper.Value.CurrentTimeZone) : null, + ctx.Filter.OrderStatusIds, + ctx.Filter.PaymentStatusIds, + ctx.Filter.ShippingStatusIds, + null, + null, + null); + + if (ctx.EntityIdsSelected.Count > 0) + query = query.Where(x => ctx.EntityIdsSelected.Contains(x.Id)); + + query = query.OrderByDescending(x => x.CreatedOnUtc); + + if (skip > 0) + query = query.Skip(skip); + + if (take != int.MaxValue) + query = query.Take(take); + + return query; + } + + private List GetOrders(DataExporterContext ctx, int skip) + { + var orders = GetOrderQuery(ctx, skip, PageSize).ToList(); + + if (ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) + { + ctx.EntityIdsLoaded = ctx.EntityIdsLoaded + .Union(orders.Select(x => x.Id)) + .Distinct() + .ToList(); + } + + try + { + SetProgress(ctx, orders.Count); + + _services.DbContext.DetachEntities(orders); + } + catch { } + + return orders; + } + + private IQueryable GetManufacturerQuery(DataExporterContext ctx, int skip, int take) + { + var showHidden = !ctx.Filter.IsPublished.HasValue; + var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + + var query = _manufacturerService.Value.GetManufacturers(showHidden, storeId); + + query = query.OrderBy(x => x.DisplayOrder); + + if (skip > 0) + query = query.Skip(skip); + + if (take != int.MaxValue) + query = query.Take(take); + + return query; + } + + private List GetManufacturers(DataExporterContext ctx, int skip) + { + var manus = GetManufacturerQuery(ctx, skip, PageSize).ToList(); + + try + { + SetProgress(ctx, manus.Count); + + _services.DbContext.DetachEntities(manus); + } + catch { } + + return manus; + } + + private IQueryable GetCategoryQuery(DataExporterContext ctx, int skip, int take) + { + var showHidden = !ctx.Filter.IsPublished.HasValue; + var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + + var query = _categoryService.Value.GetCategories(null, showHidden, null, true, storeId); + + query = query + .OrderBy(x => x.ParentCategoryId) + .ThenBy(x => x.DisplayOrder); + + if (skip > 0) + query = query.Skip(skip); + + if (take != int.MaxValue) + query = query.Take(take); + + return query; + } + + private List GetCategories(DataExporterContext ctx, int skip) + { + var categories = GetCategoryQuery(ctx, skip, PageSize).ToList(); + + try + { + SetProgress(ctx, categories.Count); + + _services.DbContext.DetachEntities(categories); + } + catch { } + + return categories; + } + + private IQueryable GetCustomerQuery(DataExporterContext ctx, int skip, int take) + { + var query = _customerRepository.Value.TableUntracked + .Expand(x => x.BillingAddress) + .Expand(x => x.ShippingAddress) + .Expand(x => x.Addresses.Select(y => y.Country)) + .Expand(x => x.Addresses.Select(y => y.StateProvince)) + .Expand(x => x.CustomerRoles) + .Where(x => !x.Deleted); + + if (ctx.EntityIdsSelected.Count > 0) + { + query = query.Where(x => ctx.EntityIdsSelected.Contains(x.Id)); + } + + query = query.OrderByDescending(x => x.CreatedOnUtc); + + if (skip > 0) + query = query.Skip(skip); + + if (take != int.MaxValue) + query = query.Take(take); + + return query; + } + + private List GetCustomers(DataExporterContext ctx, int skip) + { + var customers = GetCustomerQuery(ctx, skip, PageSize).ToList(); + + try + { + SetProgress(ctx, customers.Count); + + _services.DbContext.DetachEntities(customers); + } + catch { } + + return customers; + } + + private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int skip, int take) + { + var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + + var query = _subscriptionRepository.Value.TableUntracked; + + if (storeId > 0) + { + query = query.Where(x => x.StoreId == storeId); + } + + query = query + .OrderBy(x => x.StoreId) + .ThenBy(x => x.Email); + + if (skip > 0) + query = query.Skip(skip); + + if (take != int.MaxValue) + query = query.Take(take); + + return query; + } + + private List GetNewsLetterSubscriptions(DataExporterContext ctx, int skip) + { + var subscriptions = GetNewsLetterSubscriptionQuery(ctx, skip, PageSize).ToList(); + + try + { + SetProgress(ctx, subscriptions.Count); + + _services.DbContext.DetachEntities(subscriptions); + } + catch { } + + return subscriptions; + } + #endregion + private List Init(DataExporterContext ctx, int? totalRecords = null) + { + // Init base things that are even required for preview. Init all other things (regular export) in ExportCoreOuter. + List result = null; + + if (ctx.Projection.CurrencyId.HasValue) + ctx.ContextCurrency = _currencyService.Value.GetCurrencyById(ctx.Projection.CurrencyId.Value); + else + ctx.ContextCurrency = _services.WorkContext.WorkingCurrency; + + if (ctx.Projection.CustomerId.HasValue) + ctx.ContextCustomer = _customerService.Value.GetCustomerById(ctx.Projection.CustomerId.Value); + else + ctx.ContextCustomer = _services.WorkContext.CurrentCustomer; + + if (ctx.Projection.LanguageId.HasValue) + ctx.ContextLanguage = _languageService.Value.GetLanguageById(ctx.Projection.LanguageId.Value); + else + ctx.ContextLanguage = _services.WorkContext.WorkingLanguage; + + ctx.Stores = _services.StoreService.GetAllStores().ToDictionary(x => x.Id, x => x); + ctx.Languages = _languageService.Value.GetAllLanguages(true).ToDictionary(x => x.Id, x => x); + + if (!ctx.IsPreview && ctx.Profile.PerStore) + { + result = new List(ctx.Stores.Values.Where(x => x.Id == ctx.Filter.StoreId || ctx.Filter.StoreId == 0)); + } + else + { + int? storeId = (ctx.Filter.StoreId == 0 ? ctx.Projection.StoreId : ctx.Filter.StoreId); + + ctx.Store = ctx.Stores.Values.FirstOrDefault(x => x.Id == (storeId ?? _services.StoreContext.CurrentStore.Id)); + + result = new List { ctx.Store }; + } + + // get total records for progress + foreach (var store in result) + { + ctx.Store = store; + + int totalCount = 0; + + if (totalRecords.HasValue) + { + totalCount = totalRecords.Value; // speed up preview by not counting total at each page + } + else + { + switch (ctx.Provider.Value.EntityType) + { + case ExportEntityType.Product: + totalCount = GetProductQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Order: + totalCount = GetOrderQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Manufacturer: + totalCount = GetManufacturerQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Category: + totalCount = GetCategoryQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Customer: + totalCount = GetCustomerQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.NewsLetterSubscription: + totalCount = GetNewsLetterSubscriptionQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + break; + } + } + + ctx.RecordsPerStore.Add(store.Id, totalCount); + } + + return result; + } + + private void ExportCoreInner(DataExporterContext ctx, Store store) + { + if (ctx.ExecuteContext.Abort != ExportAbortion.None) + return; + + int fileIndex = 0; + + ctx.Store = store; + + { + var logHead = new StringBuilder(); + logHead.AppendLine(); + logHead.AppendLine(new string('-', 40)); + logHead.AppendLine("SmartStore.NET:\t\tv." + SmartStoreVersion.CurrentFullVersion); + logHead.Append("Export profile:\t\t" + ctx.Profile.Name); + logHead.AppendLine(ctx.Profile.Id == 0 ? " (volatile)" : " (Id {0})".FormatInvariant(ctx.Profile.Id)); + + logHead.AppendLine("Export provider:\t{0} ({1})".FormatInvariant(ctx.Provider.Metadata.FriendlyName, ctx.Profile.ProviderSystemName)); + + var plugin = ctx.Provider.Metadata.PluginDescriptor; + logHead.Append("Plugin:\t\t\t\t"); + logHead.AppendLine(plugin == null ? "".NaIfEmpty() : "{0} ({1}) v.{2}".FormatInvariant(plugin.FriendlyName, plugin.SystemName, plugin.Version.ToString())); + + logHead.AppendLine("Entity:\t\t\t\t" + ctx.Provider.Value.EntityType.ToString()); + + var storeInfo = (ctx.Profile.PerStore ? "{0} (Id {1})".FormatInvariant(ctx.Store.Name, ctx.Store.Id) : "All stores"); + logHead.Append("Store:\t\t\t\t" + storeInfo); + + ctx.Log.Information(logHead.ToString()); + } + + ctx.ExecuteContext.Store = ToDynamic(ctx, ctx.Store); + + ctx.ExecuteContext.MaxFileNameLength = _dataExchangeSettings.Value.MaxFileNameLength; + + ctx.ExecuteContext.HasPublicDeployment = ctx.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); + + ctx.ExecuteContext.PublicFolderPath = (ctx.ExecuteContext.HasPublicDeployment ? Path.Combine(HttpRuntime.AppDomainAppPath, PublicFolder) : null); + + var fileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); + + + using (var segmenter = CreateSegmenter(ctx)) + { + if (segmenter == null) + { + throw new SmartException("Unsupported entity type '{0}'".FormatInvariant(ctx.Provider.Value.EntityType.ToString())); + } + + if (segmenter.TotalRecords <= 0) + { + ctx.Log.Information("There are no records to export"); + } + + while (ctx.ExecuteContext.Abort == ExportAbortion.None && segmenter.HasData) + { + segmenter.RecordPerSegmentCount = 0; + ctx.ExecuteContext.RecordsSucceeded = 0; + + string path = null; + + if (ctx.IsFileBasedExport) + { + var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); + + ctx.ExecuteContext.FileName = resolvedPattern + fileExtension; + path = Path.Combine(ctx.ExecuteContext.Folder, ctx.ExecuteContext.FileName); + + if (ctx.ExecuteContext.HasPublicDeployment) + ctx.ExecuteContext.PublicFileUrl = ctx.Store.Url.EnsureEndsWith("/") + PublicFolder.EnsureEndsWith("/") + ctx.ExecuteContext.FileName; + } + + if (CallProvider(ctx, null, "Execute", path)) + { + ctx.Log.Information("Provider reports {0} successful exported record(s)".FormatInvariant(ctx.ExecuteContext.RecordsSucceeded)); + + // create info for deployment list in profile edit + if (ctx.IsFileBasedExport) + { + ctx.Result.Files.Add(new DataExportResult.ExportFileInfo + { + StoreId = ctx.Store.Id, + FileName = ctx.ExecuteContext.FileName + }); + } + } + + if (ctx.ExecuteContext.IsMaxFailures) + ctx.Log.Warning("Export aborted. The maximum number of failures has been reached"); + + if (ctx.CancellationToken.IsCancellationRequested) + ctx.Log.Warning("Export aborted. A cancellation has been requested"); + } + + if (ctx.ExecuteContext.Abort != ExportAbortion.Hard) + { + // always call OnExecuted + if (ctx.ExecuteContext.ExtraDataStreams.Count == 0) + ctx.ExecuteContext.ExtraDataStreams.Add(new ExportExtraStreams()); + + ctx.ExecuteContext.ExtraDataStreams.ForEach(x => + { + var path = (x.FileName.HasValue() ? Path.Combine(ctx.ExecuteContext.Folder, x.FileName) : null); + + CallProvider(ctx, x.Id, "OnExecuted", path); + }); + + ctx.ExecuteContext.ExtraDataStreams.Clear(); + } + } + } + + private void ExportCoreOuter(DataExporterContext ctx) + { + if (ctx.Profile == null || !ctx.Profile.Enabled) + return; + + FileSystemHelper.Delete(ctx.LogPath); + FileSystemHelper.ClearDirectory(ctx.FolderContent, false); + FileSystemHelper.Delete(ctx.ZipPath); + + using (var logger = new TraceLogger(ctx.LogPath)) + { + try + { + if (!ctx.Provider.IsValid()) + { + throw new SmartException("Export aborted because the export provider is not valid"); + } + + ctx.Log = logger; + ctx.ExecuteContext.Log = logger; + ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); + + if (ctx.Profile.ProviderConfigData.HasValue()) + { + var configInfo = ctx.Provider.Value.ConfigurationInfo; + if (configInfo != null) + { + ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(ctx.Profile.ProviderConfigData, configInfo.ModelType); + } + } + + // TODO: lazyLoading: false. It requires IDbContextExtensions::Load/QueryFor for properties internally mapped by EF + using (var scope = new DbContextScope(_services.DbContext, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, forceNoTracking: true)) + { + ctx.DeliveryTimes = _deliveryTimeService.Value.GetAllDeliveryTimes().ToDictionary(x => x.Id); + ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id); + ctx.ProductTemplates = _productTemplateService.Value.GetAllProductTemplates().ToDictionary(x => x.Id); + + if (ctx.Provider.Value.EntityType == ExportEntityType.Product) + { + var allCategories = _categoryService.Value.GetAllCategories(showHidden: true, applyNavigationFilters: false); + ctx.Categories = allCategories.ToDictionary(x => x.Id); + } + + if (ctx.Provider.Value.EntityType == ExportEntityType.Order) + { + ctx.Countries = _countryService.Value.GetAllCountries(true).ToDictionary(x => x.Id, x => x); + } + + if (ctx.Provider.Value.EntityType == ExportEntityType.Customer) + { + var subscriptionEmails = _subscriptionRepository.Value.TableUntracked + .Where(x => x.Active) + .Select(x => x.Email) + .Distinct() + .ToList(); + + ctx.NewsletterSubscriptions = new HashSet(subscriptionEmails, StringComparer.OrdinalIgnoreCase); + } + + var stores = Init(ctx); + + ctx.ExecuteContext.Language = ToDynamic(ctx, ctx.ContextLanguage); + ctx.ExecuteContext.Customer = ToDynamic(ctx, ctx.ContextCustomer); + ctx.ExecuteContext.Currency = ToDynamic(ctx, ctx.ContextCurrency); + + stores.ForEach(x => ExportCoreInner(ctx, x)); + } + + if (!ctx.IsPreview && ctx.ExecuteContext.Abort != ExportAbortion.Hard) + { + if (ctx.IsFileBasedExport) + { + if (ctx.Profile.CreateZipArchive || ctx.Profile.Deployments.Any(x => x.Enabled && x.CreateZip)) + { + ZipFile.CreateFromDirectory(ctx.FolderContent, ctx.ZipPath, CompressionLevel.Fastest, true); + } + + SetProgress(ctx, T("Common.Deployment")); + + // TODO: deployment + //foreach (var deployment in ctx.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled)) + //{ + // try + // { + // switch (deployment.DeploymentType) + // { + // case ExportDeploymentType.FileSystem: + // DeployFileSystem(ctx, deployment); + // break; + // case ExportDeploymentType.Email: + // DeployEmail(ctx, deployment); + // break; + // case ExportDeploymentType.Http: + // DeployHttp(ctx, deployment); + // break; + // case ExportDeploymentType.Ftp: + // DeployFtp(ctx, deployment); + // break; + // } + // } + // catch (Exception exc) + // { + // logger.Error("Deployment \"{0}\" of type {1} failed: {2}".FormatInvariant( + // deployment.Name, deployment.DeploymentType.ToString(), exc.Message), exc); + // } + //} + } + + if (ctx.Profile.EmailAccountId != 0 && ctx.Profile.CompletedEmailAddresses.HasValue()) + { + SendCompletionEmail(ctx); + } + } + } + catch (Exception exc) + { + logger.Error(exc); + ctx.Result.LastError = exc.ToString(); + } + finally + { + try + { + if (!ctx.IsPreview && ctx.Profile.Id != 0) + { + ctx.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); + + _exportProfileService.Value.UpdateExportProfile(ctx.Profile); + } + } + catch { } + + try + { + if (ctx.IsFileBasedExport && ctx.ExecuteContext.Abort != ExportAbortion.Hard && ctx.Profile.Cleanup) + { + FileSystemHelper.ClearDirectory(ctx.FolderContent, false); + } + } + catch { } + + try + { + ctx.NewsletterSubscriptions.Clear(); + ctx.ProductTemplates.Clear(); + ctx.Countries.Clear(); + ctx.Stores.Clear(); + ctx.QuantityUnits.Clear(); + ctx.DeliveryTimes.Clear(); + ctx.CategoryPathes.Clear(); + ctx.Categories.Clear(); + ctx.EntityIdsSelected.Clear(); + ctx.ProductExportContext = null; + ctx.OrderExportContext = null; + ctx.ManufacturerExportContext = null; + ctx.CategoryExportContext = null; + ctx.CustomerExportContext = null; + + ctx.ExecuteContext.CustomProperties.Clear(); + ctx.ExecuteContext.Log = null; + ctx.Log = null; + } + catch { } + } + } + + if (ctx.IsPreview || ctx.ExecuteContext.Abort == ExportAbortion.Hard) + return; + } + /// /// The name of the public export folder /// diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs new file mode 100644 index 0000000000..cb95e67f23 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using SmartStore.Core; +using SmartStore.Core.Domain; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.DataExchange; +using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Logging; +using SmartStore.Core.Plugins; +using SmartStore.Utilities; + +namespace SmartStore.Services.DataExchange.Internal +{ + internal class DataExporterContext + { + private ProductExportContext _productExportContext; + private OrderExportContext _orderExportContext; + private ManufacturerExportContext _manufacturerExportContext; + private CategoryExportContext _categoryExportContext; + private CustomerExportContext _customerExportContext; + + public DataExporterContext( + DataExportRequest request, + CancellationToken cancellationToken, + ExportProfile profile, + Provider provider, + string selectedIds = null, + Action previewData = null) + { + Request = request; + CancellationToken = cancellationToken; + Profile = profile; + Provider = provider; + Filter = XmlHelper.Deserialize(profile.Filtering); + Projection = XmlHelper.Deserialize(profile.Projection); + EntityIdsSelected = selectedIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); + PreviewData = previewData; + + FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(profile.FolderName)); + FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; + + Categories = new Dictionary(); + CategoryPathes = new Dictionary(); + Countries = new Dictionary(); + ProductTemplates = new Dictionary(); + NewsletterSubscriptions = new HashSet(); + + RecordsPerStore = new Dictionary(); + EntityIdsLoaded = new List(); + + Result = new DataExportResult + { + FileFolder = (IsFileBasedExport ? FolderContent : null) + }; + + ExecuteContext = new ExportExecuteContext(Result, CancellationToken, FolderContent); + ExecuteContext.Projection = XmlHelper.Deserialize(profile.Projection); + } + + public List EntityIdsSelected { get; private set; } + public List EntityIdsLoaded { get; set; } + + public int RecordCount { get; set; } + public Dictionary RecordsPerStore { get; set; } + public string ProgressInfo { get; set; } + public IQueryable QueryProducts { get; set; } + + public Action PreviewData { get; private set; } + public bool IsPreview + { + get { return PreviewData != null; } + } + + public DataExportRequest Request { get; private set; } + public CancellationToken CancellationToken { get; private set; } + public ExportProfile Profile { get; private set; } + public Provider Provider { get; private set; } + + public bool Supports(ExportFeatures feature) + { + return (!IsPreview && Provider.Metadata.ExportFeatures.HasFlag(feature)); + } + + public ExportFilter Filter { get; private set; } + public ExportProjection Projection { get; private set; } + public Currency ContextCurrency { get; set; } + public Customer ContextCustomer { get; set; } + public Language ContextLanguage { get; set; } + + public TraceLogger Log { get; set; } + public Store Store { get; set; } + + public string FolderRoot { get; private set; } + public string FolderContent { get; private set; } + public string ZipName + { + get { return Profile.FolderName + ".zip"; } + } + public string ZipPath + { + get { return Path.Combine(FolderRoot, ZipName); } + } + public string LogPath + { + get { return Path.Combine(FolderRoot, "log.txt"); } + } + + public bool IsFileBasedExport + { + get { return Provider == null || Provider.Value == null || Provider.Value.FileExtension.HasValue(); } + } + public string[] GetDeploymentFiles(ExportDeployment deployment) + { + if (!IsFileBasedExport) + return new string[0]; + + if (deployment.CreateZip) + return new string[] { ZipPath }; + + return System.IO.Directory.GetFiles(FolderContent, "*.*", SearchOption.AllDirectories); + } + + // data loaded once per export + public Dictionary Categories { get; set; } + public Dictionary CategoryPathes { get; set; } + public Dictionary DeliveryTimes { get; set; } + public Dictionary QuantityUnits { get; set; } + public Dictionary Stores { get; set; } + public Dictionary Languages { get; set; } + public Dictionary Countries { get; set; } + public Dictionary ProductTemplates { get; set; } + public HashSet NewsletterSubscriptions { get; set; } + + // data loaded once per page + public ProductExportContext ProductExportContext + { + get + { + return _productExportContext; + } + set + { + if (_productExportContext != null) + _productExportContext.Clear(); + + _productExportContext = value; + } + } + + public OrderExportContext OrderExportContext + { + get + { + return _orderExportContext; + } + set + { + if (_orderExportContext != null) + _orderExportContext.Clear(); + + _orderExportContext = value; + } + } + + public ManufacturerExportContext ManufacturerExportContext + { + get + { + return _manufacturerExportContext; + } + set + { + if (_manufacturerExportContext != null) + _manufacturerExportContext.Clear(); + + _manufacturerExportContext = value; + } + } + + public CategoryExportContext CategoryExportContext + { + get + { + return _categoryExportContext; + } + set + { + if (_categoryExportContext != null) + _categoryExportContext.Clear(); + + _categoryExportContext = value; + } + } + public CustomerExportContext CustomerExportContext + { + get + { + return _customerExportContext; + } + set + { + if (_customerExportContext != null) + _customerExportContext.Clear(); + + _customerExportContext = value; + } + } + + public ExportExecuteContext ExecuteContext { get; set; } + public DataExportResult Result { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs index c1ebf6a7a5..e3de6b5e56 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs @@ -28,7 +28,7 @@ namespace SmartStore.Services.DataExchange.Internal { public partial class DataExporter { - private void PrepareProductDescription(DataExportTaskContext ctx, dynamic dynObject, Product product) + private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObject, Product product) { try { @@ -118,25 +118,25 @@ private void PrepareProductDescription(DataExportTaskContext ctx, dynamic dynObj catch { } } - private decimal? ConvertPrice(DataExportTaskContext ctx, Product product, decimal? price) + private decimal? ConvertPrice(DataExporterContext ctx, Product product, decimal? price) { if (price.HasValue) { if (ctx.Projection.ConvertNetToGrossPrices) { decimal taxRate; - price = _taxService.GetProductPrice(product, price.Value, true, ctx.ContextCustomer, out taxRate); + price = _taxService.Value.GetProductPrice(product, price.Value, true, ctx.ContextCustomer, out taxRate); } if (price != decimal.Zero) { - price = _currencyService.ConvertFromPrimaryStoreCurrency(price.Value, ctx.ContextCurrency, ctx.Store); + price = _currencyService.Value.ConvertFromPrimaryStoreCurrency(price.Value, ctx.ContextCurrency, ctx.Store); } } return price; } - private decimal CalculatePrice(DataExportTaskContext ctx, Product product, bool forAttributeCombination) + private decimal CalculatePrice(DataExporterContext ctx, Product product, bool forAttributeCombination) { decimal price = product.Price; @@ -148,22 +148,22 @@ private decimal CalculatePrice(DataExportTaskContext ctx, Product product, bool if (ctx.Projection.PriceType.Value == PriceDisplayType.LowestPrice) { bool displayFromMessage; - price = _priceCalculationService.GetLowestPrice(product, priceCalculationContext, out displayFromMessage); + price = _priceCalculationService.Value.GetLowestPrice(product, priceCalculationContext, out displayFromMessage); } else if (ctx.Projection.PriceType.Value == PriceDisplayType.PreSelectedPrice) { - price = _priceCalculationService.GetPreselectedPrice(product, priceCalculationContext); + price = _priceCalculationService.Value.GetPreselectedPrice(product, priceCalculationContext); } else if (ctx.Projection.PriceType.Value == PriceDisplayType.PriceWithoutDiscountsAndAttributes) { - price = _priceCalculationService.GetFinalPrice(product, null, ctx.ContextCustomer, decimal.Zero, false, 1, null, priceCalculationContext); + price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, decimal.Zero, false, 1, null, priceCalculationContext); } } return ConvertPrice(ctx, product, price) ?? price; } - private void MergeWithCombination(DataExportTaskContext ctx, dynamic dynObject, Product product, + private void MergeWithCombination(DataExporterContext ctx, dynamic dynObject, Product product, ICollection productAttributes, ProductVariantAttributeCombination combination) { product.MergeWithCombination(combination); @@ -172,13 +172,13 @@ private void MergeWithCombination(DataExportTaskContext ctx, dynamic dynObject, if (combination != null && ctx.Projection.AttributeCombinationValueMerging == ExportAttributeValueMerging.AppendAllValuesToName) { - var values = _productAttributeParser.ParseProductVariantAttributeValues(combination.AttributesXml, productAttributes, ctx.Projection.LanguageId ?? 0); + var values = _productAttributeParser.Value.ParseProductVariantAttributeValues(combination.AttributesXml, productAttributes, ctx.Projection.LanguageId ?? 0); dynObject.Name = ((string)dynObject.Name).Grow(string.Join(", ", values), " "); } - dynObject._BasePriceInfo = product.GetBasePriceInfo(_services.Localization, _priceFormatter, _currencyService, _taxService, - _priceCalculationService, ctx.ContextCurrency, decimal.Zero, true); + dynObject._BasePriceInfo = product.GetBasePriceInfo(_services.Localization, _priceFormatter.Value, _currencyService.Value, _taxService.Value, + _priceCalculationService.Value, ctx.ContextCurrency, decimal.Zero, true); // navigation properties ToDeliveryTime(ctx, dynObject, product.DeliveryTimeId); @@ -212,7 +212,7 @@ private void MergeWithCombination(DataExportTaskContext ctx, dynamic dynObject, if (ctx.Projection.ConvertNetToGrossPrices) { decimal taxRate; - dynObject._OldPrice = _taxService.GetProductPrice(product, product.OldPrice, true, ctx.ContextCustomer, out taxRate); + dynObject._OldPrice = _taxService.Value.GetProductPrice(product, product.OldPrice, true, ctx.ContextCustomer, out taxRate); } else { @@ -232,7 +232,7 @@ private void MergeWithCombination(DataExportTaskContext ctx, dynamic dynObject, if (!(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) { - var specialPrice = _priceCalculationService.GetSpecialPrice(product); + var specialPrice = _priceCalculationService.Value.GetSpecialPrice(product); dynObject._SpecialPrice = ConvertPrice(ctx, product, specialPrice); @@ -248,7 +248,7 @@ private void MergeWithCombination(DataExportTaskContext ctx, dynamic dynObject, } - private List GetLocalized(DataExportTaskContext ctx, T entity, params Expression>[] keySelectors) + private List GetLocalized(DataExporterContext ctx, T entity, params Expression>[] keySelectors) where T : BaseEntity, ILocalizedEntity { if (ctx.Languages.Count <= 1) @@ -266,7 +266,7 @@ private List GetLocalized(DataExportTaskContext ctx, T entity, param // add SeName if (isSlugSupported) { - var value = _urlRecordService.GetActiveSlug(entity.Id, localeKeyGroup, language.Value.Id); + var value = _urlRecordService.Value.GetActiveSlug(entity.Id, localeKeyGroup, language.Value.Id); if (value.HasValue()) { dynamic exp = new HybridExpando(); @@ -283,7 +283,7 @@ private List GetLocalized(DataExportTaskContext ctx, T entity, param var member = keySelector.Body as MemberExpression; var propInfo = member.Member as PropertyInfo; string localeKey = propInfo.Name; - var value = _localizedEntityService.GetLocalizedValue(language.Value.Id, entity.Id, localeKeyGroup, localeKey); + var value = _localizedEntityService.Value.GetLocalizedValue(language.Value.Id, entity.Id, localeKeyGroup, localeKey); // we better not export empty values. the risk is to high that they are imported and unnecessary fill databases. if (value.HasValue()) @@ -301,7 +301,7 @@ private List GetLocalized(DataExportTaskContext ctx, T entity, param return (localized.Count == 0 ? null : localized); } - private dynamic ToDynamic(DataExportTaskContext ctx, Currency currency) + private dynamic ToDynamic(DataExporterContext ctx, Currency currency) { if (currency == null) return null; @@ -314,7 +314,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Currency currency) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Language language) + private dynamic ToDynamic(DataExporterContext ctx, Language language) { if (language == null) return null; @@ -323,7 +323,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Language language) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Country country) + private dynamic ToDynamic(DataExporterContext ctx, Country country) { if (country == null) return null; @@ -336,7 +336,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Country country) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Address address) + private dynamic ToDynamic(DataExporterContext ctx, Address address) { if (address == null) return null; @@ -362,7 +362,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Address address) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, RewardPointsHistory points) + private dynamic ToDynamic(DataExporterContext ctx, RewardPointsHistory points) { if (points == null) return null; @@ -372,7 +372,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, RewardPointsHistory points) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Customer customer) + private dynamic ToDynamic(DataExporterContext ctx, Customer customer) { if (customer == null) return null; @@ -393,7 +393,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Customer customer) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Store store) + private dynamic ToDynamic(DataExporterContext ctx, Store store) { if (store == null) return null; @@ -406,7 +406,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Store store) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, DeliveryTime deliveryTime) + private dynamic ToDynamic(DataExporterContext ctx, DeliveryTime deliveryTime) { if (deliveryTime == null) return null; @@ -419,7 +419,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, DeliveryTime deliveryTime) return result; } - private void ToDeliveryTime(DataExportTaskContext ctx, dynamic parent, int? deliveryTimeId) + private void ToDeliveryTime(DataExporterContext ctx, dynamic parent, int? deliveryTimeId) { if (ctx.DeliveryTimes != null) { @@ -430,7 +430,7 @@ private void ToDeliveryTime(DataExportTaskContext ctx, dynamic parent, int? deli } } - private dynamic ToDynamic(DataExportTaskContext ctx, QuantityUnit quantityUnit) + private dynamic ToDynamic(DataExporterContext ctx, QuantityUnit quantityUnit) { if (quantityUnit == null) return null; @@ -447,7 +447,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, QuantityUnit quantityUnit) return result; } - private void ToQuantityUnit(DataExportTaskContext ctx, dynamic parent, int? quantityUnitId) + private void ToQuantityUnit(DataExporterContext ctx, dynamic parent, int? quantityUnitId) { if (ctx.QuantityUnits != null) { @@ -458,26 +458,26 @@ private void ToQuantityUnit(DataExportTaskContext ctx, dynamic parent, int? quan } } - private dynamic ToDynamic(DataExportTaskContext ctx, Picture picture, int thumbPictureSize, int detailsPictureSize) + private dynamic ToDynamic(DataExporterContext ctx, Picture picture, int thumbPictureSize, int detailsPictureSize) { if (picture == null) return null; dynamic result = new DynamicEntity(picture); - result._ThumbImageUrl = _pictureService.GetPictureUrl(picture, thumbPictureSize, false, ctx.Store.Url); - result._ImageUrl = _pictureService.GetPictureUrl(picture, detailsPictureSize, false, ctx.Store.Url); - result._FullSizeImageUrl = _pictureService.GetPictureUrl(picture, 0, false, ctx.Store.Url); + result._ThumbImageUrl = _pictureService.Value.GetPictureUrl(picture, thumbPictureSize, false, ctx.Store.Url); + result._ImageUrl = _pictureService.Value.GetPictureUrl(picture, detailsPictureSize, false, ctx.Store.Url); + result._FullSizeImageUrl = _pictureService.Value.GetPictureUrl(picture, 0, false, ctx.Store.Url); - var relativeUrl = _pictureService.GetPictureUrl(picture); + var relativeUrl = _pictureService.Value.GetPictureUrl(picture); result._FileName = relativeUrl.Substring(relativeUrl.LastIndexOf("/") + 1); - result._ThumbLocalPath = _pictureService.GetThumbLocalPath(picture); + result._ThumbLocalPath = _pictureService.Value.GetThumbLocalPath(picture); return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, ProductVariantAttribute pva) + private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttribute pva) { if (pva == null) return null; @@ -511,7 +511,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, ProductVariantAttribute pva return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, ProductVariantAttributeCombination pvac) + private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttributeCombination pvac) { if (pvac == null) return null; @@ -524,7 +524,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, ProductVariantAttributeComb return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Manufacturer manufacturer) + private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) { if (manufacturer == null) return null; @@ -550,7 +550,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Manufacturer manufacturer) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Category category) + private dynamic ToDynamic(DataExporterContext ctx, Category category) { if (category == null) return null; @@ -580,7 +580,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Category category) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Product product) + private dynamic ToDynamic(DataExporterContext ctx, Product product) { if (product == null) return null; @@ -619,7 +619,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Product product) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Order order) + private dynamic ToDynamic(DataExporterContext ctx, Order order) { if (order == null) return null; @@ -642,7 +642,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Order order) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, OrderItem orderItem) + private dynamic ToDynamic(DataExporterContext ctx, OrderItem orderItem) { if (orderItem == null) return null; @@ -654,7 +654,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, OrderItem orderItem) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Shipment shipment) + private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) { if (shipment == null) return null; @@ -673,7 +673,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Shipment shipment) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, Discount discount) + private dynamic ToDynamic(DataExporterContext ctx, Discount discount) { if (discount == null) return null; @@ -683,7 +683,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, Discount discount) return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, ProductSpecificationAttribute psa) + private dynamic ToDynamic(DataExporterContext ctx, ProductSpecificationAttribute psa) { if (psa == null) return null; @@ -709,7 +709,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, ProductSpecificationAttribu return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, GenericAttribute genericAttribute) + private dynamic ToDynamic(DataExporterContext ctx, GenericAttribute genericAttribute) { if (genericAttribute == null) return null; @@ -719,7 +719,7 @@ private dynamic ToDynamic(DataExportTaskContext ctx, GenericAttribute genericAtt return result; } - private dynamic ToDynamic(DataExportTaskContext ctx, NewsLetterSubscription subscription) + private dynamic ToDynamic(DataExporterContext ctx, NewsLetterSubscription subscription) { if (subscription == null) return null; @@ -730,13 +730,13 @@ private dynamic ToDynamic(DataExportTaskContext ctx, NewsLetterSubscription subs } - private List Convert(DataExportTaskContext ctx, Product product) + private List Convert(DataExporterContext ctx, Product product) { var result = new List(); var languageId = (ctx.Projection.LanguageId ?? 0); var productTemplate = ctx.ProductTemplates.FirstOrDefault(x => x.Key == product.ProductTemplateId); - var pictureSize = _mediaSettings.ProductDetailsPictureSize; + var pictureSize = _mediaSettings.Value.ProductDetailsPictureSize; if (ctx.Supports(ExportFeatures.CanIncludeMainPicture) && ctx.Projection.PictureSize > 0) pictureSize = ctx.Projection.PictureSize; @@ -762,12 +762,12 @@ private List Convert(DataExportTaskContext ctx, Product product) if (ctx.Categories.Count > 0 && ctx.CategoryPathes.Count > 0) { - dynObject._CategoryPath = _categoryService.GetCategoryPath( + dynObject._CategoryPath = _categoryService.Value.GetCategoryPath( product, null, x => ctx.CategoryPathes.ContainsKey(x) ? ctx.CategoryPathes[x] : null, (id, value) => ctx.CategoryPathes[id] = value, - x => ctx.Categories.ContainsKey(x) ? ctx.Categories[x] : _categoryService.GetCategoryById(x), + x => ctx.Categories.ContainsKey(x) ? ctx.Categories[x] : _categoryService.Value.GetCategoryById(x), productCategories.OrderBy(x => x.DisplayOrder).FirstOrDefault() ); } @@ -782,7 +782,7 @@ private List Convert(DataExportTaskContext ctx, Product product) { dynamic dyn = new DynamicEntity(x); - dyn.Picture = ToDynamic(ctx, x.Picture, _mediaSettings.ProductThumbPictureSize, pictureSize); + dyn.Picture = ToDynamic(ctx, x.Picture, _mediaSettings.Value.ProductThumbPictureSize, pictureSize); return dyn; }) @@ -797,7 +797,7 @@ private List Convert(DataExportTaskContext ctx, Product product) dyn.Manufacturer = ToDynamic(ctx, x.Manufacturer); if (x.Manufacturer != null && x.Manufacturer.PictureId.HasValue) - dyn.Manufacturer.Picture = ToDynamic(ctx, x.Manufacturer.Picture, _mediaSettings.ManufacturerThumbPictureSize, _mediaSettings.ManufacturerThumbPictureSize); + dyn.Manufacturer.Picture = ToDynamic(ctx, x.Manufacturer.Picture, _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize); else dyn.Manufacturer.Picture = null; @@ -814,7 +814,7 @@ private List Convert(DataExportTaskContext ctx, Product product) dyn.Category = ToDynamic(ctx, x.Category); if (x.Category != null && x.Category.PictureId.HasValue) - dyn.Category.Picture = ToDynamic(ctx, x.Category.Picture, _mediaSettings.CategoryThumbPictureSize, _mediaSettings.CategoryThumbPictureSize); + dyn.Category.Picture = ToDynamic(ctx, x.Category.Picture, _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); if (dynObject._CategoryName == null) dynObject._CategoryName = (string)dyn.Category.Name; @@ -839,7 +839,7 @@ private List Convert(DataExportTaskContext ctx, Product product) var assignedPicture = productPictures.FirstOrDefault(y => y.PictureId == pictureId); if (assignedPicture != null && assignedPicture.Picture != null) { - assignedPictures.Add(ToDynamic(ctx, assignedPicture.Picture, _mediaSettings.ProductThumbPictureSize, pictureSize)); + assignedPictures.Add(ToDynamic(ctx, assignedPicture.Picture, _mediaSettings.Value.ProductThumbPictureSize, pictureSize)); } } @@ -934,9 +934,9 @@ private List Convert(DataExportTaskContext ctx, Product product) if (ctx.Supports(ExportFeatures.CanIncludeMainPicture)) { if (productPictures != null && productPictures.Any()) - dynObject._MainPictureUrl = _pictureService.GetPictureUrl(productPictures.First().Picture, ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); + dynObject._MainPictureUrl = _pictureService.Value.GetPictureUrl(productPictures.First().Picture, ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); else - dynObject._MainPictureUrl = _pictureService.GetDefaultPictureUrl(ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); + dynObject._MainPictureUrl = _pictureService.Value.GetDefaultPictureUrl(ctx.Projection.PictureSize, storeLocation: ctx.Store.Url); } #endregion @@ -975,7 +975,7 @@ private List Convert(DataExportTaskContext ctx, Product product) return result; } - private List Convert(DataExportTaskContext ctx, Order order) + private List Convert(DataExporterContext ctx, Order order) { var result = new List(); @@ -1025,8 +1025,8 @@ private List Convert(DataExportTaskContext ctx, Order order) dyn.Product._ProductTemplateViewPath = (productTemplate.Value == null ? "" : productTemplate.Value.ViewPath); - dyn.Product._BasePriceInfo = e.Product.GetBasePriceInfo(_services.Localization, _priceFormatter, _currencyService, _taxService, - _priceCalculationService, ctx.ContextCurrency, decimal.Zero, true); + dyn.Product._BasePriceInfo = e.Product.GetBasePriceInfo(_services.Localization, _priceFormatter.Value, _currencyService.Value, _taxService.Value, + _priceCalculationService.Value, ctx.ContextCurrency, decimal.Zero, true); ToDeliveryTime(ctx, dyn.Product, e.Product.DeliveryTimeId); ToQuantityUnit(ctx, dyn.Product, e.Product.QuantityUnitId); @@ -1044,7 +1044,7 @@ private List Convert(DataExportTaskContext ctx, Order order) return result; } - private List Convert(DataExportTaskContext ctx, Manufacturer manufacturer) + private List Convert(DataExporterContext ctx, Manufacturer manufacturer) { var result = new List(); @@ -1057,7 +1057,7 @@ private List Convert(DataExportTaskContext ctx, Manufacturer manufactur var pictures = ctx.ManufacturerExportContext.Pictures.Load(manufacturer.PictureId.Value); if (pictures.Count > 0) - dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.ManufacturerThumbPictureSize, _mediaSettings.ManufacturerThumbPictureSize); + dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize); } dynObject.ProductManufacturers = productManufacturers @@ -1075,7 +1075,7 @@ private List Convert(DataExportTaskContext ctx, Manufacturer manufactur return result; } - private List Convert(DataExportTaskContext ctx, Category category) + private List Convert(DataExporterContext ctx, Category category) { var result = new List(); @@ -1088,7 +1088,7 @@ private List Convert(DataExportTaskContext ctx, Category category) var pictures = ctx.CategoryExportContext.Pictures.Load(category.PictureId.Value); if (pictures.Count > 0) - dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.CategoryThumbPictureSize, _mediaSettings.CategoryThumbPictureSize); + dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); } dynObject.ProductCategories = productCategories @@ -1106,7 +1106,7 @@ private List Convert(DataExportTaskContext ctx, Category category) return result; } - private List Convert(DataExportTaskContext ctx, Customer customer) + private List Convert(DataExporterContext ctx, Customer customer) { var result = new List(); @@ -1142,7 +1142,7 @@ private List Convert(DataExportTaskContext ctx, Customer customer) return result; } - private List Convert(DataExportTaskContext ctx, NewsLetterSubscription subscription) + private List Convert(DataExporterContext ctx, NewsLetterSubscription subscription) { var result = new List(); diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 0e67aca232..83fe5c10e4 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -169,6 +169,7 @@ + From bca184d4728c09f28e475242bd2b251e6d9c66ca Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 11 Nov 2015 14:40:04 +0100 Subject: [PATCH 063/732] Further IDataExporter implementation --- .../DataExchange/Internal/DataExporter.cs | 184 ++++++++++++------ .../Internal/DataExporterContext.cs | 35 ++-- 2 files changed, 140 insertions(+), 79 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs index 391b210db6..37d64d5fbb 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs @@ -29,7 +29,6 @@ using SmartStore.Services.Orders; using SmartStore.Services.Seo; using SmartStore.Services.Shipping; -using SmartStore.Services.Tasks; using SmartStore.Services.Tax; using SmartStore.Utilities; using SmartStore.Utilities.Threading; @@ -158,8 +157,8 @@ private void SetProgress(DataExporterContext ctx, int loadedRecords) { int totalRecords = ctx.RecordsPerStore.Sum(x => x.Value); - if (ctx.Profile.Limit > 0 && totalRecords > ctx.Profile.Limit) - totalRecords = ctx.Profile.Limit; + if (ctx.Request.Profile.Limit > 0 && totalRecords > ctx.Request.Profile.Limit) + totalRecords = ctx.Request.Profile.Limit; ctx.RecordCount = Math.Min(ctx.RecordCount + loadedRecords, totalRecords); @@ -179,15 +178,15 @@ private void SetProgress(DataExporterContext ctx, string message) private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx, int pageIndex = 0) { - var offset = ctx.Profile.Offset + (pageIndex * PageSize); + var offset = ctx.Request.Profile.Offset + (pageIndex * PageSize); - var limit = (ctx.IsPreview ? PageSize : ctx.Profile.Limit); + var limit = (ctx.IsPreview ? PageSize : ctx.Request.Profile.Limit); - var recordsPerSegment = (ctx.IsPreview ? 0 : ctx.Profile.BatchSize); + var recordsPerSegment = (ctx.IsPreview ? 0 : ctx.Request.Profile.BatchSize); - var totalCount = ctx.Profile.Offset + ctx.RecordsPerStore.First(x => x.Key == ctx.Store.Id).Value; + var totalCount = ctx.Request.Profile.Offset + ctx.RecordsPerStore.First(x => x.Key == ctx.Store.Id).Value; - switch (ctx.Provider.Value.EntityType) + switch (ctx.Request.Provider.Value.EntityType) { case ExportEntityType.Product: ctx.ExecuteContext.Segmenter = new ExportDataSegmenter @@ -311,11 +310,11 @@ private bool CallProvider(DataExporterContext ctx, string streamId, string metho { if (method == "Execute") { - ctx.Provider.Value.Execute(ctx.ExecuteContext); + ctx.Request.Provider.Value.Execute(ctx.ExecuteContext); } else if (method == "OnExecuted") { - ctx.Provider.Value.OnExecuted(ctx.ExecuteContext); + ctx.Request.Provider.Value.OnExecuted(ctx.ExecuteContext); } if (ctx.IsFileBasedExport && path.HasValue()) @@ -355,17 +354,17 @@ private bool CallProvider(DataExporterContext ctx, string streamId, string metho private void SendCompletionEmail(DataExporterContext ctx) { - var emailAccount = _emailAccountService.Value.GetEmailAccountById(ctx.Profile.EmailAccountId); + var emailAccount = _emailAccountService.Value.GetEmailAccountById(ctx.Request.Profile.EmailAccountId); var smtpContext = new SmtpContext(emailAccount); var message = new EmailMessage(); var storeInfo = "{0} ({1})".FormatInvariant(ctx.Store.Name, ctx.Store.Url); - message.To.AddRange(ctx.Profile.CompletedEmailAddresses.SplitSafe(",").Where(x => x.IsEmail()).Select(x => new EmailAddress(x))); + message.To.AddRange(ctx.Request.Profile.CompletedEmailAddresses.SplitSafe(",").Where(x => x.IsEmail()).Select(x => new EmailAddress(x))); message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); message.Subject = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Subject", ctx.Projection.LanguageId ?? 0) - .FormatInvariant(ctx.Profile.Name); + .FormatInvariant(ctx.Request.Profile.Name); message.Body = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Body", ctx.Projection.LanguageId ?? 0) .FormatInvariant(storeInfo); @@ -387,7 +386,7 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int skip, i { OrderBy = ProductSortingEnum.CreatedOn, ProductIds = ctx.EntityIdsSelected, - StoreId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId), + StoreId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId), VisibleIndividuallyOnly = true, PriceMin = ctx.Filter.PriceMinimum, PriceMax = ctx.Filter.PriceMaximum, @@ -454,7 +453,7 @@ private List GetProducts(DataExporterContext ctx, int skip) { OrderBy = ProductSortingEnum.CreatedOn, PageSize = int.MaxValue, - StoreId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId), + StoreId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId), VisibleIndividuallyOnly = false, ParentGroupedProductId = product.Id }; @@ -485,7 +484,7 @@ private List GetProducts(DataExporterContext ctx, int skip) private IQueryable GetOrderQuery(DataExporterContext ctx, int skip, int take) { var query = _orderService.Value.GetOrders( - ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, + ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, ctx.Projection.CustomerId ?? 0, ctx.Filter.CreatedFrom.HasValue ? (DateTime?)_dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _dateTimeHelper.Value.CurrentTimeZone) : null, ctx.Filter.CreatedTo.HasValue ? (DateTime?)_dateTimeHelper.Value.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _dateTimeHelper.Value.CurrentTimeZone) : null, @@ -536,7 +535,7 @@ private List GetOrders(DataExporterContext ctx, int skip) private IQueryable GetManufacturerQuery(DataExporterContext ctx, int skip, int take) { var showHidden = !ctx.Filter.IsPublished.HasValue; - var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + var storeId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); var query = _manufacturerService.Value.GetManufacturers(showHidden, storeId); @@ -569,7 +568,7 @@ private List GetManufacturers(DataExporterContext ctx, int skip) private IQueryable GetCategoryQuery(DataExporterContext ctx, int skip, int take) { var showHidden = !ctx.Filter.IsPublished.HasValue; - var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + var storeId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); var query = _categoryService.Value.GetCategories(null, showHidden, null, true, storeId); @@ -644,7 +643,7 @@ private List GetCustomers(DataExporterContext ctx, int skip) private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int skip, int take) { - var storeId = (ctx.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + var storeId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); var query = _subscriptionRepository.Value.TableUntracked; @@ -706,7 +705,7 @@ private List Init(DataExporterContext ctx, int? totalRecords = null) ctx.Stores = _services.StoreService.GetAllStores().ToDictionary(x => x.Id, x => x); ctx.Languages = _languageService.Value.GetAllLanguages(true).ToDictionary(x => x.Id, x => x); - if (!ctx.IsPreview && ctx.Profile.PerStore) + if (!ctx.IsPreview && ctx.Request.Profile.PerStore) { result = new List(ctx.Stores.Values.Where(x => x.Id == ctx.Filter.StoreId || ctx.Filter.StoreId == 0)); } @@ -732,25 +731,25 @@ private List Init(DataExporterContext ctx, int? totalRecords = null) } else { - switch (ctx.Provider.Value.EntityType) + switch (ctx.Request.Provider.Value.EntityType) { case ExportEntityType.Product: - totalCount = GetProductQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetProductQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; case ExportEntityType.Order: - totalCount = GetOrderQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetOrderQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; case ExportEntityType.Manufacturer: - totalCount = GetManufacturerQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetManufacturerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; case ExportEntityType.Category: - totalCount = GetCategoryQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetCategoryQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; case ExportEntityType.Customer: - totalCount = GetCustomerQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetCustomerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; case ExportEntityType.NewsLetterSubscription: - totalCount = GetNewsLetterSubscriptionQuery(ctx, ctx.Profile.Offset, int.MaxValue).Count(); + totalCount = GetNewsLetterSubscriptionQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); break; } } @@ -775,18 +774,18 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) logHead.AppendLine(); logHead.AppendLine(new string('-', 40)); logHead.AppendLine("SmartStore.NET:\t\tv." + SmartStoreVersion.CurrentFullVersion); - logHead.Append("Export profile:\t\t" + ctx.Profile.Name); - logHead.AppendLine(ctx.Profile.Id == 0 ? " (volatile)" : " (Id {0})".FormatInvariant(ctx.Profile.Id)); + logHead.Append("Export profile:\t\t" + ctx.Request.Profile.Name); + logHead.AppendLine(ctx.Request.Profile.Id == 0 ? " (volatile)" : " (Id {0})".FormatInvariant(ctx.Request.Profile.Id)); - logHead.AppendLine("Export provider:\t{0} ({1})".FormatInvariant(ctx.Provider.Metadata.FriendlyName, ctx.Profile.ProviderSystemName)); + logHead.AppendLine("Export provider:\t{0} ({1})".FormatInvariant(ctx.Request.Provider.Metadata.FriendlyName, ctx.Request.Profile.ProviderSystemName)); - var plugin = ctx.Provider.Metadata.PluginDescriptor; + var plugin = ctx.Request.Provider.Metadata.PluginDescriptor; logHead.Append("Plugin:\t\t\t\t"); logHead.AppendLine(plugin == null ? "".NaIfEmpty() : "{0} ({1}) v.{2}".FormatInvariant(plugin.FriendlyName, plugin.SystemName, plugin.Version.ToString())); - logHead.AppendLine("Entity:\t\t\t\t" + ctx.Provider.Value.EntityType.ToString()); + logHead.AppendLine("Entity:\t\t\t\t" + ctx.Request.Provider.Value.EntityType.ToString()); - var storeInfo = (ctx.Profile.PerStore ? "{0} (Id {1})".FormatInvariant(ctx.Store.Name, ctx.Store.Id) : "All stores"); + var storeInfo = (ctx.Request.Profile.PerStore ? "{0} (Id {1})".FormatInvariant(ctx.Store.Name, ctx.Store.Id) : "All stores"); logHead.Append("Store:\t\t\t\t" + storeInfo); ctx.Log.Information(logHead.ToString()); @@ -796,18 +795,18 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) ctx.ExecuteContext.MaxFileNameLength = _dataExchangeSettings.Value.MaxFileNameLength; - ctx.ExecuteContext.HasPublicDeployment = ctx.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); + ctx.ExecuteContext.HasPublicDeployment = ctx.Request.Profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem); ctx.ExecuteContext.PublicFolderPath = (ctx.ExecuteContext.HasPublicDeployment ? Path.Combine(HttpRuntime.AppDomainAppPath, PublicFolder) : null); - var fileExtension = (ctx.Provider.Value.FileExtension.HasValue() ? ctx.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); + var fileExtension = (ctx.Request.Provider.Value.FileExtension.HasValue() ? ctx.Request.Provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""); using (var segmenter = CreateSegmenter(ctx)) { if (segmenter == null) { - throw new SmartException("Unsupported entity type '{0}'".FormatInvariant(ctx.Provider.Value.EntityType.ToString())); + throw new SmartException("Unsupported entity type '{0}'".FormatInvariant(ctx.Request.Provider.Value.EntityType.ToString())); } if (segmenter.TotalRecords <= 0) @@ -824,7 +823,7 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) if (ctx.IsFileBasedExport) { - var resolvedPattern = ctx.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); + var resolvedPattern = ctx.Request.Profile.ResolveFileNamePattern(ctx.Store, ++fileIndex, ctx.ExecuteContext.MaxFileNameLength); ctx.ExecuteContext.FileName = resolvedPattern + fileExtension; path = Path.Combine(ctx.ExecuteContext.Folder, ctx.ExecuteContext.FileName); @@ -875,7 +874,7 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) private void ExportCoreOuter(DataExporterContext ctx) { - if (ctx.Profile == null || !ctx.Profile.Enabled) + if (ctx.Request.Profile == null || !ctx.Request.Profile.Enabled) return; FileSystemHelper.Delete(ctx.LogPath); @@ -886,7 +885,7 @@ private void ExportCoreOuter(DataExporterContext ctx) { try { - if (!ctx.Provider.IsValid()) + if (!ctx.Request.Provider.IsValid()) { throw new SmartException("Export aborted because the export provider is not valid"); } @@ -895,12 +894,12 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.ExecuteContext.Log = logger; ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); - if (ctx.Profile.ProviderConfigData.HasValue()) + if (ctx.Request.Profile.ProviderConfigData.HasValue()) { - var configInfo = ctx.Provider.Value.ConfigurationInfo; + var configInfo = ctx.Request.Provider.Value.ConfigurationInfo; if (configInfo != null) { - ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(ctx.Profile.ProviderConfigData, configInfo.ModelType); + ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(ctx.Request.Profile.ProviderConfigData, configInfo.ModelType); } } @@ -911,18 +910,18 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id); ctx.ProductTemplates = _productTemplateService.Value.GetAllProductTemplates().ToDictionary(x => x.Id); - if (ctx.Provider.Value.EntityType == ExportEntityType.Product) + if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Product) { var allCategories = _categoryService.Value.GetAllCategories(showHidden: true, applyNavigationFilters: false); ctx.Categories = allCategories.ToDictionary(x => x.Id); } - if (ctx.Provider.Value.EntityType == ExportEntityType.Order) + if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Order) { ctx.Countries = _countryService.Value.GetAllCountries(true).ToDictionary(x => x.Id, x => x); } - if (ctx.Provider.Value.EntityType == ExportEntityType.Customer) + if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Customer) { var subscriptionEmails = _subscriptionRepository.Value.TableUntracked .Where(x => x.Active) @@ -946,7 +945,7 @@ private void ExportCoreOuter(DataExporterContext ctx) { if (ctx.IsFileBasedExport) { - if (ctx.Profile.CreateZipArchive || ctx.Profile.Deployments.Any(x => x.Enabled && x.CreateZip)) + if (ctx.Request.Profile.CreateZipArchive || ctx.Request.Profile.Deployments.Any(x => x.Enabled && x.CreateZip)) { ZipFile.CreateFromDirectory(ctx.FolderContent, ctx.ZipPath, CompressionLevel.Fastest, true); } @@ -954,7 +953,7 @@ private void ExportCoreOuter(DataExporterContext ctx) SetProgress(ctx, T("Common.Deployment")); // TODO: deployment - //foreach (var deployment in ctx.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled)) + //foreach (var deployment in ctx.Request.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled)) //{ // try // { @@ -982,7 +981,7 @@ private void ExportCoreOuter(DataExporterContext ctx) //} } - if (ctx.Profile.EmailAccountId != 0 && ctx.Profile.CompletedEmailAddresses.HasValue()) + if (ctx.Request.Profile.EmailAccountId != 0 && ctx.Request.Profile.CompletedEmailAddresses.HasValue()) { SendCompletionEmail(ctx); } @@ -997,18 +996,18 @@ private void ExportCoreOuter(DataExporterContext ctx) { try { - if (!ctx.IsPreview && ctx.Profile.Id != 0) + if (!ctx.IsPreview && ctx.Request.Profile.Id != 0) { - ctx.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); + ctx.Request.Profile.ResultInfo = XmlHelper.Serialize(ctx.Result); - _exportProfileService.Value.UpdateExportProfile(ctx.Profile); + _exportProfileService.Value.UpdateExportProfile(ctx.Request.Profile); } } catch { } try { - if (ctx.IsFileBasedExport && ctx.ExecuteContext.Abort != ExportAbortion.Hard && ctx.Profile.Cleanup) + if (ctx.IsFileBasedExport && ctx.ExecuteContext.Abort != ExportAbortion.Hard && ctx.Request.Profile.Cleanup) { FileSystemHelper.ClearDirectory(ctx.FolderContent, false); } @@ -1059,17 +1058,92 @@ public static int PageSize public DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken) { - return null; + var ctx = new DataExporterContext(request, cancellationToken); + + ExportCoreOuter(ctx); + + if (ctx.Result != null && ctx.Result.Succeeded && ctx.Result.Files.Count > 0) + { + string prefix = null; + string suffix = null; + var extension = Path.GetExtension(ctx.Result.Files.First().FileName); + var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count()); + + if (request.Provider.Value.EntityType == ExportEntityType.Product) + prefix = T("Admin.Catalog.Products"); + else if (request.Provider.Value.EntityType == ExportEntityType.Order) + prefix = T("Admin.Orders"); + else if (request.Provider.Value.EntityType == ExportEntityType.Category) + prefix = T("Admin.Catalog.Categories"); + else if (request.Provider.Value.EntityType == ExportEntityType.Manufacturer) + prefix = T("Admin.Catalog.Manufacturers"); + else if (request.Provider.Value.EntityType == ExportEntityType.Customer) + prefix = T("Admin.Customers"); + else if (request.Provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) + prefix = T("Admin.Promotions.NewsLetterSubscriptions"); + else + prefix = request.Provider.Value.EntityType.ToString(); + + if (selectedEntityCount == 0) + suffix = T("Common.All"); + else + suffix = (selectedEntityCount == 1 ? request.EntitiesToExport.First().ToString() : T("Admin.Common.Selected").Text); + + ctx.Result.DownloadFileName = string.Concat(prefix, "-", suffix).ToLower().ToValidFileName() + extension; + } + + return ctx.Result; } public IList Preview(DataExportRequest request) { - return null; + var pageIndex = (request.CustomData.ContainsKey("PageIndex") ? (int)request.CustomData["PageIndex"] : 0); + var totalRecords = (request.CustomData.ContainsKey("TotalRecords") ? (int)request.CustomData["TotalRecords"] : 0); + + var result = new List(); + var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); + + var ctx = new DataExporterContext(request, cancellation.Token, null, true); + + var unused = Init(ctx, totalRecords); + + using (var segmenter = CreateSegmenter(ctx, pageIndex)) + { + if (segmenter == null) + { + throw new SmartException("Unsupported entity type '{0}'".FormatInvariant(ctx.Request.Provider.Value.EntityType.ToString())); + } + + while (segmenter.HasData) + { + segmenter.RecordPerSegmentCount = 0; + + while (segmenter.ReadNextSegment()) + { + result.AddRange(segmenter.CurrentSegment); + } + } + } + + if (ctx.Result.LastError.HasValue()) + { + _services.Notifier.Error(ctx.Result.LastError); + } + + return result; } public long GetDataCount(DataExportRequest request) { - return 0; + var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); + + var ctx = new DataExporterContext(request, cancellation.Token, null, true); + + var unused = Init(ctx); + + var totalCount = ctx.RecordsPerStore.First().Value; + + return totalCount; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index cb95e67f23..2ae610ca6d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -12,7 +11,6 @@ using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Logging; -using SmartStore.Core.Plugins; using SmartStore.Utilities; namespace SmartStore.Services.DataExchange.Internal @@ -28,21 +26,17 @@ internal class DataExporterContext public DataExporterContext( DataExportRequest request, CancellationToken cancellationToken, - ExportProfile profile, - Provider provider, string selectedIds = null, - Action previewData = null) + bool isPreview = false) { Request = request; CancellationToken = cancellationToken; - Profile = profile; - Provider = provider; - Filter = XmlHelper.Deserialize(profile.Filtering); - Projection = XmlHelper.Deserialize(profile.Projection); + Filter = XmlHelper.Deserialize(request.Profile.Filtering); + Projection = XmlHelper.Deserialize(request.Profile.Projection); EntityIdsSelected = selectedIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); - PreviewData = previewData; + IsPreview = isPreview; - FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(profile.FolderName)); + FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(request.Profile.FolderName)); FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; Categories = new Dictionary(); @@ -60,7 +54,7 @@ public DataExporterContext( }; ExecuteContext = new ExportExecuteContext(Result, CancellationToken, FolderContent); - ExecuteContext.Projection = XmlHelper.Deserialize(profile.Projection); + ExecuteContext.Projection = XmlHelper.Deserialize(request.Profile.Projection); } public List EntityIdsSelected { get; private set; } @@ -71,20 +65,13 @@ public DataExporterContext( public string ProgressInfo { get; set; } public IQueryable QueryProducts { get; set; } - public Action PreviewData { get; private set; } - public bool IsPreview - { - get { return PreviewData != null; } - } - public DataExportRequest Request { get; private set; } public CancellationToken CancellationToken { get; private set; } - public ExportProfile Profile { get; private set; } - public Provider Provider { get; private set; } + public bool IsPreview { get; private set; } public bool Supports(ExportFeatures feature) { - return (!IsPreview && Provider.Metadata.ExportFeatures.HasFlag(feature)); + return (!IsPreview && Request.Provider.Metadata.ExportFeatures.HasFlag(feature)); } public ExportFilter Filter { get; private set; } @@ -100,7 +87,7 @@ public bool Supports(ExportFeatures feature) public string FolderContent { get; private set; } public string ZipName { - get { return Profile.FolderName + ".zip"; } + get { return Request.Profile.FolderName + ".zip"; } } public string ZipPath { @@ -113,7 +100,7 @@ public string LogPath public bool IsFileBasedExport { - get { return Provider == null || Provider.Value == null || Provider.Value.FileExtension.HasValue(); } + get { return Request.Provider == null || Request.Provider.Value == null || Request.Provider.Value.FileExtension.HasValue(); } } public string[] GetDeploymentFiles(ExportDeployment deployment) { From fadd6dda9cf31a871c27289ef1f2b78951c6076a Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 11 Nov 2015 17:26:08 +0100 Subject: [PATCH 064/732] ExportController using DataExporter --- .../DataExchange/ExportProfileService.cs | 3 +- .../DataExchange/IDataExporter.cs | 5 +- .../DataExchange/Internal/DataExportTask.cs | 1 + .../DataExchange/Internal/DataExporter.cs | 13 +-- .../DependencyRegistrar.cs | 4 +- .../Controllers/ExportController.cs | 87 ++++++++++--------- 6 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index 1651171aa2..414e5c073a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -10,6 +10,7 @@ using SmartStore.Core.Events; using SmartStore.Core.Plugins; using SmartStore.Services.DataExchange.ExportTask; +using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Tasks; using SmartStore.Utilities; @@ -91,7 +92,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi task = new ScheduleTask { CronExpression = "0 */6 * * *", // every six hours - Type = (new ExportProfileTask()).GetType().AssemblyQualifiedNameWithoutVersion(), + Type = typeof(DataExportTask).AssemblyQualifiedNameWithoutVersion(), Enabled = false, StopOnError = false, IsHidden = true diff --git a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs index 4a96e5c515..843a15351e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs @@ -13,12 +13,9 @@ public interface IDataExporter { DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); - // Handle model conversion for grid in backend's controller IList Preview(DataExportRequest request); - // useful for decision making whether export should - // be processed sync or async - long GetDataCount(DataExportRequest request); + int GetDataCount(DataExportRequest request); } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs index b1e3d31f54..33b2eb1e2a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs @@ -4,6 +4,7 @@ namespace SmartStore.Services.DataExchange.Internal { + // note: namespace persisted in ScheduleTask.Type public partial class DataExportTask : ITask { private readonly IDataExporter _exporter; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs index 37d64d5fbb..faccb9748f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs @@ -903,8 +903,9 @@ private void ExportCoreOuter(DataExporterContext ctx) } } - // TODO: lazyLoading: false. It requires IDbContextExtensions::Load/QueryFor for properties internally mapped by EF - using (var scope = new DbContextScope(_services.DbContext, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, forceNoTracking: true)) + // TODO: lazyLoading: false, proxyCreation: false possible? how to identify all properties of all data levels of all entities + // that require manual resolving for now and for future? fragile, susceptible to faults (e.g. price calculation)... + using (var scope = new DbContextScope(_services.DbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) { ctx.DeliveryTimes = _deliveryTimeService.Value.GetAllDeliveryTimes().ToDictionary(x => x.Id); ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id); @@ -1100,7 +1101,7 @@ public IList Preview(DataExportRequest request) var pageIndex = (request.CustomData.ContainsKey("PageIndex") ? (int)request.CustomData["PageIndex"] : 0); var totalRecords = (request.CustomData.ContainsKey("TotalRecords") ? (int)request.CustomData["TotalRecords"] : 0); - var result = new List(); + var resultData = new List(); var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); var ctx = new DataExporterContext(request, cancellation.Token, null, true); @@ -1120,7 +1121,7 @@ public IList Preview(DataExportRequest request) while (segmenter.ReadNextSegment()) { - result.AddRange(segmenter.CurrentSegment); + resultData.AddRange(segmenter.CurrentSegment); } } } @@ -1130,10 +1131,10 @@ public IList Preview(DataExportRequest request) _services.Notifier.Error(ctx.Result.LastError); } - return result; + return resultData; } - public long GetDataCount(DataExportRequest request) + public int GetDataCount(DataExportRequest request) { var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 7eea57921e..574f649606 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -76,6 +76,7 @@ using Module = Autofac.Module; using SmartStore.Core.Domain.DataExchange; using SmartStore.Utilities.Reflection; +using SmartStore.Services.DataExchange.Internal; namespace SmartStore.Web.Framework { @@ -223,8 +224,9 @@ public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, b builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index e53c712192..55ac7330ac 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -2,11 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net.Mime; -using System.Text; using System.Web; using System.Web.Mvc; -using Autofac; using SmartStore.Admin.Extensions; using SmartStore.Admin.Models.DataExchange; using SmartStore.Core; @@ -21,12 +18,13 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Customers; using SmartStore.Services.DataExchange; -using SmartStore.Services.DataExchange.ExportTask; +using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; using SmartStore.Services.Messages; using SmartStore.Services.Security; +using SmartStore.Services.Tasks; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Plugins; @@ -48,9 +46,10 @@ public class ExportController : AdminControllerBase private readonly ILanguageService _languageService; private readonly ICurrencyService _currencyService; private readonly IEmailAccountService _emailAccountService; - private readonly IComponentContext _componentContext; private readonly IDateTimeHelper _dateTimeHelper; private readonly DataExchangeSettings _dataExchangeSettings; + private readonly ITaskScheduler _taskScheduler; + private readonly IDataExporter _dataExporter; public ExportController( ICommonServices services, @@ -63,9 +62,10 @@ public ExportController( ILanguageService languageService, ICurrencyService currencyService, IEmailAccountService emailAccountService, - IComponentContext componentContext, IDateTimeHelper dateTimeHelper, - DataExchangeSettings dataExchangeSettings) + DataExchangeSettings dataExchangeSettings, + ITaskScheduler taskScheduler, + IDataExporter dataExporter) { _services = services; _exportService = exportService; @@ -77,9 +77,10 @@ public ExportController( _languageService = languageService; _currencyService = currencyService; _emailAccountService = emailAccountService; - _componentContext = componentContext; _dateTimeHelper = dateTimeHelper; _dataExchangeSettings = dataExchangeSettings; + _taskScheduler = taskScheduler; + _dataExporter = dataExporter; } #region Utilities @@ -245,7 +246,7 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile { try { - var publicFolder = Path.Combine(HttpRuntime.AppDomainAppPath, ExportProfileTask.PublicFolder); + var publicFolder = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder); var resultInfo = XmlHelper.Deserialize(profile.ResultInfo); if (resultInfo != null && resultInfo.Files != null) @@ -261,7 +262,7 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile StoreId = store.Id, StoreName = store.Name, FileName = fileInfo.FileName, - FileUrl = string.Concat(store.Url.EnsureEndsWith("/"), ExportProfileTask.PublicFolder.EnsureEndsWith("/"), fileInfo.FileName) + FileUrl = string.Concat(store.Url.EnsureEndsWith("/"), DataExporter.PublicFolder.EnsureEndsWith("/"), fileInfo.FileName) }); } } @@ -722,15 +723,15 @@ public ActionResult Preview(int id) var provider = _exportService.LoadProvider(profile.ProviderSystemName); - var task = new ExportProfileTask(); - var totalRecords = task.GetRecordCount(profile, provider, _componentContext); + var request = new DataExportRequest(profile, provider); + var totalRecords = _dataExporter.GetDataCount(request); var model = new ExportPreviewModel { Id = profile.Id, Name = profile.Name, ThumbnailUrl = GetThumbnailUrl(provider), - GridPageSize = ExportProfileTask.PageSize, + GridPageSize = DataExporter.PageSize, EntityType = provider.Value.EntityType, TotalRecords = totalRecords, LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()) @@ -751,25 +752,30 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) { var productModel = new List(); var orderModel = new List(); - var task = new ExportProfileTask(); - Action previewData = x => + var request = new DataExportRequest(profile, provider); + request.CustomData.Add("PageIndex", command.Page - 1); + request.CustomData.Add("TotalRecords", totalRecords); + + var data = _dataExporter.Preview(request); + + foreach (dynamic item in data) { if (provider.Value.EntityType == ExportEntityType.Product) { - var product = x.Entity as Product; + var product = item.Entity as Product; var pm = new ExportPreviewProductModel { - Id = x.Id, - ProductTypeId = x.ProductTypeId, + Id = product.Id, + ProductTypeId = product.ProductTypeId, ProductTypeName = product.GetProductTypeLabel(_services.Localization), ProductTypeLabelHint = product.ProductTypeLabelHint, - Name = x.Name, - Sku = x.Sku, - Price = x.Price, - Published = x.Published, - StockQuantity = x.StockQuantity, - AdminComment = x.AdminComment + Name = item.Name, + Sku = item.Sku, + Price = item.Price, + Published = product.Published, + StockQuantity = product.StockQuantity, + AdminComment = item.AdminComment }; productModel.Add(pm); @@ -778,23 +784,21 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) { var om = new ExportPreviewOrderModel { - Id = x.Id, - HasNewPaymentNotification = x.HasNewPaymentNotification, - OrderNumber = x.OrderNumber, - OrderStatus = x.OrderStatus, - PaymentStatus = x.PaymentStatus, - ShippingStatus = x.ShippingStatus, - CustomerEmail = x.Customer.Email, - StoreName = (x.Store == null ? "".NaIfEmpty() : x.Store.Name), - CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), - OrderTotal = x.OrderTotal + Id = item.Id, + HasNewPaymentNotification = item.HasNewPaymentNotification, + OrderNumber = item.OrderNumber, + OrderStatus = item.OrderStatus, + PaymentStatus = item.PaymentStatus, + ShippingStatus = item.ShippingStatus, + CustomerEmail = item.Customer.Email, + StoreName = (item.Store == null ? "".NaIfEmpty() : item.Store.Name), + CreatedOn = _dateTimeHelper.ConvertToUserTime(item.CreatedOnUtc, DateTimeKind.Utc), + OrderTotal = item.OrderTotal }; orderModel.Add(om); } - }; - - task.Preview(profile, provider, _componentContext, command.Page - 1, totalRecords, previewData); + } var normalizedTotal = totalRecords; @@ -831,12 +835,15 @@ public ActionResult Execute(int id, string selectedIds, bool exportAll) if (profile == null) return RedirectToAction("List"); + // TODO: profile.CacheSelectedEntityIds(selectedIds); - var returnUrl = Url.Action("List", "Export", new { area = "admin" }); + _taskScheduler.RunSingleTask(profile.SchedulingTaskId); - return RedirectToAction("RunJob", "ScheduleTask", new { area = "admin", id = profile.SchedulingTaskId, returnUrl = returnUrl }); - } + NotifyInfo(T("Admin.System.ScheduleTasks.RunNow.Progress")); + + return RedirectToAction("List"); + } [ChildActionOnly] public ActionResult InfoProfile(string systemName, string returnUrl) From b93c2ef966a51d88071244126eb4a3ff37c848ea Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 11 Nov 2015 17:44:44 +0100 Subject: [PATCH 065/732] Fix #808: Removed C# 6 features to remain compatible with Visual Studio 2013 --- .../Extensions/StringExtensions.cs | 2 +- .../Extensions/TypeExtensions.cs | 1 - .../Infrastructure/AppDomainTypeFinder.cs | 2 +- .../DependencyManagement/ContainerManager.cs | 70 +++++++--- .../Utilities/Reflection/FastActivator.cs | 127 +++++++++++++++++- .../Utilities/Reflection/FastProperty.cs | 49 +++---- .../SmartStore.Core.Tests/PerformanceTests.cs | 90 ++++++++----- .../Customer/RegisterValidatorTests.cs | 2 +- 8 files changed, 261 insertions(+), 82 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs index 1fca421bad..4a7f28e209 100644 --- a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs @@ -511,7 +511,7 @@ public static string EncodeJsString(this string value) [DebuggerStepThrough] public static string EncodeJsString(this string value, char delimiter, bool appendDelimiters) { - StringBuilder sb = new StringBuilder(value?.Length ?? 16); + StringBuilder sb = new StringBuilder(value != null ? value.Length : 16); using (StringWriter w = new StringWriter(sb, CultureInfo.InvariantCulture)) { EncodeJsString(w, value, delimiter, appendDelimiters); diff --git a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs index 981d71d9f8..987938eab3 100644 --- a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs @@ -7,7 +7,6 @@ namespace SmartStore { - public static class TypeExtensions { private static Type[] s_predefinedTypes; diff --git a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs index 8e2e1b1498..7c5169afac 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs @@ -21,7 +21,7 @@ public class AppDomainTypeFinder : ITypeFinder private static object s_lock = new object(); - private string _assemblySkipLoadingPattern = @"^System|^mscorlib|^Microsoft|^CppCodeProvider|^VJSharpCodeProvider|^WebDev|^Nuget|^Castle|^Iesi|^log4net|^Autofac|^AutoMapper|^EntityFramework|^EPPlus|^Fasterflect|^nunit|^TestDriven|^MbUnit|^Rhino|^QuickGraph|^TestFu|^Telerik|^Antlr3|^Recaptcha|^FluentValidation|^ImageResizer|^itextsharp|^MiniProfiler|^Newtonsoft|^Pandora|^WebGrease|^Noesis|^DotNetOpenAuth|^Facebook|^LinqToTwitter|^PerceptiveMCAPI|^CookComputing|^GCheckout|^Mono\.Math|^Org\.Mentalis|^App_Web|^BundleTransformer|^ClearScript|^JavaScriptEngineSwitcher|^MsieJavaScriptEngine|^Glimpse|^Ionic|^App_GlobalResources|^AjaxMin|^MaxMind|^NReco|^OffAmazonPayments|^UAParser"; + private string _assemblySkipLoadingPattern = @"^System|^mscorlib|^Microsoft|^CppCodeProvider|^VJSharpCodeProvider|^WebDev|^Nuget|^Castle|^Iesi|^log4net|^Autofac|^AutoMapper|^EntityFramework|^EPPlus|^nunit|^TestDriven|^MbUnit|^Rhino|^QuickGraph|^TestFu|^Telerik|^Antlr3|^Recaptcha|^FluentValidation|^ImageResizer|^itextsharp|^MiniProfiler|^Newtonsoft|^Pandora|^WebGrease|^Noesis|^DotNetOpenAuth|^Facebook|^LinqToTwitter|^PerceptiveMCAPI|^CookComputing|^GCheckout|^Mono\.Math|^Org\.Mentalis|^App_Web|^BundleTransformer|^ClearScript|^JavaScriptEngineSwitcher|^MsieJavaScriptEngine|^Glimpse|^Ionic|^App_GlobalResources|^AjaxMin|^MaxMind|^NReco|^OffAmazonPayments|^UAParser"; private string _assemblyRestrictToLoadingPattern = ".*"; private readonly IDictionary _assemblyMatchTable = new Dictionary(); diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs index f1abc97e26..c8c8bca9b3 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs @@ -1,15 +1,19 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Autofac; using Autofac.Builder; using SmartStore.Core.Caching; +using SmartStore.Utilities.Reflection; namespace SmartStore.Core.Infrastructure.DependencyManagement { public class ContainerManager { private readonly IContainer _container; + private readonly ConcurrentDictionary _cachedActivators = new ConcurrentDictionary(); public ContainerManager(IContainer container) { @@ -61,30 +65,60 @@ public T ResolveUnregistered(ILifetimeScope scope = null) where T : class public object ResolveUnregistered(Type type, ILifetimeScope scope = null) { - var constructors = type.GetConstructors(); + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); foreach (var constructor in constructors) { - try - { - var parameters = constructor.GetParameters(); - var parameterInstances = new List(); - foreach (var parameter in parameters) - { - var service = Resolve(parameter.ParameterType, scope); - if (service == null) - throw new SmartException("Unkown dependency"); - parameterInstances.Add(service); - } - return Activator.CreateInstance(type, parameterInstances.ToArray()); - } - catch - { - - } + object instance; + if (TryInvokeConstructor(constructor, out instance, scope)) + { + return instance; + } } + throw new SmartException("No contructor was found that had all the dependencies satisfied."); } + private FastActivator GetActivatorForUnregisteredService(Type serviceType) + { + FastActivator activator; + if (!_cachedActivators.TryGetValue(serviceType, out activator)) + { + + } + + return activator; + } + + private bool TryInvokeConstructor(ConstructorInfo constructor, out object instance, ILifetimeScope scope = null) + { + instance = null; + + try + { + var parameters = constructor.GetParameters(); + var parameterInstances = new List(); + + foreach (var parameter in parameters) + { + var service = Resolve(parameter.ParameterType, scope); + if (service == null) + { + return false; + } + + parameterInstances.Add(service); + } + + instance = constructor.Invoke(parameterInstances.ToArray()); + + return instance != null; + } + catch + { + return false; + } + } + public bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance) { return (scope ?? Scope()).TryResolve(serviceType, out instance); diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs index 88de6542d6..970ddd6ec1 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs @@ -5,12 +5,13 @@ using System.Reflection; using System.Threading.Tasks; using System.Linq.Expressions; +using System.Collections.Concurrent; namespace SmartStore.Utilities.Reflection { public class FastActivator { - private Action _invoker; + private static readonly ConcurrentDictionary _activatorsCache = new ConcurrentDictionary(); public FastActivator(ConstructorInfo constructorInfo) { @@ -23,12 +24,12 @@ public FastActivator(ConstructorInfo constructorInfo) /// /// Gets the backing . /// - public ConstructorInfo Constructor { get; } + public ConstructorInfo Constructor { get; private set; } /// /// Gets the constructor invoker. /// - public Func Invoker { get; } + public Func Invoker { get; private set; } /// /// Creates an instance of the type using the specified parameters. @@ -70,5 +71,125 @@ public static Func MakeFastInvoker(ConstructorInfo constructor return lambda.Compile(); } + + #region Static + + /// + /// Creates and caches fast constructor invokers + /// + /// The type to extract fast constructor invokers for + /// A cached array of all public instance constructors from the given type. + /// The parameterless default constructor is always excluded from the list of activators + public static FastActivator[] GetActivators(Type type) + { + return GetActivatorsCore(type); + } + + private static FastActivator[] GetActivatorsCore(Type type) + { + FastActivator[] activators; + if (!_activatorsCache.TryGetValue(type, out activators)) + { + var candidates = GetCandidateConstructors(type); + activators = candidates.Select(c => new FastActivator(c)).ToArray(); + _activatorsCache.TryAdd(type, activators); + } + + return activators; + } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The type of object to create. + /// + /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. + /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. + /// + /// A reference to the newly created object. + public static T CreateInstance(params object[] args) + { + return (T)CreateInstance(typeof(T), args); + } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The type of object to create. + /// + /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. + /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. + /// + /// A reference to the newly created object. + public static object CreateInstance(Type type, params object[] args) + { + Guard.ArgumentNotNull(() => type); + + if (args == null || args.Length == 0) + { + // don't struggle with FastActivator: native reflection is really fast with default constructor! + return Activator.CreateInstance(type); + } + + var activators = GetActivatorsCore(type); + var matchingActivator = FindMatchingActivatorCore(activators, type, args); + + if (matchingActivator == null) + { + throw new ArgumentException("No matching contructor was found for the given arguments.", "args"); + } + + return matchingActivator.Activate(args); + } + + public static FastActivator FindMatchingActivator(Type type, params object[] args) + { + var activators = GetActivatorsCore(type); + var matchingActivator = FindMatchingActivatorCore(activators, type, args); + + return matchingActivator; + } + + private static FastActivator FindMatchingActivatorCore(FastActivator[] activators, Type type, object[] args) + { + if (activators.Length == 0) + { + return null; + } + + if (activators.Length == 1) + { + // this seems to be bad design, but it's on purpose for performance reasons. + // In nearly ALL cases there is only one constructor. + return activators[0]; + } + + var argTypes = args.Select(x => x.GetType()).ToArray(); + var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.Public, null, argTypes, null); + + if (constructor != null) + { + var matchingActivator = activators.FirstOrDefault(a => a.Constructor == constructor); + return matchingActivator; + } + + return null; + } + + private static IEnumerable GetCandidateConstructors(Type type) + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + return constructors.Where(c => c.GetParameters().Length > 0); + } + + private static void CheckIsValidType(Type type) + { + if (type.IsAbstract || type.IsInterface) + { + throw new ArgumentException("The type to create activators for must be concrete. Type: {0}".FormatInvariant(type.ToString()), "type"); + } + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs index fd26b61225..65a726248f 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs @@ -16,17 +16,17 @@ public class FastProperty // Delegate type for a by-ref property getter private delegate TValue ByRefFunc(ref TDeclaringType arg); - private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter)); - private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference)); - private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter)); - private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference)); - private static readonly MethodInfo CallPropertySetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter)); + private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertyGetter"); + private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertyGetterByReference"); + private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetter"); + private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetterByReference"); + private static readonly MethodInfo CallPropertySetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertySetter"); - private static readonly ConcurrentDictionary SinglePropertiesCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _singlePropertiesCache = new ConcurrentDictionary(); // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. - private static readonly ConcurrentDictionary> PropertiesCache = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> VisiblePropertiesCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> _propertiesCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> _visiblePropertiesCache = new ConcurrentDictionary>(); private Action _valueSetter; @@ -36,10 +36,7 @@ public class FastProperty /// public FastProperty(PropertyInfo property) { - if (property == null) - { - throw new ArgumentNullException(nameof(property)); - } + Guard.ArgumentNotNull(() => property); Property = property; Name = property.Name; @@ -49,7 +46,7 @@ public FastProperty(PropertyInfo property) /// /// Gets the backing . /// - public PropertyInfo Property { get; } + public PropertyInfo Property { get; private set; } /// /// Gets (or sets in derived types) the property name. @@ -59,7 +56,7 @@ public FastProperty(PropertyInfo property) /// /// Gets the property value getter. /// - public Func ValueGetter { get; } + public Func ValueGetter { get; private set; } /// /// Gets the property value setter. @@ -119,7 +116,7 @@ public static IReadOnlyDictionary GetProperties(object ins /// public static IReadOnlyDictionary GetProperties(Type type) { - return (IReadOnlyDictionary)GetProperties(type, CreateInstance, PropertiesCache); + return (IReadOnlyDictionary)GetProperties(type, CreateInstance, _propertiesCache); } /// @@ -138,7 +135,7 @@ public static IReadOnlyDictionary GetProperties(Type type) /// public static IReadOnlyDictionary GetVisibleProperties(object instance) { - return (IReadOnlyDictionary)GetVisibleProperties(instance.GetType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); + return (IReadOnlyDictionary)GetVisibleProperties(instance.GetType(), CreateInstance, _propertiesCache, _visiblePropertiesCache); } /// @@ -157,7 +154,7 @@ public static IReadOnlyDictionary GetVisibleProperties(obj /// public static IReadOnlyDictionary GetVisibleProperties(Type type) { - return (IReadOnlyDictionary)GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); + return (IReadOnlyDictionary)GetVisibleProperties(type, CreateInstance, _propertiesCache, _visiblePropertiesCache); } public static FastProperty GetProperty(Type type, string propertyName, bool cacheAllProperties = false) @@ -172,21 +169,21 @@ public static FastProperty GetProperty(Type type, string propertyName, bool cach return fastProperty; } - if (PropertiesCache.TryGetValue(type, out allProperties)) + if (_propertiesCache.TryGetValue(type, out allProperties)) { allProperties.TryGetValue(propertyName, out fastProperty); return fastProperty; } var key = new PropertyKey(type, propertyName); - if (!SinglePropertiesCache.TryGetValue(key, out fastProperty)) + if (!_singlePropertiesCache.TryGetValue(key, out fastProperty)) { - var pi = GetPropertyCandidates(type).Where(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + var pi = GetCandidateProperties(type).Where(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); //var pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (pi != null) { fastProperty = CreateInstance(pi); - SinglePropertiesCache.TryAdd(key, fastProperty); + _singlePropertiesCache.TryAdd(key, fastProperty); } } @@ -483,7 +480,11 @@ protected static IDictionary GetVisibleProperties( break; } - currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo(); + if (currentTypeInfo.BaseType != null) + { + currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo(); + } + } if (!ignoreProperty) @@ -509,7 +510,7 @@ protected static IDictionary GetProperties( IDictionary fastProperties; if (!cache.TryGetValue(type, out fastProperties)) { - var candidates = GetPropertyCandidates(type); + var candidates = GetCandidateProperties(type); fastProperties = candidates.Select(p => createPropertyHelper(p)).ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); cache.TryAdd(type, fastProperties); } @@ -517,7 +518,7 @@ protected static IDictionary GetProperties( return fastProperties; } - private static IEnumerable GetPropertyCandidates(Type type) + private static IEnumerable GetCandidateProperties(Type type) { // We avoid loading indexed properties using the Where statement. var properties = type.GetRuntimeProperties().Where(IsCandidateProperty); diff --git a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs index ee3e85d923..e0b542d3be 100644 --- a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs +++ b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs @@ -1,36 +1,60 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using NUnit.Framework; -using SmartStore.Core.Domain.Catalog; -using SmartStore.Tests; -using SmartStore.Utilities.Reflection; - -namespace SmartStore.Core.Tests -{ - [TestFixture] - public class PerformanceTests - { - [Test] - public void InstantiatePerfTest() - { - int cycles = 500000; - - Chronometer.Measure(cycles, "Create Product NATIVE", i => new Product()); - Chronometer.Measure(cycles, "Create Product Reflection", i => Activator.CreateInstance(typeof(Product))); - - var list = new List(); - - Chronometer.Measure(cycles, "Create List NATIVE", i => new List(list)); - Chronometer.Measure(cycles, "Create List Reflection", i => Activator.CreateInstance(typeof(List), list)); - - var ctor = typeof(List).GetConstructor(new Type[] { typeof(List) }); - var activator = new FastActivator(ctor); - Chronometer.Measure(cycles, "Create List FASTACTIVATOR", i => activator.Activate(list) ); - } - } -} +//using System; +//using System.Collections.Generic; +//using System.Diagnostics; +//using System.Reflection; +//using NUnit.Framework; +//using SmartStore.Core.Domain.Catalog; +//using SmartStore.Tests; +//using SmartStore.Utilities.Reflection; + +//namespace SmartStore.Core.Tests +//{ +// [TestFixture] +// public class PerformanceTests +// { +// [Test] +// public void InstantiatePerfTest() +// { +// int cycles = 500000; + +// Chronometer.Measure(cycles, "Create Product NATIVE", i => new Product()); +// Chronometer.Measure(cycles, "Create Product Reflection", i => Activator.CreateInstance(typeof(Product))); + +// var list = new List(); + +// Chronometer.Measure(cycles, "Create List NATIVE", i => new TestClass(list)); +// Chronometer.Measure(cycles, "Create List Reflection", i => Activator.CreateInstance(typeof(TestClass), list)); +// Chronometer.Measure(cycles, "Create List FASTACTIVATOR.CreateInstance()", i => FastActivator.CreateInstance(typeof(TestClass), list) ); + +// var ctor = typeof(TestClass).GetConstructor(new Type[] { typeof(List) }); +// //var activator = new FastActivator(ctor); +// var activator = FastActivator.FindMatchingActivator(typeof(TestClass), list); +// Chronometer.Measure(cycles, "Create List FASTACTIVATOR.Activate()", i => activator.Activate(list)); + +// Chronometer.Measure(cycles, "Create List CTOR.Invoke()", i => ctor.Invoke(new object[] { list })); +// } +// } + +// public class TestClass +// { +// //public TestClass() +// //{ +// //} +// public TestClass(IEnumerable param1) +// { +// } +// //public TestClass(int param1) +// //{ +// //} +// //public TestClass(IEnumerable param1, int param2) +// //{ +// //} +// //public TestClass(IEnumerable param1, int param2, string param3) +// //{ +// //} +// } + +//} diff --git a/src/Tests/SmartStore.Web.MVC.Tests/Public/Validators/Customer/RegisterValidatorTests.cs b/src/Tests/SmartStore.Web.MVC.Tests/Public/Validators/Customer/RegisterValidatorTests.cs index 8998a635ef..71787a6fae 100644 --- a/src/Tests/SmartStore.Web.MVC.Tests/Public/Validators/Customer/RegisterValidatorTests.cs +++ b/src/Tests/SmartStore.Web.MVC.Tests/Public/Validators/Customer/RegisterValidatorTests.cs @@ -12,7 +12,7 @@ public class RegisterValidatorTests : BaseValidatorTests { private RegisterValidator _validator; private CustomerSettings _customerSettings; - private TaxSettings _taxSettings; + private TaxSettings _taxSettings = new TaxSettings(); [SetUp] public new void Setup() From dd0162d9c9f389210522411cc33041c7e1dbb99e Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 11 Nov 2015 18:55:06 +0100 Subject: [PATCH 066/732] * (Perf) ContainerManager.ResolveUnregistered() now much faster * (Perf) FluentValidator instances are now request scoped, not transient --- .../DependencyManagement/ContainerManager.cs | 63 +++++----- .../Utilities/Reflection/FastActivator.cs | 40 +++--- .../Security/PermissionService.cs | 2 +- .../Validators/SmartValidatorFactory.cs | 27 +++-- .../SmartStore.Core.Tests/PerformanceTests.cs | 114 +++++++++--------- 5 files changed, 136 insertions(+), 110 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs index c8c8bca9b3..d0effe4bee 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs @@ -65,53 +65,60 @@ public T ResolveUnregistered(ILifetimeScope scope = null) where T : class public object ResolveUnregistered(Type type, ILifetimeScope scope = null) { - var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); - foreach (var constructor in constructors) - { - object instance; - if (TryInvokeConstructor(constructor, out instance, scope)) + FastActivator activator; + object[] parameterInstances = null; + + if (!_cachedActivators.TryGetValue(type, out activator)) + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance); + foreach (var constructor in constructors) { - return instance; + var parameterTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray(); + if (TryResolveAll(parameterTypes, out parameterInstances, scope)) + { + activator = new FastActivator(constructor); + _cachedActivators.TryAdd(type, activator); + break; + } } - } - - throw new SmartException("No contructor was found that had all the dependencies satisfied."); - } + } - private FastActivator GetActivatorForUnregisteredService(Type serviceType) - { - FastActivator activator; - if (!_cachedActivators.TryGetValue(serviceType, out activator)) + if (activator != null) { - + if (parameterInstances == null) + { + TryResolveAll(activator.ParameterTypes, out parameterInstances, scope); + } + if (parameterInstances != null) + { + return activator.Activate(parameterInstances); + } } - return activator; - } + throw new SmartException("No contructor was found that had all the dependencies satisfied."); + } - private bool TryInvokeConstructor(ConstructorInfo constructor, out object instance, ILifetimeScope scope = null) + private bool TryResolveAll(Type[] types, out object[] instances, ILifetimeScope scope = null) { - instance = null; + instances = null; try { - var parameters = constructor.GetParameters(); - var parameterInstances = new List(); - - foreach (var parameter in parameters) + var instances2 = new object[types.Length]; + + for (int i = 0; i < types.Length; i++) { - var service = Resolve(parameter.ParameterType, scope); + var service = Resolve(types[i], scope); if (service == null) { return false; } - parameterInstances.Add(service); + instances2[i] = service; } - instance = constructor.Invoke(parameterInstances.ToArray()); - - return instance != null; + instances = instances2; + return true; } catch { diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs index 970ddd6ec1..3f95ddeee4 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs @@ -19,13 +19,19 @@ public FastActivator(ConstructorInfo constructorInfo) Constructor = constructorInfo; Invoker = MakeFastInvoker(constructorInfo); + ParameterTypes = constructorInfo.GetParameters().Select(p => p.ParameterType).ToArray(); } /// - /// Gets the backing . + /// Gets the backing . /// public ConstructorInfo Constructor { get; private set; } + /// + /// Gets the parameter types from the backing + /// + public Type[] ParameterTypes { get; private set; } + /// /// Gets the constructor invoker. /// @@ -47,27 +53,29 @@ public object Activate(params object[] parameters) /// a fast invoker. public static Func MakeFastInvoker(ConstructorInfo constructorInfo) { - // parameters to execute - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + var paramsInfo = constructorInfo.GetParameters(); - // build parameter list - var parameterExpressions = new List(); - var paramInfos = constructorInfo.GetParameters(); - for (int i = 0; i < paramInfos.Length; i++) + var parametersExpression = Expression.Parameter(typeof(object[]), "args"); + var argumentsExpression = new Expression[paramsInfo.Length]; + + for (int paramIndex = 0; paramIndex < paramsInfo.Length; paramIndex++) { - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType); + var indexExpression = Expression.Constant(paramIndex); + var parameterType = paramsInfo[paramIndex].ParameterType; - parameterExpressions.Add(valueCast); - } + var parameterIndexExpression = Expression.ArrayIndex(parametersExpression, indexExpression); + var convertExpression = Expression.Convert(parameterIndexExpression, parameterType); + argumentsExpression[paramIndex] = convertExpression; - // new T((T0)parameters[0], (T1)parameters[1], ...) - var instanceCreate = Expression.New(constructorInfo, parameterExpressions); + if (!parameterType.GetTypeInfo().IsValueType) + continue; - // (object)new T((T0)parameters[0], (T1)parameters[1], ...) - var instanceCreateCast = Expression.Convert(instanceCreate, typeof(object)); + var nullConditionExpression = Expression.Equal(parameterIndexExpression, Expression.Constant(null)); + argumentsExpression[paramIndex] = Expression.Condition(nullConditionExpression, Expression.Default(parameterType), convertExpression); + } - var lambda = Expression.Lambda>(instanceCreateCast, parametersParameter); + var newExpression = Expression.New(constructorInfo, argumentsExpression); + var lambda = Expression.Lambda>(newExpression, parametersExpression); return lambda.Compile(); } diff --git a/src/Libraries/SmartStore.Services/Security/PermissionService.cs b/src/Libraries/SmartStore.Services/Security/PermissionService.cs index a4c8bd1803..3ea2815466 100644 --- a/src/Libraries/SmartStore.Services/Security/PermissionService.cs +++ b/src/Libraries/SmartStore.Services/Security/PermissionService.cs @@ -207,7 +207,7 @@ public virtual void InstallPermissions(IPermissionProvider permissionProvider) if (customerRole == null) { //new role (save it) - customerRole = new CustomerRole() + customerRole = new CustomerRole { Name = defaultPermission.CustomerRoleSystemName, Active = true, diff --git a/src/Presentation/SmartStore.Web.Framework/Validators/SmartValidatorFactory.cs b/src/Presentation/SmartStore.Web.Framework/Validators/SmartValidatorFactory.cs index fd72c748ed..7a1eea0a51 100644 --- a/src/Presentation/SmartStore.Web.Framework/Validators/SmartValidatorFactory.cs +++ b/src/Presentation/SmartStore.Web.Framework/Validators/SmartValidatorFactory.cs @@ -1,28 +1,39 @@ using System; using FluentValidation; using FluentValidation.Attributes; +using SmartStore.Core.Caching; using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.Validators { public class SmartValidatorFactory : AttributedValidatorFactory { - //private readonly InstanceCache _cache = new InstanceCache(); public override IValidator GetValidator(Type type) { if (type != null) { - var attribute = (ValidatorAttribute)Attribute.GetCustomAttribute(type, typeof(ValidatorAttribute)); + var attribute = (ValidatorAttribute)Attribute.GetCustomAttribute(type, typeof(ValidatorAttribute)); if ((attribute != null) && (attribute.ValidatorType != null)) { - //validators can depend on some customer specific settings (such as working language) - //that's why we do not cache validators - //var instance = _cache.GetOrCreateInstance(attribute.ValidatorType, - // x => EngineContext.Current.ContainerManager.ResolveUnregistered(x)); - var instance = EngineContext.Current.ContainerManager.ResolveUnregistered(attribute.ValidatorType); - return instance as IValidator; + var container = EngineContext.Current.ContainerManager; + + // Validators can depend on some scoped dependencies settings (such as working language), + // that's why we do not cache validators in a singleton cache. + var requestCache = container.Resolve(); + + string cacheKey = "FluentValidator.{0}".FormatInvariant(attribute.ValidatorType.ToString()); + var result = requestCache.Get(cacheKey, () => + { + System.Diagnostics.Debug.WriteLine("VAL: " + type.Name); + + var instance = EngineContext.Current.ContainerManager.ResolveUnregistered(attribute.ValidatorType); + return instance as IValidator; + }); + + return result; } } + return null; } diff --git a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs index e0b542d3be..1f1330805e 100644 --- a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs +++ b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs @@ -1,60 +1,60 @@ -//using System; -//using System.Collections.Generic; -//using System.Diagnostics; -//using System.Reflection; -//using NUnit.Framework; -//using SmartStore.Core.Domain.Catalog; -//using SmartStore.Tests; -//using SmartStore.Utilities.Reflection; - -//namespace SmartStore.Core.Tests -//{ -// [TestFixture] -// public class PerformanceTests -// { -// [Test] -// public void InstantiatePerfTest() -// { -// int cycles = 500000; - -// Chronometer.Measure(cycles, "Create Product NATIVE", i => new Product()); -// Chronometer.Measure(cycles, "Create Product Reflection", i => Activator.CreateInstance(typeof(Product))); - -// var list = new List(); - -// Chronometer.Measure(cycles, "Create List NATIVE", i => new TestClass(list)); -// Chronometer.Measure(cycles, "Create List Reflection", i => Activator.CreateInstance(typeof(TestClass), list)); -// Chronometer.Measure(cycles, "Create List FASTACTIVATOR.CreateInstance()", i => FastActivator.CreateInstance(typeof(TestClass), list) ); - -// var ctor = typeof(TestClass).GetConstructor(new Type[] { typeof(List) }); -// //var activator = new FastActivator(ctor); -// var activator = FastActivator.FindMatchingActivator(typeof(TestClass), list); -// Chronometer.Measure(cycles, "Create List FASTACTIVATOR.Activate()", i => activator.Activate(list)); - -// Chronometer.Measure(cycles, "Create List CTOR.Invoke()", i => ctor.Invoke(new object[] { list })); -// } -// } - -// public class TestClass -// { -// //public TestClass() -// //{ -// //} -// public TestClass(IEnumerable param1) -// { -// } -// //public TestClass(int param1) -// //{ -// //} -// //public TestClass(IEnumerable param1, int param2) -// //{ -// //} -// //public TestClass(IEnumerable param1, int param2, string param3) -// //{ -// //} -// } - -//} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using NUnit.Framework; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Tests; +using SmartStore.Utilities.Reflection; + +namespace SmartStore.Core.Tests +{ + [TestFixture] + public class PerformanceTests + { + [Test] + public void InstantiatePerfTest() + { + int cycles = 500000; + + Chronometer.Measure(cycles, "Create Product NATIVE", i => new Product()); + Chronometer.Measure(cycles, "Create Product Reflection", i => Activator.CreateInstance(typeof(Product))); + + var list = new List(); + + Chronometer.Measure(cycles, "Create List NATIVE", i => new TestClass(list)); + Chronometer.Measure(cycles, "Create List Reflection", i => Activator.CreateInstance(typeof(TestClass), list)); + Chronometer.Measure(cycles, "Create List FASTACTIVATOR.CreateInstance()", i => FastActivator.CreateInstance(typeof(TestClass), list) ); + + var ctor = typeof(TestClass).GetConstructor(new Type[] { typeof(List) }); + //var activator = new FastActivator(ctor); + var activator = FastActivator.FindMatchingActivator(typeof(TestClass), list); + Chronometer.Measure(cycles, "Create List FASTACTIVATOR.Activate()", i => activator.Activate(list)); + + Chronometer.Measure(cycles, "Create List CTOR.Invoke()", i => ctor.Invoke(new object[] { list })); + } + } + + public class TestClass + { + public TestClass() + { + } + public TestClass(IEnumerable param1) + { + } + public TestClass(int param1) + { + } + public TestClass(IEnumerable param1, int param2) + { + } + public TestClass(IEnumerable param1, int param2, string param3) + { + } + } + +} From e178bf51efcb3636833c6e7376dbfb9068936bbe Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 11 Nov 2015 19:51:50 +0100 Subject: [PATCH 067/732] Export of selected entities using task parameter instead of HTTP runtime cache --- .../Collections/Querystring.cs | 11 ++++---- .../201509150931528_ExportFramework2.cs | 4 +++ .../DataExchange/ExportExtensions.cs | 28 ------------------- .../ExportTask/ExportProfileTask.cs | 2 +- .../DataExchange/Internal/DataExportTask.cs | 11 ++------ .../DataExchange/Internal/DataExporter.cs | 12 ++++++-- .../Internal/DataExporterContext.cs | 3 +- .../Tasks/TaskScheduler.cs | 2 +- .../Controllers/ExportController.cs | 13 ++++++--- .../Administration/Views/Export/List.cshtml | 1 - .../Views/Export/Preview.cshtml | 8 ++++-- 11 files changed, 40 insertions(+), 55 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Collections/Querystring.cs b/src/Libraries/SmartStore.Core/Collections/Querystring.cs index 76606ef24d..e430e4ccb2 100644 --- a/src/Libraries/SmartStore.Core/Collections/Querystring.cs +++ b/src/Libraries/SmartStore.Core/Collections/Querystring.cs @@ -45,22 +45,23 @@ public static string ExtractQuerystring(string s) /// /// the string to parse /// the QueryString object - public QueryString FillFromString(string s) + public QueryString FillFromString(string s, bool urlDecode = false) { base.Clear(); if (string.IsNullOrEmpty(s)) { return this; } + foreach (string keyValuePair in ExtractQuerystring(s).Split('&')) { if (string.IsNullOrEmpty(keyValuePair)) { continue; } + string[] split = keyValuePair.Split('='); - base.Add(split[0], - split.Length == 2 ? split[1] : ""); + base.Add(split[0], split.Length == 2 ? (urlDecode ? HttpUtility.UrlDecode(split[1]) : split[1]) : ""); } return this; } @@ -73,7 +74,7 @@ public QueryString FromCurrent() { if (HttpContext.Current != null) { - return FillFromString(HttpContext.Current.Request.QueryString.ToString()); + return FillFromString(HttpContext.Current.Request.QueryString.ToString(), true); } base.Clear(); return this; @@ -182,7 +183,7 @@ public override string ToString() { if (!string.IsNullOrEmpty(base.Keys[i])) { - foreach (string val in base[base.Keys[i]].Split(',')) + foreach (string val in base[base.Keys[i]].EmptyNull().Split(',')) { builder.Append((builder.Length == 0) ? "?" : "&").Append( HttpUtility.UrlEncode(base.Keys[i])).Append("=").Append(val); diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index 819cb147ae..b32ed81653 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -56,6 +56,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Cannot load the provider {0}.", "Der Provider {0} konnte nicht geladen werden."); + builder.AddOrUpdate("Admin.Common.NoEntriesSelected", + "No entries have been selected.", + "Es wurden keine Eintrge ausgewhlt."); + builder.AddOrUpdate("ActivityLog.EditPaymentMethod", "Edited payment method '{0}' ({1})", "Zahlungsart '{0}' ({1}) bearbeitet"); diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index 8a0cdc959d..cf86f0d411 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -2,8 +2,6 @@ using System.Globalization; using System.IO; using System.Text; -using System.Web; -using System.Web.Caching; using SmartStore.Core.Domain; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Plugins; @@ -96,31 +94,5 @@ public static string ResolveFileNamePattern(this ExportProfile profile, Store st return result; } - - /// - /// Gets the cache key for selected entity identifiers - /// - /// Export profile - /// Cache key for selected entity identifiers - public static string GetSelectedEntityIdsCacheKey(this ExportProfile profile) - { - // do not use profile.Id because it can be 0 - return "ExportProfile_SelectedEntityIds_" + profile.ProviderSystemName; - } - - /// - /// Caches selected entity identifiers to be considered during following export - /// - /// Export profile - /// Comma separated entity identifiers - public static void CacheSelectedEntityIds(this ExportProfile profile, string selectedIds) - { - var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); - - if (selectedIds.HasValue()) - HttpRuntime.Cache.Add(selectedIdsCacheKey, selectedIds, null, DateTime.UtcNow.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); - else - HttpRuntime.Cache.Remove(selectedIdsCacheKey); - } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs index 3bc601bedf..95f276a993 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportTask/ExportProfileTask.cs @@ -2872,7 +2872,7 @@ public void Execute(TaskExecutionContext taskContext) var profileId = taskContext.ScheduleTask.Alias.ToInt(); var profile = _exportProfileService.Value.GetExportProfileById(profileId); - var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); + var selectedIdsCacheKey = "ExportProfile_SelectedEntityIds_" + profile.ProviderSystemName; var selectedEntityIds = HttpRuntime.Cache[selectedIdsCacheKey] as string; var provider = _exportProfileService.Value.LoadProvider(profile.ProviderSystemName); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs index 33b2eb1e2a..ab5d8e40c5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs @@ -27,11 +27,6 @@ public void Execute(TaskExecutionContext ctx) var profileId = ctx.ScheduleTask.Alias.ToInt(); var profile = _exportProfileService.GetExportProfileById(profileId); - // TODO: find a better way to transmit selected entity ids (e.g. new TaskExecutionContext.Parameters property) - var selectedIdsCacheKey = profile.GetSelectedEntityIdsCacheKey(); - var selectedEntityIds = HttpRuntime.Cache[selectedIdsCacheKey] as string; - HttpRuntime.Cache.Remove(selectedIdsCacheKey); - // load provider var provider = _exportProfileService.LoadProvider(profile.ProviderSystemName); if (provider == null) @@ -50,10 +45,10 @@ public void Execute(TaskExecutionContext ctx) ctx.SetProgress(null, msg, true); }; - if (selectedEntityIds.HasValue()) + if (ctx.Parameters.ContainsKey("SelectedIds")) { - request.EntitiesToExport = selectedEntityIds.ToIntArray(); - } + request.EntitiesToExport = ctx.Parameters["SelectedIds"].ToIntArray(); + } // process! _exporter.Export(request, ctx.CancellationToken); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs index faccb9748f..cc263c1b5b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs @@ -1061,6 +1061,11 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc { var ctx = new DataExporterContext(request, cancellationToken); + if (request.EntitiesToExport != null) + { + ctx.EntityIdsSelected.AddRange(request.EntitiesToExport); + } + ExportCoreOuter(ctx); if (ctx.Result != null && ctx.Result.Succeeded && ctx.Result.Files.Count > 0) @@ -1068,7 +1073,6 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc string prefix = null; string suffix = null; var extension = Path.GetExtension(ctx.Result.Files.First().FileName); - var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count()); if (request.Provider.Value.EntityType == ExportEntityType.Product) prefix = T("Admin.Catalog.Products"); @@ -1085,6 +1089,8 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc else prefix = request.Provider.Value.EntityType.ToString(); + var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count()); + if (selectedEntityCount == 0) suffix = T("Common.All"); else @@ -1104,7 +1110,7 @@ public IList Preview(DataExportRequest request) var resultData = new List(); var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); - var ctx = new DataExporterContext(request, cancellation.Token, null, true); + var ctx = new DataExporterContext(request, cancellation.Token, true); var unused = Init(ctx, totalRecords); @@ -1138,7 +1144,7 @@ public int GetDataCount(DataExportRequest request) { var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); - var ctx = new DataExporterContext(request, cancellation.Token, null, true); + var ctx = new DataExporterContext(request, cancellation.Token, true); var unused = Init(ctx); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index 2ae610ca6d..b12b3a1bdb 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -26,19 +26,18 @@ internal class DataExporterContext public DataExporterContext( DataExportRequest request, CancellationToken cancellationToken, - string selectedIds = null, bool isPreview = false) { Request = request; CancellationToken = cancellationToken; Filter = XmlHelper.Deserialize(request.Profile.Filtering); Projection = XmlHelper.Deserialize(request.Profile.Projection); - EntityIdsSelected = selectedIds.SplitSafe(",").Select(x => x.ToInt()).ToList(); IsPreview = isPreview; FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(request.Profile.FolderName)); FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; + EntityIdsSelected = new List(); Categories = new Dictionary(); CategoryPathes = new Dictionary(); Countries = new Dictionary(); diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs index 3ff2e5e04f..22cecb7046 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs @@ -113,7 +113,7 @@ public void RunSingleTask(int scheduleTaskId, IDictionary taskPa query = qs.ToString(); } - CallEndpoint(_baseUrl + "/Execute/{0}{1}".FormatInvariant(scheduleTaskId, query)); + CallEndpoint("{0}/Execute/{1}{2}".FormatInvariant(_baseUrl, scheduleTaskId, query)); } private void Elapsed(object sender, System.Timers.ElapsedEventArgs e) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 55ac7330ac..518e66169c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -826,7 +826,7 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) } [HttpPost] - public ActionResult Execute(int id, string selectedIds, bool exportAll) + public ActionResult Execute(int id, string selectedIds) { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) return AccessDeniedView(); @@ -835,10 +835,15 @@ public ActionResult Execute(int id, string selectedIds, bool exportAll) if (profile == null) return RedirectToAction("List"); - // TODO: - profile.CacheSelectedEntityIds(selectedIds); + Dictionary taskParams = null; - _taskScheduler.RunSingleTask(profile.SchedulingTaskId); + if (selectedIds.HasValue()) + { + taskParams = new Dictionary(); + taskParams.Add("SelectedIds", selectedIds); + } + + _taskScheduler.RunSingleTask(profile.SchedulingTaskId, taskParams); NotifyInfo(T("Admin.System.ScheduleTasks.RunNow.Progress")); diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml index 689da7f9a4..740e0447ce 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml @@ -18,7 +18,6 @@
-
@if(Model.Count > 0) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml index b5e527976d..2bca030bc9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml @@ -16,7 +16,6 @@
- @if (Model.LogFileExists) { @@ -42,8 +41,13 @@ e.preventDefault(); var form = $('#ProfileExportForm'), exportAll = $(this).attr('name') === 'ExportAll'; + + if (!exportAll && selectedIds.length === 0) { + EventBroker.publish("message", { title: '@T("Admin.Common.NoEntriesSelected")', type: 'warning' }); + return false; + } + form.find('[name=SelectedIds]').val(exportAll ? '' : selectedIds.join(',')); - form.find('[name=ExportAll]').val(exportAll.toString()); form.submit(); return false; }); From bde240e9c660249616febc6d46fd603dd8caea62 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 11 Nov 2015 23:12:06 +0100 Subject: [PATCH 068/732] (Perf) more refactoring --- .../Extensions/MiscExtensions.cs | 6 +- .../Infrastructure/ComparableObject.cs | 118 ++++----- .../DependencyManagement/ContainerManager.cs | 2 +- .../SmartStore.Core/SmartStore.Core.csproj | 1 + .../Utilities/HashCodeCombiner.cs | 90 +++++++ .../Utilities/Reflection/FastActivator.cs | 12 +- .../Extensions/DataReaderExtensions.cs | 223 ++++++++++-------- .../Extensions/SqlDataReaderExtensions.cs | 30 --- .../SmartStore.Data/SmartStore.Data.csproj | 1 - .../SmartStore.Core.Tests/PerformanceTests.cs | 31 ++- 10 files changed, 296 insertions(+), 218 deletions(-) create mode 100644 src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs delete mode 100644 src/Libraries/SmartStore.Data/Extensions/SqlDataReaderExtensions.cs diff --git a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs index 865d5f8ed4..c59cb07b21 100644 --- a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs @@ -11,12 +11,12 @@ namespace SmartStore { public static class MiscExtensions { - public static void Dump(this Exception exc) + public static void Dump(this Exception exception) { try { - exc.StackTrace.Dump(); - exc.Message.Dump(); + exception.StackTrace.Dump(); + exception.Message.Dump(); } catch { } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs index ed24bd71f4..7291139dd8 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs @@ -4,6 +4,9 @@ using System.Linq.Expressions; using System.Reflection; using System.ComponentModel; +using SmartStore.Utilities.Reflection; +using SmartStore.Utilities; +using System.Collections.Concurrent; namespace SmartStore { @@ -13,26 +16,9 @@ namespace SmartStore [Serializable] public abstract class ComparableObject { - /// - /// To help ensure hashcode uniqueness, a carefully selected random number multiplier - /// is used within the calculation. Goodrich and Tamassia's Data Structures and - /// Algorithms in Java asserts that 31, 33, 37, 39 and 41 will produce the fewest number - /// of collissions. See http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/ - /// for more information. - /// - protected const int HashMultiplier = 31; - - private readonly List _extraSignatureProperties = new List(); + private readonly HashSet _extraSignatureProperties = new HashSet(StringComparer.OrdinalIgnoreCase); - /// - /// This static member caches the domain signature properties to avoid looking them up for - /// each instance of the same type. - /// - /// A description of the ThreadStatic attribute may be found at - /// http://www.dotnetjunkies.com/WebLog/chris.taylor/archive/2005/08/18/132026.aspx - /// - [ThreadStatic] - private static IDictionary> s_signatureProperties; + private static readonly ConcurrentDictionary _signaturePropertyNames = new ConcurrentDictionary(); public override bool Equals(object obj) { @@ -58,21 +44,23 @@ public override int GetHashCode() var signatureProperties = GetSignatureProperties(); Type t = this.GetType(); - // It's possible for two objects to return the same hash code based on - // identically valued properties, even if they're of two different types, - // so we include the object's type in the hash calculation - int hashCode = t.GetHashCode(); + var combiner = HashCodeCombiner.Start(); - foreach (var pi in signatureProperties) + // It's possible for two objects to return the same hash code based on + // identically valued properties, even if they're of two different types, + // so we include the object's type in the hash calculation + combiner.Add(t.GetHashCode()); + + foreach (var prop in signatureProperties) { - object value = pi.GetValue(this); + object value = prop.GetValue(this); if (value != null) - hashCode = (hashCode * HashMultiplier) ^ value.GetHashCode(); + combiner.Add(value.GetHashCode()); } if (signatureProperties.Any()) - return hashCode; + return combiner.CombinedHash; // If no properties were flagged as being part of the signature of the object, // then simply return the hashcode of the base object as the hashcode. @@ -121,56 +109,46 @@ protected virtual bool HasSameSignatureAs(ComparableObject compareTo) /// /// - public IEnumerable GetSignatureProperties() + public IEnumerable GetSignatureProperties() { - IEnumerable properties; - - // Init the signaturePropertiesDictionary here due to reasons described at - // http://blogs.msdn.com/jfoscoding/archive/2006/07/18/670497.aspx - if (s_signatureProperties == null) - s_signatureProperties = new Dictionary>(); - - var t = GetType(); - - if (s_signatureProperties.TryGetValue(t, out properties)) - return properties; - - return (s_signatureProperties[t] = GetSignaturePropertiesCore()); + var type = GetType(); + var propertyNames = GetSignaturePropertyNamesCore(); + + foreach (var name in propertyNames) + { + var fastProperty = FastProperty.GetProperty(type, name); + if (fastProperty != null) + { + yield return fastProperty; + } + } } /// /// Enforces the template method pattern to have child objects determine which specific /// properties should and should not be included in the object signature comparison. /// - protected virtual IEnumerable GetSignaturePropertiesCore() + protected virtual string[] GetSignaturePropertyNamesCore() { - Type t = this.GetType(); - //var properties = TypeDescriptor.GetProvider(t).GetTypeDescriptor(t) - // .GetPropertiesWith(); + Type type = this.GetType(); + string[] names; - //if (_extraSignatureProperties.Count > 0) - //{ - // properties = properties.Union(_extraSignatureProperties); - //} + if (!_signaturePropertyNames.TryGetValue(type, out names)) + { + names = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => Attribute.IsDefined(p, typeof(ObjectSignatureAttribute), true)) + .Select(p => p.Name) + .ToArray(); - //return new PropertyDescriptorCollection(properties.ToArray(), true); + _signaturePropertyNames.TryAdd(type, names); + } - var properties = t.GetProperties() - .Where(p => Attribute.IsDefined(p, typeof(ObjectSignatureAttribute), true)); + if (_extraSignatureProperties.Count == 0) + { + return names; + } - return properties.Union(_extraSignatureProperties).ToList(); - } - - /// - /// Adds an extra property to the type specific signature properties list. - /// - /// The property to add. - /// Both lists are unioned, so - /// that no duplicates can occur within the global descriptor collection. - protected void RegisterSignatureProperty(PropertyInfo propertyInfo) - { - Guard.ArgumentNotNull(() => propertyInfo); - _extraSignatureProperties.Add(propertyInfo); + return names.Union(_extraSignatureProperties).ToArray(); } /// @@ -183,13 +161,7 @@ protected void RegisterSignatureProperty(string propertyName) { Guard.ArgumentNotEmpty(() => propertyName); - Type t = GetType(); - - var pi = t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); - if (pi == null) - throw Error.Argument("propertyName", "Could not find property '{0}' on type '{1}'.", propertyName, t); - - RegisterSignatureProperty(pi); + _extraSignatureProperties.Add(propertyName); } } @@ -212,7 +184,7 @@ protected void RegisterSignatureProperty(Expression> expression) { Guard.ArgumentNotNull(() => expression); - base.RegisterSignatureProperty(expression.ExtractPropertyInfo()); + base.RegisterSignatureProperty(expression.ExtractPropertyInfo().Name); } public virtual bool Equals(T other) diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs index d0effe4bee..6e314c8f0c 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs @@ -70,7 +70,7 @@ public object ResolveUnregistered(Type type, ILifetimeScope scope = null) if (!_cachedActivators.TryGetValue(type, out activator)) { - var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance); + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (var constructor in constructors) { var parameterTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray(); diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index e4bb819bdb..8019c3d8d6 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -558,6 +558,7 @@ + diff --git a/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs b/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs new file mode 100644 index 0000000000..b69d640a05 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +/* + Copied over from Microsoft.Framework.Internal +*/ + +namespace SmartStore.Utilities +{ + internal struct HashCodeCombiner + { + private long _combinedHash64; + + public int CombinedHash + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _combinedHash64.GetHashCode(); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashCodeCombiner(long seed) + { + _combinedHash64 = seed; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(IEnumerable e) + { + if (e == null) + { + Add(0); + } + else + { + var count = 0; + foreach (object o in e) + { + Add(o); + count++; + } + Add(count); + } + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator int (HashCodeCombiner self) + { + return self.CombinedHash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(int i) + { + _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(string s) + { + var hashCode = (s != null) ? s.GetHashCode() : 0; + return Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(object o) + { + var hashCode = (o != null) ? o.GetHashCode() : 0; + return Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(TValue value, IEqualityComparer comparer) + { + var hashCode = value != null ? comparer.GetHashCode(value) : 0; + return Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HashCodeCombiner Start() + { + return new HashCodeCombiner(0x1505L); + } + } +} diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs index 3f95ddeee4..ce0ea4b9a2 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastActivator.cs @@ -131,7 +131,7 @@ public static T CreateInstance(params object[] args) /// A reference to the newly created object. public static object CreateInstance(Type type, params object[] args) { - Guard.ArgumentNotNull(() => type); + Guard.ArgumentNotNull(type, "type"); if (args == null || args.Length == 0) { @@ -164,8 +164,8 @@ private static FastActivator FindMatchingActivatorCore(FastActivator[] activator { return null; } - - if (activators.Length == 1) + + if (activators.Length == 1) { // this seems to be bad design, but it's on purpose for performance reasons. // In nearly ALL cases there is only one constructor. @@ -173,7 +173,11 @@ private static FastActivator FindMatchingActivatorCore(FastActivator[] activator } var argTypes = args.Select(x => x.GetType()).ToArray(); - var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.Public, null, argTypes, null); + var constructor = type.GetConstructor( + BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, + null, + argTypes, + null); if (constructor != null) { diff --git a/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs index 9581cf4ce4..edd6e3dba4 100644 --- a/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs @@ -4,112 +4,141 @@ using System.Collections.Generic; using System.Data; using System.Reflection; +using System.Linq; +using SmartStore.Utilities.Reflection; +using System.Dynamic; namespace SmartStore.Data { public static class DataReaderExtensions { - /// - /// Creates a list of a given type from all the rows in a DataReader. - /// - /// Note this method uses Reflection so this isn't a high performance - /// operation, but it can be useful for generic data reader to entity - /// conversions on the fly and with anonymous types. - /// - /// - /// An open DataReader that's in position to read - /// Optional - comma delimited list of fields that you don't want to update - /// - /// Optional - Cached PropertyInfo dictionary that holds property info data for this object. - /// Can be used for caching hte PropertyInfo structure for multiple operations to speed up - /// translation. If not passed automatically created. - /// - /// - public static List DataReaderToObjectList(this IDataReader reader, string fieldsToSkip = null, Dictionary piList = null) - where TType : new() - { - if (reader == null) - return null; - - var items = new List(); - - // Create lookup list of property info objects - if (piList == null) - { - piList = new Dictionary(); - var props = typeof(TType).GetProperties(BindingFlags.Instance | BindingFlags.Public); - foreach (var prop in props) - piList.Add(prop.Name.ToLower(), prop); - } + public static object GetValue(this IDataReader reader, string columnName) + { + try + { + if (reader != null && !reader.IsClosed && columnName.HasValue()) + { + int ordinal = reader.GetOrdinal(columnName); + return reader.GetValue(ordinal); + } + } + catch (Exception ex) + { + ex.Dump(); + } + + return null; + } + + public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) + where T : new() + { + Guard.ArgumentNotNull(() => reader); + while (reader.Read()) + { + yield return reader.Map(fieldsToSkip); + } + } + + public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) + { + Guard.ArgumentNotNull(() => reader); + while (reader.Read()) - { - var inst = new TType(); - DataReaderToObject(reader, inst, fieldsToSkip, piList); - items.Add(inst); - } + { + yield return reader.Map(fieldsToSkip); + } + } - return items; - } - - /// - /// Populates the properties of an object from a single DataReader row using - /// Reflection by matching the DataReader fields to a public property on - /// the object passed in. Unmatched properties are left unchanged. - /// - /// You need to pass in a data reader located on the active row you want - /// to serialize. - /// - /// This routine works best for matching pure data entities and should - /// be used only in low volume environments where retrieval speed is not - /// critical due to its use of Reflection. - /// - /// Instance of the DataReader to read data from. Should be located on the correct record (Read() should have been called on it before calling this method) - /// Instance of the object to populate properties on - /// Optional - A comma delimited list of object properties that should not be updated - /// Optional - Cached PropertyInfo dictionary that holds property info data for this object - public static void DataReaderToObject(this IDataReader reader, object instance, string fieldsToSkip = null, Dictionary piList = null) - { - if (reader.IsClosed) - throw new InvalidOperationException("Data reader cannot be used because it's already closed"); - - if (string.IsNullOrEmpty(fieldsToSkip)) - fieldsToSkip = string.Empty; - else - fieldsToSkip = "," + fieldsToSkip + ","; - - fieldsToSkip = fieldsToSkip.ToLower(); - - // create a dictionary of properties to look up - // we can pass this in so we can cache the list once - // for a list operation - if (piList == null) - { - piList = new Dictionary(); - var props = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); - foreach (var prop in props) - piList.Add(prop.Name.ToLower(), prop); - } + /// + /// Populates an object from a single DataReader row using + /// fast reflection routines by matching the DataReader fields to a public property on + /// the object passed in. Unmatched properties are left unchanged. + /// + /// You need to pass in a data reader located on the active row you want + /// to serialize. + /// + /// Instance of the DataReader to read data from. Should be located on the correct record (Read() should have been called on it before calling this method) + /// An array of reader field names to ignore + public static T Map(this IDataReader reader, params string[] fieldsToSkip) + where T : new() + { + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + var instance = new T(); + MapObject(reader, instance, fieldsToSkip); + return instance; + } + + public static dynamic Map(this IDataReader reader, params string[] fieldsToSkip) + { + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + dynamic instance = new ExpandoObject(); + MapDictionary(reader, instance, fieldsToSkip); + return instance; + } + + public static void Map(this IDataReader reader, object instance, params string[] fieldsToSkip) + { + Guard.ArgumentNotNull(instance, "instance"); - for (int index = 0; index < reader.FieldCount; index++) - { - string name = reader.GetName(index).ToLower(); - if (piList.ContainsKey(name)) - { - var prop = piList[name]; - - if (fieldsToSkip.Contains("," + name + ",")) - continue; - - if ((prop != null) && prop.CanWrite) - { - var val = reader.GetValue(index); - prop.SetValue(instance, (val == DBNull.Value) ? null : val, null); - } - } + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + var dict = instance as IDictionary; + + if (dict != null) + { + MapDictionary(reader, dict, fieldsToSkip); } + else + { + MapObject(reader, instance, fieldsToSkip); + } + } + + private static void MapObject(IDataReader reader, object instance, params string[] fieldsToSkip) + { + var fastProperties = FastProperty.GetProperties(instance); + + if (fastProperties.Count == 0) + return; + + for (int i = 0; i < reader.FieldCount; i++) + { + string name = reader.GetName(i); + if (fastProperties.ContainsKey(name)) + { + var fastProp = fastProperties[name]; - return; - } - } + if (fieldsToSkip.Contains(name)) + continue; + + if ((fastProp != null) && fastProp.Property.CanWrite) + { + var dbValue = reader.GetValue(i); + fastProp.SetValue(instance, (dbValue == DBNull.Value) ? null : dbValue); + } + } + } + } + + private static void MapDictionary(IDataReader reader, IDictionary instance, params string[] fieldsToSkip) + { + for (int i = 0; i < reader.FieldCount; i++) + { + string name = reader.GetName(i); + + if (fieldsToSkip.Contains(name)) + continue; + + var dbValue = reader.GetValue(i); + instance[name] = (dbValue == DBNull.Value) ? null : dbValue; + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Extensions/SqlDataReaderExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/SqlDataReaderExtensions.cs deleted file mode 100644 index becda17803..0000000000 --- a/src/Libraries/SmartStore.Data/Extensions/SqlDataReaderExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.SqlClient; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore -{ - - public static class SqlDataReaderExtensions - { - public static object GetValue(this SqlDataReader reader, string columnName) - { - try - { - if (reader != null && columnName.HasValue()) - { - int ordinal = reader.GetOrdinal(columnName); - return reader.GetValue(ordinal); - } - } - catch (Exception exc) - { - exc.Dump(); - } - return null; - } - } -} diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 8764a6a9ed..5c46f0934d 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -375,7 +375,6 @@ - diff --git a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs index 1f1330805e..36e99e743f 100644 --- a/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs +++ b/src/Tests/SmartStore.Core.Tests/PerformanceTests.cs @@ -19,6 +19,7 @@ public void InstantiatePerfTest() Chronometer.Measure(cycles, "Create Product NATIVE", i => new Product()); Chronometer.Measure(cycles, "Create Product Reflection", i => Activator.CreateInstance(typeof(Product))); + Chronometer.Measure(cycles, "Create Product FASTACTIVATOR", i => FastActivator.CreateInstance(typeof(Product))); var list = new List(); @@ -43,15 +44,27 @@ public TestClass() public TestClass(IEnumerable param1) { } - public TestClass(int param1) - { - } - public TestClass(IEnumerable param1, int param2) - { - } - public TestClass(IEnumerable param1, int param2, string param3) - { - } + //public TestClass(int param1) + //{ + //} + //public TestClass(IEnumerable param1, int param2) + //{ + //} + //public TestClass(IEnumerable param1, int param2, string param3) + //{ + //} + //public TestClass(DateTime param1) + //{ + //} + //public TestClass(double param1) + //{ + //} + //public TestClass(decimal param1) + //{ + //} + //public TestClass(long param1) + //{ + //} } } From 14b775db26a66e9c292a5a3d93bb6aa1cd528c26 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 11 Nov 2015 23:20:01 +0100 Subject: [PATCH 069/732] Updated AutoMapper to version 4.1.1 --- .../SmartStore.WebApi/SmartStore.WebApi.csproj | 9 +++------ src/Plugins/SmartStore.WebApi/packages.config | 2 +- .../SmartStore.Web.Framework.csproj | 11 +++-------- .../SmartStore.Web.Framework/packages.config | 2 +- .../Administration/SmartStore.Admin.csproj | 11 +++-------- .../SmartStore.Web/Administration/Web.config | 4 ++++ .../SmartStore.Web/Administration/packages.config | 2 +- .../SmartStore.Web.MVC.Tests.csproj | 11 +++-------- src/Tests/SmartStore.Web.MVC.Tests/packages.config | 2 +- 9 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj b/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj index 20e3fc318c..f5e2443c24 100644 --- a/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj +++ b/src/Plugins/SmartStore.WebApi/SmartStore.WebApi.csproj @@ -87,11 +87,9 @@ ..\..\packages\Autofac.WebApi.3.1.0\lib\net40\Autofac.Integration.WebApi.dll - - ..\..\packages\AutoMapper.3.2.1\lib\net40\AutoMapper.dll - - - ..\..\packages\AutoMapper.3.2.1\lib\net40\AutoMapper.Net4.dll + + ..\..\packages\AutoMapper.4.1.1\lib\net45\AutoMapper.dll + True ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll @@ -324,7 +322,6 @@ - - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.DevTools/Web.config b/src/Plugins/SmartStore.DevTools/Web.config index b3e042e7ec..e4010c8209 100644 --- a/src/Plugins/SmartStore.DevTools/Web.config +++ b/src/Plugins/SmartStore.DevTools/Web.config @@ -1,135 +1,135 @@ - + -
+
- - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.DiscountRules/web.config b/src/Plugins/SmartStore.DiscountRules/web.config index 3766af5f74..c6048447e5 100644 --- a/src/Plugins/SmartStore.DiscountRules/web.config +++ b/src/Plugins/SmartStore.DiscountRules/web.config @@ -1,117 +1,117 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.FacebookAuth/web.config b/src/Plugins/SmartStore.FacebookAuth/web.config index e0c21864c3..3a843a4ce5 100644 --- a/src/Plugins/SmartStore.FacebookAuth/web.config +++ b/src/Plugins/SmartStore.FacebookAuth/web.config @@ -1,121 +1,121 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.GoogleAnalytics/web.config b/src/Plugins/SmartStore.GoogleAnalytics/web.config index 8e255a8d61..06e0d9a92a 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/web.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config index 3766af5f74..c6048447e5 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config @@ -1,117 +1,117 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.OfflinePayment/web.config b/src/Plugins/SmartStore.OfflinePayment/web.config index 3766af5f74..c6048447e5 100644 --- a/src/Plugins/SmartStore.OfflinePayment/web.config +++ b/src/Plugins/SmartStore.OfflinePayment/web.config @@ -1,117 +1,117 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.PayPal/web.config b/src/Plugins/SmartStore.PayPal/web.config index 76f7e23429..92a2529d51 100644 --- a/src/Plugins/SmartStore.PayPal/web.config +++ b/src/Plugins/SmartStore.PayPal/web.config @@ -1,11 +1,11 @@ - + -
+
- + @@ -14,128 +14,128 @@ - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.Shipping/web.config b/src/Plugins/SmartStore.Shipping/web.config index 8e255a8d61..06e0d9a92a 100644 --- a/src/Plugins/SmartStore.Shipping/web.config +++ b/src/Plugins/SmartStore.Shipping/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.ShippingByWeight/web.config b/src/Plugins/SmartStore.ShippingByWeight/web.config index 8e255a8d61..06e0d9a92a 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/web.config +++ b/src/Plugins/SmartStore.ShippingByWeight/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Plugins/SmartStore.Tax/web.config b/src/Plugins/SmartStore.Tax/web.config index 3766af5f74..c6048447e5 100644 --- a/src/Plugins/SmartStore.Tax/web.config +++ b/src/Plugins/SmartStore.Tax/web.config @@ -1,117 +1,117 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index 3a44246262..8d6e7b5986 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -828,6 +828,9 @@ public ActionResult MaintenanceDeleteImageCache() _imageCache.Value.DeleteCachedImages(); + // get rid of cached image metadata + _cache("static").Clear(); + return RedirectToAction("Maintenance"); } From 6a008e6474066afd56c291119db2b9d8fa097785 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 12 Nov 2015 00:15:14 +0100 Subject: [PATCH 074/732] Updated MsieJavaScriptEngine to latest version --- src/Plugins/SmartStore.AmazonPay/web.config | 132 ++++++++--------- src/Plugins/SmartStore.Clickatell/web.config | 116 +++++++-------- src/Plugins/SmartStore.DevTools/Web.config | 130 ++++++++--------- .../SmartStore.DiscountRules/web.config | 116 +++++++-------- .../SmartStore.FacebookAuth/web.config | 120 ++++++++-------- .../SmartStore.GoogleAnalytics/web.config | 116 +++++++-------- .../web.config | 116 +++++++-------- .../SmartStore.OfflinePayment/web.config | 116 +++++++-------- src/Plugins/SmartStore.PayPal/web.config | 136 +++++++++--------- src/Plugins/SmartStore.Shipping/web.config | 116 +++++++-------- .../SmartStore.ShippingByWeight/web.config | 116 +++++++-------- src/Plugins/SmartStore.Tax/web.config | 116 +++++++-------- src/Plugins/SmartStore.WebApi/web.config | 2 +- .../SmartStore.Web.Framework.csproj | 6 +- .../SmartStore.Web.Framework/app.config | 4 +- .../SmartStore.Web.Framework/packages.config | 2 +- .../SmartStore.Web/Administration/Web.config | 2 +- .../SmartStore.Web/SmartStore.Web.csproj | 16 ++- src/Presentation/SmartStore.Web/Web.config | 6 +- .../SmartStore.Web/packages.config | 6 +- src/Tests/SmartStore.Web.MVC.Tests/App.config | 6 +- 21 files changed, 757 insertions(+), 739 deletions(-) diff --git a/src/Plugins/SmartStore.AmazonPay/web.config b/src/Plugins/SmartStore.AmazonPay/web.config index 56c636965f..0e57dde0c1 100644 --- a/src/Plugins/SmartStore.AmazonPay/web.config +++ b/src/Plugins/SmartStore.AmazonPay/web.config @@ -1,127 +1,131 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + diff --git a/src/Plugins/SmartStore.Clickatell/web.config b/src/Plugins/SmartStore.Clickatell/web.config index c6048447e5..3e1b370f5d 100644 --- a/src/Plugins/SmartStore.Clickatell/web.config +++ b/src/Plugins/SmartStore.Clickatell/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.DevTools/Web.config b/src/Plugins/SmartStore.DevTools/Web.config index e4010c8209..e1ac213bd8 100644 --- a/src/Plugins/SmartStore.DevTools/Web.config +++ b/src/Plugins/SmartStore.DevTools/Web.config @@ -1,135 +1,135 @@ - + -
+
- - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + diff --git a/src/Plugins/SmartStore.DiscountRules/web.config b/src/Plugins/SmartStore.DiscountRules/web.config index c6048447e5..3e1b370f5d 100644 --- a/src/Plugins/SmartStore.DiscountRules/web.config +++ b/src/Plugins/SmartStore.DiscountRules/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.FacebookAuth/web.config b/src/Plugins/SmartStore.FacebookAuth/web.config index 3a843a4ce5..6b304e839a 100644 --- a/src/Plugins/SmartStore.FacebookAuth/web.config +++ b/src/Plugins/SmartStore.FacebookAuth/web.config @@ -1,120 +1,120 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.GoogleAnalytics/web.config b/src/Plugins/SmartStore.GoogleAnalytics/web.config index 06e0d9a92a..32347fc005 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/web.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/web.config @@ -1,115 +1,115 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config index c6048447e5..3e1b370f5d 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.OfflinePayment/web.config b/src/Plugins/SmartStore.OfflinePayment/web.config index c6048447e5..3e1b370f5d 100644 --- a/src/Plugins/SmartStore.OfflinePayment/web.config +++ b/src/Plugins/SmartStore.OfflinePayment/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.PayPal/web.config b/src/Plugins/SmartStore.PayPal/web.config index 92a2529d51..d9badc0f86 100644 --- a/src/Plugins/SmartStore.PayPal/web.config +++ b/src/Plugins/SmartStore.PayPal/web.config @@ -1,11 +1,11 @@ - + -
+
- + @@ -14,127 +14,131 @@ - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + diff --git a/src/Plugins/SmartStore.Shipping/web.config b/src/Plugins/SmartStore.Shipping/web.config index 06e0d9a92a..32347fc005 100644 --- a/src/Plugins/SmartStore.Shipping/web.config +++ b/src/Plugins/SmartStore.Shipping/web.config @@ -1,115 +1,115 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.ShippingByWeight/web.config b/src/Plugins/SmartStore.ShippingByWeight/web.config index 06e0d9a92a..32347fc005 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/web.config +++ b/src/Plugins/SmartStore.ShippingByWeight/web.config @@ -1,115 +1,115 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.Tax/web.config b/src/Plugins/SmartStore.Tax/web.config index c6048447e5..3e1b370f5d 100644 --- a/src/Plugins/SmartStore.Tax/web.config +++ b/src/Plugins/SmartStore.Tax/web.config @@ -1,116 +1,116 @@ - + - - + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/Plugins/SmartStore.WebApi/web.config b/src/Plugins/SmartStore.WebApi/web.config index b6bf71d919..71013386a2 100644 --- a/src/Plugins/SmartStore.WebApi/web.config +++ b/src/Plugins/SmartStore.WebApi/web.config @@ -46,7 +46,7 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index e4907bffb2..1c59563458 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -102,9 +102,9 @@ ..\..\packages\FluentValidation.5.6.2.0\lib\Net45\FluentValidation.dll True - - False - ..\..\packages\JavaScriptEngineSwitcher.Core.1.1.3\lib\net40\JavaScriptEngineSwitcher.Core.dll + + ..\..\packages\JavaScriptEngineSwitcher.Core.1.2.4\lib\net40\JavaScriptEngineSwitcher.Core.dll + True False diff --git a/src/Presentation/SmartStore.Web.Framework/app.config b/src/Presentation/SmartStore.Web.Framework/app.config index f31d89f1ed..5d4773d412 100644 --- a/src/Presentation/SmartStore.Web.Framework/app.config +++ b/src/Presentation/SmartStore.Web.Framework/app.config @@ -16,7 +16,7 @@ - + @@ -52,4 +52,4 @@ - + diff --git a/src/Presentation/SmartStore.Web.Framework/packages.config b/src/Presentation/SmartStore.Web.Framework/packages.config index 64af87c9ed..d105ec9f25 100644 --- a/src/Presentation/SmartStore.Web.Framework/packages.config +++ b/src/Presentation/SmartStore.Web.Framework/packages.config @@ -10,7 +10,7 @@ - + diff --git a/src/Presentation/SmartStore.Web/Administration/Web.config b/src/Presentation/SmartStore.Web/Administration/Web.config index 7f2608b189..82720de3ee 100644 --- a/src/Presentation/SmartStore.Web/Administration/Web.config +++ b/src/Presentation/SmartStore.Web/Administration/Web.config @@ -92,7 +92,7 @@ - + diff --git a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj index aea5811809..2d2216c935 100644 --- a/src/Presentation/SmartStore.Web/SmartStore.Web.csproj +++ b/src/Presentation/SmartStore.Web/SmartStore.Web.csproj @@ -114,12 +114,13 @@ ..\..\packages\FluentValidation.MVC5.5.6.2.0\lib\Net45\FluentValidation.Mvc.dll True - - False - ..\..\packages\JavaScriptEngineSwitcher.Core.1.1.3\lib\net40\JavaScriptEngineSwitcher.Core.dll + + ..\..\packages\JavaScriptEngineSwitcher.Core.1.2.4\lib\net40\JavaScriptEngineSwitcher.Core.dll + True - - ..\..\packages\JavaScriptEngineSwitcher.Msie.1.1.4\lib\net40\JavaScriptEngineSwitcher.Msie.dll + + ..\..\packages\JavaScriptEngineSwitcher.Msie.1.2.11\lib\net40\JavaScriptEngineSwitcher.Msie.dll + True False @@ -129,8 +130,9 @@ True ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\MsieJavaScriptEngine.1.4.2\lib\net40\MsieJavaScriptEngine.dll + + ..\..\packages\MsieJavaScriptEngine.1.5.6\lib\net40\MsieJavaScriptEngine.dll + True False diff --git a/src/Presentation/SmartStore.Web/Web.config b/src/Presentation/SmartStore.Web/Web.config index 61b4ad427c..72900c3413 100644 --- a/src/Presentation/SmartStore.Web/Web.config +++ b/src/Presentation/SmartStore.Web/Web.config @@ -256,7 +256,7 @@ - + @@ -306,6 +306,10 @@ + + + + diff --git a/src/Presentation/SmartStore.Web/packages.config b/src/Presentation/SmartStore.Web/packages.config index 555ecc8763..46414d814b 100644 --- a/src/Presentation/SmartStore.Web/packages.config +++ b/src/Presentation/SmartStore.Web/packages.config @@ -11,8 +11,8 @@ - - + + @@ -26,7 +26,7 @@ - + diff --git a/src/Tests/SmartStore.Web.MVC.Tests/App.config b/src/Tests/SmartStore.Web.MVC.Tests/App.config index 0d3c92bfdd..47411d9ac8 100644 --- a/src/Tests/SmartStore.Web.MVC.Tests/App.config +++ b/src/Tests/SmartStore.Web.MVC.Tests/App.config @@ -43,7 +43,7 @@ - + @@ -73,6 +73,10 @@ + + + + From 48cc39feaea772e6a39f689540a4148af9f39389 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 12 Nov 2015 12:05:37 +0100 Subject: [PATCH 075/732] Further IDataExporter implementation --- .../DataExchange/IDataExporter.cs | 9 ++++- .../DataExchange/Internal/DataExportTask.cs | 7 +++- .../DataExchange/Internal/DataExporter.cs | 37 +++++++++---------- .../Internal/DataExporterContext.cs | 4 -- .../Controllers/ExportController.cs | 4 +- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs index 843a15351e..b39c61fcf5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IDataExporter.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using SmartStore.Core.Domain; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Plugins; namespace SmartStore.Services.DataExchange @@ -13,7 +15,7 @@ public interface IDataExporter { DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); - IList Preview(DataExportRequest request); + IList Preview(DataExportRequest request, int pageIndex, int? totalRecords = null); int GetDataCount(DataExportRequest request); } @@ -35,6 +37,7 @@ public DataExportRequest(ExportProfile profile, Provider provid ProgressValueSetter = _voidProgressValueSetter; ProgressMessageSetter = _voidProgressMessageSetter; + EntitiesToExport = new List(); CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -42,13 +45,15 @@ public DataExportRequest(ExportProfile profile, Provider provid public Provider Provider { get; private set; } - public IEnumerable EntitiesToExport { get; set; } + public IList EntitiesToExport { get; set; } public ProgressValueSetter ProgressValueSetter { get; set; } public ProgressMessageSetter ProgressMessageSetter { get; set; } public IDictionary CustomData { get; private set; } + public IQueryable ProductQuery { get; set; } + private static void SetProgress(int val, int max, string msg) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs index ab5d8e40c5..54ed0768e6 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs @@ -1,4 +1,4 @@ -using System.Web; +using System.Linq; using SmartStore.Core.Localization; using SmartStore.Services.Tasks; @@ -47,7 +47,10 @@ public void Execute(TaskExecutionContext ctx) if (ctx.Parameters.ContainsKey("SelectedIds")) { - request.EntitiesToExport = ctx.Parameters["SelectedIds"].ToIntArray(); + request.EntitiesToExport = ctx.Parameters["SelectedIds"] + .SplitSafe(",") + .Select(x => x.ToInt()) + .ToList(); } // process! diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs index cc263c1b5b..43cf433f7a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs @@ -380,12 +380,12 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int skip, i { IQueryable query = null; - if (ctx.QueryProducts == null) + if (ctx.Request.ProductQuery == null) { var searchContext = new ProductSearchContext { OrderBy = ProductSortingEnum.CreatedOn, - ProductIds = ctx.EntityIdsSelected, + ProductIds = ctx.Request.EntitiesToExport, StoreId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId), VisibleIndividuallyOnly = true, PriceMin = ctx.Filter.PriceMinimum, @@ -421,7 +421,7 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int skip, i } else { - query = ctx.QueryProducts; + query = ctx.Request.ProductQuery; } if (skip > 0) @@ -495,8 +495,8 @@ private IQueryable GetOrderQuery(DataExporterContext ctx, int skip, int t null, null); - if (ctx.EntityIdsSelected.Count > 0) - query = query.Where(x => ctx.EntityIdsSelected.Contains(x.Id)); + if (ctx.Request.EntitiesToExport.Count > 0) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); query = query.OrderByDescending(x => x.CreatedOnUtc); @@ -610,10 +610,8 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int skip, .Expand(x => x.CustomerRoles) .Where(x => !x.Deleted); - if (ctx.EntityIdsSelected.Count > 0) - { - query = query.Where(x => ctx.EntityIdsSelected.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Count > 0) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); query = query.OrderByDescending(x => x.CreatedOnUtc); @@ -890,6 +888,11 @@ private void ExportCoreOuter(DataExporterContext ctx) throw new SmartException("Export aborted because the export provider is not valid"); } + foreach (var item in ctx.Request.CustomData) + { + ctx.ExecuteContext.CustomProperties.Add(item.Key, item.Value); + } + ctx.Log = logger; ctx.ExecuteContext.Log = logger; ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); @@ -1025,13 +1028,15 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.DeliveryTimes.Clear(); ctx.CategoryPathes.Clear(); ctx.Categories.Clear(); - ctx.EntityIdsSelected.Clear(); ctx.ProductExportContext = null; ctx.OrderExportContext = null; ctx.ManufacturerExportContext = null; ctx.CategoryExportContext = null; ctx.CustomerExportContext = null; + ctx.Request.EntitiesToExport.Clear(); + ctx.Request.CustomData.Clear(); + ctx.ExecuteContext.CustomProperties.Clear(); ctx.ExecuteContext.Log = null; ctx.Log = null; @@ -1061,11 +1066,6 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc { var ctx = new DataExporterContext(request, cancellationToken); - if (request.EntitiesToExport != null) - { - ctx.EntityIdsSelected.AddRange(request.EntitiesToExport); - } - ExportCoreOuter(ctx); if (ctx.Result != null && ctx.Result.Succeeded && ctx.Result.Files.Count > 0) @@ -1089,7 +1089,7 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc else prefix = request.Provider.Value.EntityType.ToString(); - var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count()); + var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count); if (selectedEntityCount == 0) suffix = T("Common.All"); @@ -1102,11 +1102,8 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc return ctx.Result; } - public IList Preview(DataExportRequest request) + public IList Preview(DataExportRequest request, int pageIndex, int? totalRecords = null) { - var pageIndex = (request.CustomData.ContainsKey("PageIndex") ? (int)request.CustomData["PageIndex"] : 0); - var totalRecords = (request.CustomData.ContainsKey("TotalRecords") ? (int)request.CustomData["TotalRecords"] : 0); - var resultData = new List(); var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index b12b3a1bdb..23da17e069 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using SmartStore.Core; using SmartStore.Core.Domain; @@ -37,7 +36,6 @@ public DataExporterContext( FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(request.Profile.FolderName)); FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; - EntityIdsSelected = new List(); Categories = new Dictionary(); CategoryPathes = new Dictionary(); Countries = new Dictionary(); @@ -56,13 +54,11 @@ public DataExporterContext( ExecuteContext.Projection = XmlHelper.Deserialize(request.Profile.Projection); } - public List EntityIdsSelected { get; private set; } public List EntityIdsLoaded { get; set; } public int RecordCount { get; set; } public Dictionary RecordsPerStore { get; set; } public string ProgressInfo { get; set; } - public IQueryable QueryProducts { get; set; } public DataExportRequest Request { get; private set; } public CancellationToken CancellationToken { get; private set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 518e66169c..354da99fe9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -754,10 +754,8 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) var orderModel = new List(); var request = new DataExportRequest(profile, provider); - request.CustomData.Add("PageIndex", command.Page - 1); - request.CustomData.Add("TotalRecords", totalRecords); - var data = _dataExporter.Preview(request); + var data = _dataExporter.Preview(request, command.Page - 1, totalRecords); foreach (dynamic item in data) { From f685b80399f4584fccecd17c19af37756278ebe5 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 12 Nov 2015 17:16:49 +0100 Subject: [PATCH 076/732] Creating system export profiles --- .../Domain/DataExchange/ExportProfile.cs | 10 + .../Mapping/DataExchange/ExportProfileMap.cs | 1 + ...1511121139009_ExportFramework3.Designer.cs | 29 +++ .../201511121139009_ExportFramework3.cs | 193 ++++++++++++++++++ .../201511121139009_ExportFramework3.resx | 126 ++++++++++++ .../SmartStore.Data/SmartStore.Data.csproj | 7 + .../{Internal => }/DataExportTask.cs | 2 +- .../{Internal => }/DataExporter.cs | 3 +- .../{Internal => }/DynamicEntityHelper.cs | 3 +- .../DataExchange/ExportProfileService.cs | 8 +- .../Providers/CategoryXmlExportProvider.cs | 3 +- .../Providers/CustomerXlsxExportProvider.cs | 3 +- .../Providers/CustomerXmlExportProvider.cs | 3 +- .../ManufacturerXmlExportProvider.cs | 3 +- .../Providers/OrderXlsxExportProvider.cs | 3 +- .../Providers/OrderXmlExportProvider.cs | 3 +- .../Providers/ProductXlsxExportProvider.cs | 3 +- .../Providers/ProductXmlExportProvider.cs | 3 +- .../Providers/SubscriberCsvExportProvider.cs | 3 +- .../SmartStore.Services.csproj | 6 +- .../Localization/resources.en-us.xml | 2 +- .../Controllers/ExportController.cs | 2 + .../Models/DataExchange/ExportProfileModel.cs | 6 + .../Administration/Views/Export/Edit.cshtml | 25 ++- .../Administration/Views/Export/List.cshtml | 12 ++ 25 files changed, 432 insertions(+), 30 deletions(-) create mode 100644 src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.Designer.cs create mode 100644 src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs create mode 100644 src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.resx rename src/Libraries/SmartStore.Services/DataExchange/{Internal => }/DataExportTask.cs (96%) rename src/Libraries/SmartStore.Services/DataExchange/{Internal => }/DataExporter.cs (99%) rename src/Libraries/SmartStore.Services/DataExchange/{Internal => }/DynamicEntityHelper.cs (99%) diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs index ea98e0838b..b49d8d76d1 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs @@ -32,11 +32,21 @@ public ExportProfile() ///
public string FileNamePattern { get; set; } + /// + /// The system name of the profile + /// + public string SystemName { get; set; } + /// /// The system name of the export provider /// public string ProviderSystemName { get; set; } + /// + /// Whether the profile is an unremovable system profile + /// + public bool IsSystemProfile { get; set; } + /// /// Whether the export profile is enabled /// diff --git a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs index d54bf034e3..6e5e6758fe 100644 --- a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs @@ -12,6 +12,7 @@ public ExportProfileMap() this.Property(x => x.Name).IsRequired().HasMaxLength(100); this.Property(x => x.FolderName).IsRequired().HasMaxLength(100); + this.Property(x => x.SystemName).HasMaxLength(400); this.Property(x => x.ProviderSystemName).IsRequired().HasMaxLength(4000); this.Property(x => x.Filtering).IsMaxLength(); this.Property(x => x.Projection).IsMaxLength(); diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.Designer.cs new file mode 100644 index 0000000000..634dd864d5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.Designer.cs @@ -0,0 +1,29 @@ +// +namespace SmartStore.Data.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class ExportFramework3 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ExportFramework3)); + + string IMigrationMetadata.Id + { + get { return "201511121139009_ExportFramework3"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs new file mode 100644 index 0000000000..34a97d19bc --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs @@ -0,0 +1,193 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using Setup; + using Core.Domain.Tasks; + using System.Collections.Generic; + using Core.Domain; + using System.Linq; + using Utilities; + + public partial class ExportFramework3 : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.ExportProfile", "SystemName", c => c.String(maxLength: 400)); + AddColumn("dbo.ExportProfile", "IsSystemProfile", c => c.Boolean(nullable: false)); + } + + public override void Down() + { + DropColumn("dbo.ExportProfile", "IsSystemProfile"); + DropColumn("dbo.ExportProfile", "SystemName"); + } + + public bool RollbackOnFailure + { + get { return false; } + } + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + + var systemProfilesInfos = new List + { + new SystemProfileInfo { SystemName = "SmartStoreCategoryXml", ProviderSystemName = "Exports.SmartStoreCategoryXml", Name = "Category XML Export" }, + new SystemProfileInfo { SystemName = "SmartStoreCustomerXlsx", ProviderSystemName = "Exports.SmartStoreCustomerXlsx", Name = "Customer Excel Export" }, + new SystemProfileInfo { SystemName = "SmartStoreCustomerXml", ProviderSystemName = "Exports.SmartStoreCustomerXml", Name = "Customer XML Export" }, + new SystemProfileInfo { SystemName = "SmartStoreManufacturerXml", ProviderSystemName = "Exports.SmartStoreManufacturerXml", Name = "Manufacturer XML Export" }, + new SystemProfileInfo { SystemName = "SmartStoreOrderXlsx", ProviderSystemName = "Exports.SmartStoreOrderXlsx", Name = "Order Excel Export" }, + new SystemProfileInfo { SystemName = "SmartStoreOrderXml", ProviderSystemName = "Exports.SmartStoreOrderXml", Name = "Order XML Export" }, + new SystemProfileInfo { SystemName = "SmartStoreProductXlsx", ProviderSystemName = "Exports.SmartStoreProductXlsx", Name = "Product Excel Export" }, + new SystemProfileInfo { SystemName = "SmartStoreProductXml", ProviderSystemName = "Exports.SmartStoreProductXml", Name = "Product XML Export" }, + new SystemProfileInfo { SystemName = "SmartStoreNewsSubscriptionCsv", ProviderSystemName = "Exports.SmartStoreNewsSubscriptionCsv", Name = "Newsletter Subscribers CSV Export" } + }; + + var tasks = context.Set(); + var profiles = context.Set(); + + foreach (var profileInfo in systemProfilesInfos) + { + var profile = profiles.FirstOrDefault(x => x.IsSystemProfile && x.SystemName == profileInfo.SystemName && x.ProviderSystemName == profileInfo.ProviderSystemName); + + if (profile != null) + continue; + + var task = new ScheduleTask + { + CronExpression = "0 */6 * * *", // every six hours + Type = "SmartStore.Services.DataExchange.DataExportTask, SmartStore.Services", + Enabled = false, + StopOnError = false, + IsHidden = true + }; + + task.Name = string.Concat(profileInfo.Name, " task"); + + task = tasks.Add(task); + context.SaveChanges(); + + var seoName = SeoHelper.GetSeName(profileInfo.Name, true, false).Replace("/", "").Replace("-", ""); + + profile = new ExportProfile + { + IsSystemProfile = true, + Name = profileInfo.Name, + SystemName = profileInfo.SystemName, + ProviderSystemName = profileInfo.ProviderSystemName, + FolderName = string.Concat("sm-", seoName.Replace("export", "").ToValidPath().Truncate(50)), + FileNamePattern = "%Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%", + Enabled = true, + PerStore = false, + CreateZipArchive = false, + Cleanup = false, + SchedulingTaskId = task.Id + }; + + profile = profiles.Add(profile); + + task.Alias = profile.Id.ToString(); + + tasks.AddOrUpdate(task); + context.SaveChanges(); + } + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.NewsLetterSubscription", + "Newsletter Subscribers", + "Newsletter Abonnenten"); + + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreCategoryXml", + "Category XML Export", + "Warengruppen XML Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreCategoryXml", + "Allows to export category data in XML format.", + "Ermglicht den Export von Warengruppendaten im XML Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreCustomerXlsx", + "Customer Excel Export", + "Kunden Excel Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreCustomerXlsx", + "Allows to export customer data in Excel format.", + "Ermglicht den Export von Kundendaten im Excel Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreCustomerXml", + "Customer XML Export", + "Kunden XML Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreCustomerXml", + "Allows to export customer data in XML format.", + "Ermglicht den Export von Kundendaten im XML Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreManufacturerXml", + "Manufacturer XML Export", + "Hersteller XML Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreManufacturerXml", + "Allows to export manufacturer data in XML format.", + "Ermglicht den Export von Herstellerdaten im XML Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreOrderXlsx", + "Order Excel Export", + "Auftrags Excel Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreOrderXlsx", + "Allows to export order data in Excel format.", + "Ermglicht den Export von Auftragsdaten im Excel Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreOrderXml", + "Order XML Export", + "Auftrags XML Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreOrderXml", + "Allows to export order data in XML format.", + "Ermglicht den Export von Auftragsdaten im XML Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreProductXlsx", + "Product Excel Export", + "Produkt Excel Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreProductXlsx", + "Allows to export product data in Excel format.", + "Ermglicht den Export von Produktdaten im Excel Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreProductXml", + "Product XML Export", + "Produkt XML Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreProductXml", + "Allows to export product data in XML format.", + "Ermglicht den Export von Produktdaten im XML Format."); + + builder.AddOrUpdate("Providers.FriendlyName.Exports.SmartStoreNewsSubscriptionCsv", + "Newsletter Subscribers CSV Export", + "Newsletter Abonnenten CSV Export"); + builder.AddOrUpdate("Providers.Description.Exports.SmartStoreNewsSubscriptionCsv", + "Allows to export newsletter subscriber data in CSV format.", + "Ermglicht den Export von Newsletter Abonnentendaten im CSV Format."); + + + builder.AddOrUpdate("Admin.DataExchange.Export.SystemName", + "System name of profile", + "Systemname des Profils", + "The system name of the export profile.", + "Der Systemname des Exportprofils."); + + builder.AddOrUpdate("Admin.DataExchange.Export.IsSystemProfile", + "System profile", + "Systemprofil", + "Indicates whether the export profile is a system profile. System profiles cannot be removed.", + "Gibt an, ob es sich bei dem Exportprofil um eine Systemprofil handelt. Systemprofile knnen nicht entfernt werden."); + + builder.AddOrUpdate("Admin.DataExchange.Export.CannotDeleteSystemProfile", + "Cannot delete a system export profile.", + "Ein System-Exportprofil kann nicht gelscht werden."); + } + } + + + internal class SystemProfileInfo + { + public string SystemName { get; set; } + public string ProviderSystemName { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.resx b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.resx new file mode 100644 index 0000000000..1ed5a1d758 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.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 + + + H4sIAAAAAAAEAOy923LcOrIo+D4R8w8OP50zsY+97N4d0adjrTkhydKyYvuilmR7735RUFWQhDaLrMWLLfXEfNk8zCfNLwwA3kAgcQfJKnW92CUikQASmYlEIpH4//6f//fX//W4SV/8QEWJ8+y3l29e/fLyBcpW+Rpn97+9rKu7//GXl//r//zf/7dfT9ebxxdfO7g/UThSMyt/e/lQVdu/vn5drh7QJilfbfCqyMv8rnq1yjevk3X++u0vv/zP12/evEYExUuC68WLXy/rrMIbxP4gf57k2QptqzpJP+ZrlJbtd1JyxbC++JRsULlNVui3l1ebpKiuqrxAr94lVfLyxVGKE9KNK5TevXyRZFleJRXp5F+/lOiqKvLs/mpLPiTp9dMWEbi7JC1R2/m/DuC24/jlLR3H66Fih2pVl1W+cUT45k8tYV6L1b3I+7InHCHdKSFx9URHzcj328vrfItXL1+ILf31JC0o1Ii0J4y+BAxnr1i9svnv314IQP/WM8XbV29f/fLql397cVKnVV2g3zJUV0WS/tuLi/o2xav/QE/X+XeU/ZbVacr3lPSVlI0+kE8XRb5FRfV0ie7a/p+vX754Pa73WqzYV+PqNIM7z6o/vX354hNpPLlNUc8IHCHYqH5HGSqSCq0vkqpCRUZxIEZKqXWhraunskIb+rtrk/AfkaOXLz4mjx9Qdl89/PaS/Hz54gw/onX3pe3HlwwTsSOVqqJGpqbOs1Var9F5doVJk8m2a/A4z1OUZMAwDfjKi6Qsf+bFmhRUaEWGH4qyQzg5La5xlU5P8eN8/TR5Ix9RlRCOpmQrZ2nsHSpXBd42CmeG9uaZqw94Q8RifZ0zgS5DOfkSZWtUHJXf8PoeVaHYGix/z7Pp6dA09a1ItmSBrYgSk/puU//qIf85mrewkR8T5kZFBP1S4LxgWlmv3y2Ux3VyH3kufn09rL76NTl5PCGLzX1ePPmszMnjKw7DYXFWt2VYlv/9l1+sJtmRvd7hcpsmT58py7swqjX/XKGqYmNx5h2iEu7wfV0w6FctngMHeXPQ22k46GuS1jFWCsdmGa3M1PXi2Q/5KknxP9G6a9ODe1scDfNKCA9srG6rmQ63qQVMrCS7r5N7RxYB8NCpQ4Q+vxd5vZ1fQfftL9X0XPL9KfmB7xkDKWby5YtLlDKA8gFvG3+KLFk3A/hZkW8u8xQS6B7q5iqvixUdX24EvU4KZl776ZS+W4GqpMVz0CDqtgwL4ZuJxKWdmZbq2pV4ivZJ7T9qdIXyE4JD13qEjdtZmtyfb8hgz3CKDOT+s91oDVvcKg3dj0XedDNZKh/C94m+JrhWZzLV3UzGJSqZjivVClSAVOtQFWCvG0dqVAndKV1/60xAHcVAE3AeNKxZ14VaVx2tl9m6dK0vtIU5L6l0XaT1Pc4kJWKqep3XK0j5xLOqwpUCaFsZVYiXUrhAxQaXVDgv0Yo59Z0VwhVa1dRf90rEdVAE6rYiHSa5bv5tDrLe/vnPU7Q9eEMnb1kpvCeMt1FBxQpe1kUevhGqDCKsh5Rk2AAeJMQ8Kh+HYVu9fMUjOkivt/ROJEFnBUJXhFW3rL0w4/k6eTx9RJtt8KkXQdQa4hSPMAX6qkerCv8IPnw6Lxut1jB/GK6o+tFWKYmaYWLFJO44LPWYn3WRE/l3V0i0Wsn+PSghdVux9hJLmiJtTMTkB+bRnA70zPxz9p7IxwUz6cOwHaVp/vP3GpUV2Zd8zatghAE+EemciMjdO8KYX6pVh4n+eY03xrqn2dqzZqivyW/bRjUNuE0bFcgm3agUsuC0ap/UPsrKn0SdKTvVlN80anTcLa5IVulCebAOb5AFafIGxUGfa3QUodJ+6vJP9eYWFZ/vqAYrwwYwhVO3EZ8wEYNkHxJBlz4RcjGHjsboE6BueGEcd1YBBuoGFWywnuARB2kLHlE0nfHiOClR2wFK3c7Q7WLojNLZkMlZRi2WgKlmH2JbE6cEuSCiuB8Oq4S6rY5Gv9e4b7X57XrsWZJ2TSeQMY4gT8ksp5O3YhGVHrehs7zYJFXogt1hu0rSavKuH603ODvJNxsuYni6mwxlNB/T0d0dTjERl1Bqx/E4vUMpinCPonNcHa1WeQ2EcE/hu4rDRx+SsjrfHq3XZIumu85gGzBi0HgFoprycwbuJ52DTcrqQ36PM98NKqnPuIioaiUKb4OgJanibKJT/Tcc2GAGyKXS6g+AuNqtxzhNyTT3c6/rpggL9HUMou6wAOfaa9HS03W7hbkZLBq53yKMZGUrASETO+zEqketdAjDEGpim0+mdD0+faQGTZIe1dUDNWlWDEi3y9HVAKfBqoI0J3a1XCeImAH15iIvK3hsfTE4ELlU6jUA4tXF5q6nuo+sXN3JcTHcSwHGtZtszw/3kBWBnRuXSP0Sil27dInIHoOwyB/MRQt2bQQCdhGGkLqqAHPv8s+kWF/kOKvK95ggoSfuYL8lOEXv1XDAGDTAriPpjjqt1hoJGFB/AoxaAYqArirw6iFn9U/IJvacGGVw30UokPxKIIn2asggt05PUI97SJtNnr1qERz29Oq2znBRVpFct2bzdZaGTJv+OK0QFtsm2fTXt0/oBq2Q79YYj9AqRL79wNlK3roaWuQuwE42rFY038zV0NvJG/o73lJbKUkNsfyRTpUf8gw1hx+Tt3WWPM7UUth+W72TaWQIXAq7hbaHGRZAoUhatMVy57WaF1Jt5wRIuYsjAGVHx1BhTveOXM5r8ztcoBU1z161OA7Ls7otw4I50bUoFhtSto6PKIEmZbTgwZ/5B0SJeF7OcWnq+qFAyLbBP0VokChaVOCV0JhnHE19+w8iatf51yTYxbsLV6fmCti5JLtgMgcEd8e1H1H1kCtcSGOYm6Fyo97w2CFmhgZ2V8YqzvtbfiFQDGu0VgCrIwggdx6ECtoWjunhc2ukRSBM3WEd0qgl/To0VaaTGTNJxdNL5/cZofXJA5WE4Curp48sKduad4yfaxN5xTlnayWVNU9FgJ0mPLqfOVsoWU5PzqFeRePYQSP7qaux7R5gL48wHbSVkXknjmicyvi+vS3QD5wYtF6cA+5dMOu8tu2e5okk/FojJigcrT/wjRCV1uI6yLxG5ltShQq9TXCJ7d12UxDO0XZLWC9c+KKGqXzZrifxwfUnTLEDIFRnacpICS+xPk7z+z5+zVmkae3yFYdjNyKL285cE8tucgObDp46yOOZkB1GkKU4Wt8MgAM7QeUSK4FAwWzUdCWAhyiCw1qgbivW9cRYmYUdm42UFtrHT9sye/C2tVvZWnwnfJSppzGeV/GRXif30+fEXuY6o2Xab1vPjbmxXUv7HWdkkTOQTXTAya1TsBfFazUUfbnaJTPKbdgeK3QjVioEl+sxRFiCoiJf16vqkuzG0U+ffVxSJaRHr0Z4dsPwa7u0KwukvpWGcLMYqZdJxR1S+hHlPUq3d3X6X6i8JpySRkH2KffBpb7910w/fPWP59abHpK79AcByNf9QCjnu6o8lpYWGdmcn2o8xqo6N4I8K0akrCHfZ7WrFna1taN/qPY57BeM2pA2F36priBtssTTLB9xDC37FZeYQJ9na/wDr+skTZ9C7ZBlzvOuHnJiCc9oJ56R/s3Z3qy3HDuuRZttGuGCYtz0Mh2eeMeqhw1NnIu4bJvfnSazFSrabr+xnq7qTbStfiSMHTpmRAmDDu5jPKR9wNbRatdSZ199r6cXuiSr75IVNUEKso5WxqDjOM3+XmGdgMdp5Lz8Hd9VJ0kRfNjT4YlhrdArX7hAn6sHQvFmOYnwshnDORg/hmvaUZRaTW1jeq+S2EZH67XQh+AxnZfv8p9Zmifh5+QtntCZ+5KljYB3CIPH+LGL8v98J+H0TKjUojl93OLmSah3yZOI0w4Fu/fOUMRg+/dJeZUQqwnFmtUxNsf7NqQ3NCHK0X2BEG84+nZmhGwWr8l5eUkTcRcR4rt7RCdPqxQ1nQrVcTzGC1TgPFj6epxs7WeIA2XlnEXIn2a0SoT0HjHz9RJ9iqnkJWmHsYlt7P3XaIU31Dd1UZBf7ZvPf3n54opmjifrp0f3o6VvOS9Py2Bycq8hhjIOMXHouWT2g4gmQdfEHAbv3Kp89f1vddL6OoJUdrNdYxiPfiSY1MUphzUwPAzsqfeChbOII/+Q/2xG3WZYCQ4ezCt898QcAmd50fXxGJHdVxji42T1nT13Sp81D85KRHeDFON5Q0uyA+k3vcEWBdv0k1nCm3oTZ5IajMljPIyMAGjdIsNoBmOVyALt4HH9dFxX1eBcCJAtCv0Nlw8pLqs4SFvhTxFhXqLXR/4b78NPYp0zdHgV7F8aIYm+An1O19M20G5MTtgx7ERtXG0JniR1HIg9zj6ugR5ee0Qo8LjaOAdPTJ076zSrUFFG4a9WbY0wo4mZolVss7ZJNh/XGDUyGazwyRqKyuqoIqrztq7QSb65xVl7rBeRCUmfic5jmetoCG2Kwy3mbwjfP0wniuN9THT03/B6Quzvp6VNv9KE6pMeUZgyiXdiEe9yRZysjzsUJi4PEP9AxROt7Og96cxAYoLJB60mS7pE7eoTZdvd4/uIkrIuEO2SxpyM8shm3+bRhg+HDFWxPVr6Y4zajrR1tk4RO9EyeKDiuNeb9i5QQdNPxXJ8jJBSasTGyefNckaszuTZLIn9EgkGyXSlNy30dT74yofAGDWUFAyjAXUNEeS1gS7C52YMKMXzjMpVAUpjIM/4pGZW1fnWJLCblg+U0VUqUFUQkhLelfjdzqRxeBlirDq3mCZMTARRDUCC8+w4f4io7TsPqOk/BKYaAwjrOY4LzPBoh9DCaHovQKg6LoJ59rk/BI8XVqiNuQvtL9uB3rXZWPtti7b/cBXNePQVVOMz1PIcb3PdQaNQR3CyPuWKleqUh/HUpl+TAhOjDtpHaruvqaeZIItaqlmyqeo5VSJqp4E7jNZ1iN7j4u10LQ+OAWUm5MuVXDgCcs7LJpwNazorgsrdHUMoOyyAuXaZ9+EA3e2LIc6QCiVekCGCYpo7U9ErGUZr1Pa/DnHN6rYMUb4TPdHezUyMSI0vJd0rrchwI8Skdh2TMUZ3anVNue7KnU9+l7ha2EZXlSd5vaWZgdbBMyNjmizjSTczLD6SSVkojw6YKPFc40BM2/jr3LAVkzfefAXdXn6Ak5YlLbDr4gS4GKbwSJjHIDkvrD0SLaqW89X3LPs24RrAOCBA9VBA6LB0Ne2+O+CGUYfisBSr2zIsxRNduKH3XyK1bHLgz3dX4jivCJ/Oeq0nxckMj021YhTrQs/hwkykR/fojb5YAYCtp83xpIrepLrC/0RhbY+u/pTXOVmx0KoScXtH5LR4Po+DxyZLn8U2pJdJdq+N2YrDA5HvxsWPX9jhm0O7dgoe73x/l8/T7xJiaH3F6OfHSV6jiH346LFlMR9AAvsbay9so6bhZHX9SVUHxCWpE8rk5HQiQFiGkq4Hzrb7R7TGyau2/sFw16ivhkTHOEuKIWS9/ctGkExxhhuWxEW7hE1yFx/lZzhFmX538KdIlx8/oZ+hevu8vC6SrMQxbkhNk+LS5+RWViEwhPHkVtA3fupkhDM858hBvdimHplqWzGfbeS1lHpLg+IYTSU0jhIcN+bC1FdNaiTnIzXOLxjldI3Dd5BirdwwckWQP5Hsl3WKrN5Xj5SscosmP7VqH5aKlKW/v5QaB93w/gPZQIwe3XDy2Axoeg0/faZRrtHkiTJP/27RbA13s2H2AsVuufVXOM7T6SNRVMT2S2bcIw/xD9OdICl3x9rzJr+1ZxSFG/C+CY/osNqo2zIsBJb3CpyP6NO8eI8evyZpPX/rrWH6IadrztR3KnytYNd91hCzHb7VGnAdJEfdVqTd1ijCPhRZtMQNq5gXgNSxUgunnDQe1MycYRKnhAX5MN3AsyG8RtcP9eY24x4x90XWJg7dnROc53j0oj4K6Zii4RHLm0FCrRt+kdDcElJXM98Y0tR1PTcRrjVNexVK4VgxXp2K6A3i2rIagH3Xo7iFTFMd0+4QUB/MEHVbA9GC8/d0RI+GiNn3wY+olcTsaMJLPFYNg3ZpdOEyutQotxZ6OESSI0RrCpgOcqpuK5KFHyt27Lw8I1ZPPeQ1XdAeU7/z13OoxbXhAVh9b7j/oZA9GXCCdd7norOpvzFXeP7mcbhu4LEd9MPk+oEn97+Ejhhzq+UV/XEl/T390R8KOVRXmEB/hCQbsOl/FF0SSYkctMcuXwmZNUif44TDrYbdutVwuIewh/cQDqH5z8g/rAuMGy/uclgcVC4ZCiBQjN0GnJ0mfN8B4z3YEDrlE2UHAhO+0aBRUow33rAI+fOam1jtsGNcxpok5NW8IYidR0qxSbDMPmU7MB2feIxWj86WBDosjnTRogrSnPqheitOHdqD3nRVeMFvPsSJ0N0dDRYvQd4Mou6WSs9Ka4TpRZBM+nYVeAb6eFSXdKAPjgmUX3S1d1B4/wqqSce9sGZaQOREXRQitiE7tevkPnxbRpAcRMtbtGL5cU1rdbTMrop1FMz+GsKbmuyp4TyrQX7gZXVbczzzO8t7uwu9JRz1nRvXV2P02BwfiTGEKTu+CWOIh9vNRyyOyhLfZ0SKurOSOR6HW+QllPOSPXsYfvYSxx057DT/c5NGsFoNOi/eI5LM1/m5rj7fMaTM7nU/pQl+GEOXl9zwZoZtVZVjzbr+BEEW0+V/9xisr4vVNk+6rm1DCnXbqj7DNiZet7YSx4MIuFbKIzrYfuq2DPuYiS52WkVXxLzWudt3SKG1uc3m5raOhO7G4m/BDrKnbiuS4dSiiXagQd9uI5+4h9gns8LOyzYjQXAUCbcuZVWRpxTbTiZGcrdofB52sVzEvQ0Wked8xgP6XW3ruI5Qf6hhMVQRMbsy5fRAD6txo9S8Rhpo69s+5aNHEsPDF3ExOawiFqvIPHnKFzpKmiwMN9RSajOixDaXGNoDt6vbUtAtPF5tj4TIEAVLc/Zc/VEnBSov728nH1Tjw13/g0jCBk2YQK55rX2GhsLuMU+2JWSqIYZZ+wFn37kMdYsksnG1tnbHzrK0O+2MNa8VaXh40DMBeIfgsM5o5Lal0e817lttfrveIyj7Bx6/FMHJfQBUky0kXVtTZUGnO3XUvBg4+Vgssp7Haej0kYypjG+vQsvUs8mvbq36+jdJAyzvHsdB/anbimQdwMlZXa91+SfkjXPA6xjf4Z9Fgxuo4UXfMSj0rC8PIRktCrCYx7OOrw+re6hzUbru4psX5N+jdHtXpxkqy/AdvIQymlp5QSNY2q6wJ2jaqWrXzZc2Atz0LlQCvyVlO8B4YQ2jDurMe4nAN0JVyaA31FCZ8KZqQQxI1uryJN945uintV9xKHaDx9rOmO6ux3JrNI2hx+mPpSilvRK1Kbm9wwgyOjexNwPgwNZQucTEIFAwy3rmKe759ZCb2OCLSbL7OrkPP7ONJIPuF3OLGOLo2OwZU11ztxotccIyD7g3CTgaDRGcROJouy3yH2jd4jsBwmxdr1HmVXykkVNnRE0wcchp4zcy5Rrb6VLlGsuWxAFqvMCOCsHVdQzhulfi1mc4asDLChBP/bWmgpcVQB1aRZakR3X1QNe05sLaJVoRvvXZPXUZgF7pEB9MBo0SaikYajKcbrhk85P6Q+ksN6O7w9rrOHGbbI/eZmz5M+Vlxn3zNHW0WpFt6jwNkj9/4DUqpnyWzOgZAxWnTpHcDDUHTWpVQVoC7GoF7bjO8qLeXOSlj4uA1S1f9SgOKlTd1nW+xatYbukY4bbzb2bOL47W64J5QCeOD9mHrGxa/dKLFKhM5FJJcwAgrtYjQ8HY1tBFHhDq5FCu6SYHFK7P2s4EKTSG46DR1G0xKu2MRqOzFSNS6aq+/Qda6bTjv09zQelTIwhlWPe/YrIDC3RgJGVFexIc89XiiTXFHb5G77o5op7HgtCoR+WKMC6Gda0A47UmmDrYAoG9Yz91XWsAwpeAQO1/UPzqthiBfi/yejtxHjvbvMk7E+EPanXG14E6Oc7iQDVolH3Ic1wi/rVyQg8yrNbmNzyQoM25Mlib8wDh2rztRJBKZzgOel2jZJ69Nn6WMh75/FGvMuBzJCd1IZ4fKfWJl7po59ZZUzQpdJr/DkpC3RYjkDHJWaQDB9pW8G2IqAHBwS964zQlxGodocHOCiKCWw06C/JeEbaoo3UkDraL5ImeJkdF9hFVD/l6ypMkBcec1EWBstXTCak5Q6NNY5dJNRjA+ujxv3jLwnXy2C6oMRxvXxNz7sSIauWqvq2ISkzPs1V6TdHGCrXXNXb6OE9j17QxMjcrGs401whHjc4z0lbrzDPCtrFZR0YachBl98ZGypGsIpiaCEl6htDUNFW3PDWB1S1PTe0Wv+78M6KGm5xJO1mfthWmViZrghjedbZGa9eEsM7N/EyK9UWOs6r8hgpE2C48pvfkAa2+5/Vwx3vOrbTU+CwpVDvTI1YM+9HdHU5xhNci+/3BdnIasKBquqkh6E8KRPTXCeGtse3kzVIEE8Uw/UTSLs9ihku0mfAV0vI7WqumZNIRnvz48XaWhk4ft7hgjoSPeTak+Z6pzf9CyfT05OWrSe76Dt3iCO9b96iOVmzdfJ+n6xn4Q254JsbkGj5Osu+zbICFNmdRMXyb5ydzNseuqwx5MuZo8vw2mcG4aFdTZgD2V1inlvuabAgK/E+maViKimRFfw6mwexNzyIyqsYvUcklA55Qw2+pI31egsuNzjTaq/q2t9HnHfJFXawekhLN6cC/SLDv1cHOA9Js06efl7Y5uj8nCmdbN6cls3uNIz1wvbvHk/xO+BLRozfusV6rY4v3Cc1C1HpxPuVV/6JZvKPO8SkNeOTJxOhGBByOPaFyKVICBHKNydPGDTYtQCGD4xJF17wDBTvf0JeS7NLf45I+kAB2EAK8aQ9xh86qoaTjZA2o62ubv+M7tpEzDgICvPlSovU3XD1IgzFDS4OyqOI6OFaL3tXU8De7yin1XyiSOiuWe/WMCLYi1XVfrOjZUAT3jCt37dklWiO0QWteiZ02FjjQUR7KyBRGYGkw5hrOr8uSvqsvC3elMtnHJfKblONin15trbSxBClqOwFAofREqLDMmaAi9HhlpkFTggrzELCibqs/Bg308DZaLdRNHGSXGVdb+xV2qGFYZLuPkqzooV3thVanTGEgWHV8rLm8JB22FnwD0SBsBzFXt9XRK1RCRytoDGRt1v6JDhYnUigdNe2t3aGGwdDtPkpyqYd2VSiCoTWH0W41INimC1I44UrmoFjUbXV+M04rQGn67LRTjEC5iUMVzsuus+yp1iSCN6pDeJLX25mc2peEGFuaYnoWr13f2jwJbq5QRvexc4ysaWqeYX0kmy2WJWvidugLcB13MPfh0v7WiRxQ7kuyldtJWsBtRwNrUu14blR15BEpQJWrsgo+aEnmehh0A+WQw9RmM06IFHw7JIqdf7Howzai5ZvhiqUxnzqEt29o8ojdOUYzy0imjjDufBuNjTg1ycatTU27RcJc5w1v7Z6/iZCG9rzskEUz4z8QAcmGp4Uct0BUWzfvqdlziOEuXZ2tU0TMrGT6QIZGwZ+wPHmx+FtpKB2VZb6iUcnrzliBTz0iWkkqy89kVYX7dx0OGsHjGuAg0tog1bwlMuC+kN8SkQo1XbuI8ZZIf3AZaFdSFAe7Ut1WFGuwmadgjeS+GLOrmPkQ7LGTe1qDJrA72AfFDTj49xI24GA9KOs0gO8ggxq+i3TVviF4DBzHSZpkw3Nc/mdBE7tu53KgTaQZtIFsULQLFNamg5PUhhY4/olT5IAgm9FEPG7qwng8dGEXUdPHEB30n7qtKDbIdZGsvhOKzxTozS7pxt3dMZ5BvuHj71CKf6DiybP67LaPdXCdKPSK2DvXkD91ICgPcTPoALmDIwBlEOAYKijVE48yhlI6ON3NEsnoFCWSzuetuzj+cqMwxJQDlbzC0uK5XyHsmF2iP2rk9YxD6yAYoTnIgUYOYiQUiyYEsbZLcQ6fLlFS5tlZXjTcNL8bpOVfxNzeUQ4IwjqgvzxlN7XiC3rTRXtUyd1de/Ni6oOV9QZn5qu9//5LlAdBRrotTl65Hbpi57uj5mii2EtDEMC+EwQL3GzmzDg7IYtQWDyFiOmwsunkP8LKdpEUrX3jeELYnOd5VOSnOEaEZTQPZJywkGVSLBGRRAXhIRoWMZnHMo7RsQ+qeGBuxe5irKVueHh+j6EEA3Yaatiod4ylhqC1RAlk7re3R1Z3viu1AhzzqmDMXY5y6CulWPNfBCVUh1VQ3ZYhsNr2DWFXjy16rMinzXb6JCQ0APqPGhcRnhWnPjQK3zJ8LLzn5XXyePqIOGr4oiKITggb3OfFU7SF+CTPqiJPY9ga8Z4kOC9ZpBdyJpha64tag91lg93EMOwNoMMG/WpbR3ImW1cM8i/DrURUwgzfQROr25IoNvGDUxOpdmZDH63/QfiGd3dEt6abA7gZGjovCSYi9mgVIaw0QAPaa675dZZoIzorO8+zgFVdUL5u8yMF30NXIDxoLXVbIsmec+YJcawKTyPIQzdyZd73aFcH8EZaVowqZ/EE7CBZGiZ+WqWoWZoDpYEiukAFzoMTRrDQF4YvMNbwqiLTroxXmWszIMfRxMhHeJ7hCifpLmsyvotWWuxmXEOtukaARn01hnZ1eSnX/7nVspytzFGfe6llxvTk04f83kMjk1r3NCCIw3LQxuq2ODLt0qlLvHzRu6GYBDKDoszB3Ejwg/RqwCS9pIONenzANwSdHEDl2t7GOYGWyBhDnVD4g0pRt9VkWiY9+pkXuqTPb6Zx1BjcQxO9gHuaUWBHG8uaj8OWwsMSqG2LkOcD+oHS8Pcz86KKd33HsfUzAj5b8p1tn501WM487QlT+Mo9+lJMH2dBmBwVBSrmaGvGAAkDpxU0SixbhQZ85FlF9xBJqXuB6N+9uMjPiFEZL1qjJZ6xwl7P5t+W8FD5zfveryRUB/2vbounU3AqolgbIDaD4W6tLV7tqKtGK4kS/4JyqYaSpFQDGiSzFwVLy9IvvL4CO8ZzkFaNtMYIb6U8FEtSh2QNwQZkffsPtNJG0v95srCl+S1WGsGURIgyat3bx0/NI1gREfYpJkNxTqRDeTYG9ehYrdyM4QdFqgGTNKkO1tWlxOc5Mfeeh1b2fQAy9ZyDDPMxdc/Pemj/vm45/Dwof3Vb7bYzOF4vzoFY7FA93eMoPXvcAA+jSIWya1WCCLvZs3pA6zpF10n53YPtabXyFY/kwPTqtgwOzT9P49A8Iuyi8+/YNmtaGPPs9HFLOVJ/0fNNnDuEzSmAspW/7JBzGDR9t5+z06IIN3I+EYvvsvbx/nxI2OXLovKse5qtfVutVyvCJ77t8mSbjsHOy/d4TQQ7dILIn/dUKi4Q0eNSHlG7umZ3cKRBX+Y/W23dDxtnCY1YOMkzGhpAvYUfGTrW1ljm4A54KNTmzU+09jXkVikx+HOvtziuaAQEQfeqR3JY0dRtNaQPteIaLMtEg3dbB2pdhY7jnNizaUQzku+b4rC+5dGbMSh/XA9BAAf2IFiQafmlCJHC/FVf/yCAz1kAr9L6fv5WY0Vkfkiy+5oszW4zYP+oFGUMvAq5c0oDsPLslYjpIFRTCxUZ0e9FXm/nZ27S8vyNjl7Wm8/37HGKYC191w9og74mBaaofLwjtH75aoTmIHfqthihInDuLLcOw6ThbZz92pTcz3D72G70S9n8d+D2yfnQNV5IG+A0lY1XprE8Z3R7bogIiyPB7/NSm+MtksflQ36fX+AVlYDdSV7wvtqkx/maM4GmC4Zrgse6DMGfUPUzL75PPrsXBSaK6YmJ8Enr1wpPMMVwnj6uHsiuANGnpbxRa7LoKBuBM+vQEd5oa3EpdkzAcq4dYw33NEHyzJhHJoArhjSC0o9lDBqWOajvlvNa+g4XaEVvZr3qkBxWVHVbxvO1aRyIzcQYHtL98yTZTe1fr/uL73LyIacIwslq44Y9y4sNmXTWwrTtfcAbTFjsOm+s1eBDHipg5cOi6TeWizJ3HSPTbafZmsxsdAvLbXvzMWGZ4gJ3OS2Wg2pWt/UM/NQTbrWPk9X384y0sPoeeG3gJKmSNL9/pcB4YNGoEwye2MdIMhvt9vUCIf8K1gMD/02wko1urBAzK6mqsQs5OakB1HocFzFSlX5MsvouYT6F4hptiEHhd3LU6hII3UGRqNtaZn36itFP0lX9FbxJWva1We030G3G0Ai8LKI68PGBj2fj41a5R2BjAdOBiw9cPBsXM0OJPkXQGkHeTDxGdOBhdVv9ruJNpN3J22VOmuxX/CIvS2KDp+FcJqI68Nmu8pn9Otokp/uIqofcJ7q0rV++GiE6MIZmQnlCNWmhzOvqNDdwHldpvUbrcdz29Peb+nbpm+H08QJDypyYbXZvozbkn2O07ShZB6gEsFP6x2C31kec4U29YeuG6+vrBszJ40SYG2yXqCS0Zm8IRqIFTfP0DnG+3nBR8lKmf6tRjdanRDGmR1WVrB48Eyi3d5TKVyDCg3JVt8URLDjxBOkJmQR6ZjtiKurNFGZEAnVd4/W7m0jhUWcYuJZjME6J4SfeXrMJWwraRk6UjPAj3iDa5gwtq3NTYMXtI1DQbxrwwTOuhpKc4hpQV7c+h8qh76NapiFwXy1HwtcIcvCP+hlFUx/0s0bTFTgvgp9FpOw0/+V/2qoxLCrKffx8/sFd57MM7RJt06co47NqZ5YxnZxM3sTxajV5G+ZEW5HsEBoNPX0sdMyQrSui3K4LHPwOBkHjlVy0WfxWK7p7CjZrUbamp9BJmvazECFLT78yw5l6ItsI4hsM9laF7YB4kptGdDMGBgcygtHZOGPAINNm3C1/24bHczBuDGKq2178eZLI5fYEwbSrsmzcfJNn/hFe5IWY7MU16LYkMxmHQM57+7I05PyfqOXmmthVmTqreZl479BdQqSarKpMFkgtN6QO8SmbbYLvfeI1e33V4TjoKnVbBm0x1Q1zo405UcORbM4l7w0EXbOwj3dshCggrqYXQwHVQRq9pXEijyjZVTamXZP9Ek3/csXz2mPGSnwTc3sX+TJW0G5RuUd843EUMdR+G1T7T8ba1rryE/pZfkBU1ANvmvQqE8Z40JzqtmCKBb9YsdBWLo4+iev7mvCu1keUlIRpm9fdg650jzAd5EUjL3pLY6Ln5xZ+/e6Skm7qy93TRXC2zP2OiG9W+q0wkqT0yA7CchCW5yQsV0/Zyv8qOg146VKvvOJQHaRE3Vac++jNkYg+3eKbadKOWF2Fn0ja2gjIhTQNo/kkjw2Kw2SCeO6Ys72p1fVp6hNj1hi1uSX726Svs5WftW1/kve4zYvqHdqm+ZNnTKmI4qDR1G2RP+8gL4GjeC0j1JGyA56XzWTH2Yz+HW9DEQ2sS/GEzo3FgWCk7Hjm8784DRkTOUbJ5FhV2+siycoNZo/TxJgKCOc42pqpLhjMNYaPiHVjX0e6UWrj2bVwr0eaHtaexYuJEQcXx21NpQT/QB+5xG8Bp9U+Z966rCZ3qrBtcVG96YGHICAVjBQEpAQMCwJiWPt+edoNbf2D0aBua5nV/ixPyU56obYJR9BfLakmVzi2lyWj5LTNf2BC2EXvZ56X7VLVCW/gMWOktNHNC4WECPSNwuCrezglvDPH/o78SZdF/f3AaE0x7iGb+zt8z1+imqzJS1QSvXue3eli6+M09fnurkSB8XjsmDoMxXFSrR6u8D9RoOVBhLzJzr87h/j0zTb2GKuL/RgtaJ7s1Y6K1UOMg0haq3bf+SltscE4gsPMg+wxMajcaLjZ9nr8MKyy2z36EbjYcQhKYUqCoG7GZL2R4snVt17Py7M0uS/7WQ2NOFe1FM0KJdJBtHT6RNcyTlzH0/oRbW5R0SmdNL99+YK9SPLby18kFhjBUvOImK+IprRGfaU3+krNtrSFBQJTmgnRTZJmsxw8QaPjEXVLC07QFaaq84KFqdtN00fSEbwl3aUZs+kAR5Xf2EzBUVnmK8xI2K1vLNN3o6jJ0szE86Z74Ezo/2m2ftGIrbbWIOSDZwSqQNYFNiJCTCLSv738P6Th2zbYK0SuwW4IQiNvxmMijXzOmgfFXxwxm4tGdperZC2rTULR9fhLKzQ0zJxYUCVhD5xV8gYRZysyb6nLUAQklvtM2sm+ObGErA8oo1tElzm06Qf/HJ7cn75ZgZgm2v36mmNWCx4m9tW665slA4NVlNzLQzuzLtzU/vGtdhxzMa123vaCY4kt365CwAuspZJr9dUgzhVruDCuoTWAeccP0mpbciFWnqZmiR5BgaTI6TrtMPwRwr0SVbDrM0gnOAf7IZCk50dZ+RMVN4xPdEzBwan4rAFx5TYeMcBvEAPvBq8BHZ+J24C5sGmZwi/Ga+PMbjddlrEuzRzWLADmqhBHjmu5cKVFe9Ay0CSSi7cCXJFdH2K+SrJBvenQKzsNQYN04QGdyAK2YE+J5aVWO4IZZFc7Rzbt9ykZFxLi1tdpZEYBDmLDFsSFAUWs9qz3y6tXsmPHi4UUfZiBeRQ03Se2Gase0zSPpSUuC41xA4yk1ZLx2Qnsz4xMBdLapv1RxcUYrH8TZziMUXGADAqx1vCCjz1vAZgBxrJjWp+xH+OUHvt2DRi7OYaPTgUBvT0pZOnyoEb72O7wFJOpv2IFHT1aWB+ySM1oHAq7Z0CZRjGDxjLNl9V6yL05toi+Ok7ze3qOYXbwSJAQX3ZALgwpI94rZ4+y+zOwoHJO9sLpQ3t/km/YkXnPODouEYFVHNjCuTKhhB7gQxWD7wYfqkYwEyuq5sem+a7Oci7I5v2MC8we3btp/1d7IkFw0CE5gnRySsJtQL5JGPnyXKkfwhxeSu08WTkrmyo7w5jt6zy2TCM+hjkFYwoPaMpt7D5jjoewAGOO58mKMYd3b5fZ67bvOhp1pQgI7mhaGKetjIjXXjPGc5yoOjHHDkRB133Qau9w2WSuPdqSSaGJqNrR6M5fdJUgpurgXZhK2wa0R7Zj3CDStJLuQpiuynRk6VuwV/sRaNL9uER/1LhATYivsdNQLRfKRF2vbPsH0BWAM0+il1qzIt0MOs6KRDb96OovbcfdEL7BP1DxxO79GyysEbDGhvMw3saoIV7j+znZ8qnrzXxWGEhnK67i6i3NWcd1tk7ReYU2R1VV4Nu6Qs2tqZuhxMRwNjg0fKis7sGgVl1Rrz3cmHd18+EywvlkwYUFrDw7fa3dEZB2KJZ7aVU9K0EI4nyhvT3cYJvGsgRfw7Noz8tLb7zlAbkz8nws7MO8MU0LdVcW4b09dPS07ffuhn7La2ACqYKG23z8P8pmHLbpO6MolaOYj0uV82XTha7OznCppU4U4Sfm0T1eylVjWIBB91CJ0uz4dwlzuhZGjzkEDDEmD+fClSD+Jbznuo7MwFc6Ou+DF71l6tEwRjxhUEvKihot6MtzxiYB/tO1tTN60Tii+RSkcT5tusLX2ynOtlzRoToz8fMer+66cSzEwHu4yrftX23RCt/hJg1J70azZWB9bQ0rwxU9mNrQgz1kb7sRzcfodnO8DywPj+Qze1/nRsGRKvbzwAXeLNSgcbpo6NEd6AqPlVguLyoBw51BcAJ4w6Z3MIYdXUi0DO6n2/XkXWzJ0XbLWtZg0V9e4sLHvviKZcM3/vLX4FlaCrtl+Tq512SHkWEjR2zwmNUmGCmOmPylwfk1KXCSVf20nOSbW5wxQKd4Fls8GsJpUHjQ1LpDSwfIuHZ0Pr3gOqc2PdulsBrd+Cw3dBYodoLj93h/5zCs3RCNPdzpWYzqb3XCMsR+yXCQVPB4dkI0Rh0C5GM08CUXA6iju8Hx0Jza9Iyvt2u877sCeKj9CAz9nBT87mj1Z6TKu6GYPXW2CJZhcK07ThrjvrH8Et431/l2EIKdcbaJg2OpvW9U3OrImlpkDlLC8EQQFX1/1GJjktqdlR6rAS8nSVb84SBVIoqlhctpa2Br//t40XbKkl/YXH8WNvnNVULfcniX/8zSPFkbeWsMHpm7BOSQ27Dr5+S8BfdlRu6CKW11GjCquRiHXWNUkFHTHKl8bnUVE8DgEIf1kC48pkDvmAme8dnSa7R+KDOwqH6qbDrA19sBBjU5IiTICdhyH90Lyt7PyoT760K4RD8w+mnrBxtDa9beBtBjBRZa2CdW1I5gvmUbnqO9Y8n3KN3e1WlGc76OmcqKg5TVjUzL1fTmX3XraoaGRWbH2No4sLn53DjPDozfVFyM/T+hnyW7Y2pM5ipBQkzdAbkwsYx4r5K5Krs/A1cq58Sm7cWTudLed/k/e8bRcYkIrOJAj2SuIHqAD1UMvht8qBrBTKyomh+b5rs6y6fht3vYDQaPnoh+oQfcTh8rVGRJelRXD5S8TYil8KickjZWtSFS6Sq6kM+uA3uVvd5pSDPIu9Mcu/hGFlMAZ3lRb1gOaiODy6AQN/dQLqwLoHbi0yjeYHUnZuAsNXH3h42u8y1eWfLRGFbJSAzMmZME5AuxEtyLuXgJJvD+MNMN+/f3Iq+3ek7iAJVs5MxBPFKAfbi+7dyaqer/XIwHzIdN00OtXVBiDddYKJlmyFOorwazivl2lO+Ars+r8EbzYc13O2B+cfxitpK4Acc3wTjkKu4D+XpHWFAxhlltOHl+bJpnFRZjxc/F2v5JOggYYkUG58KGIGL7l+gi2W+6XszASDrq2jQ/rrkwRxn3A2OwiFy0nz4PuO+zcd1e7hi6nPVfyuQevcekN8VTnwhfyXjaWrpXD/gKPm9DwA1qnjHYPS61GsoMTGs1hzb9WPzZA3AkjeZz4qdGjOfi3qY1gHVBnb2jfDsaxFJMO5o3m06wCssu7uy8S8+jApxyeXc9QBfx7g8LKno+1wovz8U+MZspfE6CnIDh9jFkTtn7WdluD0Plfsd31UlSrG8uSJcfkhKtv+HqYeAgFbsY6kFs2VVx4UpTMyq1CHF/vIsVlr2agfcsp8GKE0EMizPmyIjoWcjEL2AtHVP6Wo36BgH2VEnB8jrUaigz8rR2Dm360dXZLR7+wouYGyOPqs7GzeNW98cQtR/MUkwNzqdNZ0YVlzVbP+UVstkjDXBKk5WCOJusHN79YU1Fz+cyVuW52P090iX6ScTnIicIyk5+jL53XSWIDQF4F4bUNrdXXnqbkczArTbztxcefGggdoaAsaa12gP3Py4N+QmM3KxLUvEHvGXh53oijcHAVOAthFPa7zHW/Vle4I7PIK/wPOz+4tL1u9kzd6xi4osRtI7pXH1xcANQlmsFV+8OC4JDmJETwTmyab9HsGx4Ae3G1jpiRYCOGGwgYraPWYnndtP2ZC7bWUVjW57a7kDkyiWq6iK7RH/UyOZmBAwOmwMcpJvlDDaxZzazbgyzWMu6edoLO3notKXeU1WIfmkvpgJ0skxy1uxJUlTcm9XasxlNHdhKGYO7WSrqptTnh+onpeMtFhY9m8UMMU6FTS+GWguayMJIjMuGssbkTLif64dxGEvw616uItIoTGEVqgqTc+o+xlqYBrEEm+5h5MVFnqZf84qMor1gTT8cZeVP3XO96jpgOiIB3CkNkaYpiFuHzu8cw1oMZQaetZg7K7btay1npD+g1fe8FnMWS5/VRrslAtCIB+s6mfS2rUPWgzTGneN21+HNwPqu821lZIiVF/SmrOqCkOL+InliXsbzDFO8pnMdTS3YtzKu4OZe0TVmf7ARZWdm1ZlZ3CUWM2DVD67eznBhd4QnsY0tj6gQ2PCm19m5ZfMAt5pEY3ml7Dq6BdjfNN82XRLrLiYNdFp/kLn/kN/fcL8pzygFQFMH4nkOxIXPda1APkWh8zvH2RbjmYGZLebOphdC1Z1gX6OfDQKeiGH307GmG8HMvLmX7jQrLjRxnyPX+XLbTrxqsBCj7S2DsXwiV/VtuSpw82C4XZY1sIoyZQwP7Zw6Bm5qodRr2s7MwGhm4u8F25Hx/kgq9BGVNCj/5qzIN0a+09SBE8Lz4G5p4NUNzc92Fr2Zw4VqJr5NL/h6u8J817kr6w01JmU8rpnF2U7uy/xMJ5Pdpg9DreX2FHd3OCVf0I0ppkaCBHcTHZDTXkLCPHvuK2UX5tgJqAhrtTddOGjwaJUKmaDpoDS7Uggc3pem7seTCvSOydSX3yroxzHL7lQ3Ty52HK23XMhHlRf0+Sy8SYqn08fVQ5Ldo0siaid1QZpYPaljP0w1wSAQWskp8sPYCsi6bd+nUYXWfZqBDa1nwaYvGjS7waDsDzfOHFWJz5Jj9AvzItiZuZkQJLgD943qL8Z2x8nq+3lG+rL67uZgMVWEWFBRx4Upjc3ulefZdjQz8LbtfO6F50Y1GFOcp6HezDy9j8GflmNZkKH3MBT0bzWq0fp0k+D0qKqS1QM7dz/Dmu2VugrExCC0CwtrmnN92nxpDjYPZQbmNU+flSMTL7jj4oZw0wxkpc9HrKpgYFhPNh03ATDpqM87p2dNI5mXR8H5sukCX28XOJUTNp7F3NQeT5j5lC3fKsDOGonZKW7Wj2gx1QvMqU1fuGqLsffp4zYvKtK3O7Z8rB7Quk7RdVJ+V/K1ugr8giMH7fZko7IZKJkF3/NpvAzmDs3AgGbi23SirYeze1pzYeYjDaV5Ey3asYmeJ+QKasYbYN15D2gHMgV0DL689jQNZTaeVc+a5S7rbj6D9ZTUqZ5InYrUQEUnNZukqD7f/gOtKlqEHgkjrJg3I8myvGJY/vqlRCdpQfmj/O1lVdQyM1PUV6jiHzYqX75ovnN81b4jJfGoUD15PEkqdJ8XGIFY+vInIy7yi94wg9C0RUYUH/JVkuJ/onU7g3CnRChz17qnyUFs/UvwNp1DV1XBrtGVjPmU3RPgjMgvULHBZYm7J28hxCKMEen45WkZ4fh40tTDPE3BXpHvVpWbm4MqFN0FTssh6YZjRNIeZ4M06SMATB2h2wqF1DRlFhLT5vX4iKqHHJzyMYQZIdEiiEjFD6JawZ6NAKyJzdRVVulo3oIYUR6n+T19xw3C1ZWZuanxmYGs1DkvDSi6d0IgHMPTQCb66FSntd68wKuqLkAcbZEtQXSYRhDW5KEJfXCBNorZB8DMqFGKf6Di6RpvwJ7y5bYDH3KUaMbOZ35xRdvf9DzDaaVQYoY6to1qmWoMY8FbDfzHJKvvEjb1ur7zYEbUJpxOyNoOXG3RCt+1b7/39NP0GK5g1pNgtc/MiQ+qTQ28Z2P2zdgS7zoBba+h1BbR16TASTbcbj7JN7c4S1TEMdcyNvy3OmFfvmQY1DN8ue8oHLpu24QNbmekYmfYfXWHzrc5CUxauD0mATVwf4Ri2j1gVJBtHGxq9IVGNJ/Qz1Klu7syI5LTR6JjsyQ9qqsHumdrhEhtTOvgjY31z9FCmLk3gm3QKHdw/HPAZkSJCoNdL9jz4MpetO+xGxCxS+gQjvZGv6XNwT1TAC+C8Gt7BuzAIwgwdvgdC0vsOoR29FMJAve0kA0ammZfiaZ57MCARk6vDdMLTMNtsfFR2ZRDXmFLJCqKjTMgG0fL5a+EhzlKL2rs2zhJFNw/MZmXabshph0Btx1ylhhXtMoVR5X/xkhb6La/ipsU2Rwc27DBbfYTDLdnQVcBf6nZHhVz7unRNdftjf4osFc2vZEu0Sk1/zjuyGjB8NdVYJtlfDXIRLXu0gJIr+EWiEkauXMOUBJHpz/GmUzVVgV3n8CA5kuhQdMXmhcelCFiZGlVgghjtuseyFae2ZW3sONwBGDhlcphp0QbNmz0QrE4VoWXpQ8NtunEx4RpXGVf2nKzFwuO/wKdWqoYPoft9TXabFOFFEBwto6rJx1iEcZ6G6rBKYBYaHUKttb4+8YQ5oEXeVmSiqkGpQhjHniznKi9uCMAi80wECQA74rBYA979Aak5j0XF2ED7rFGYU5GntxsE3wPClFXZpaaZmHRCswYxGp3+gFVZLdoEnUY0qLPSUmk9xvC9w8gGUcAtujeYcINpaKnIoxZez5lK53yHIotNurj41x4cy4evlshbU951Rj7Y3YBHXeOqz/vuxlOC7k6moO/oYJ47MznG9HU62MT+iErTyKlk23bJrqwBL6J4cRUDCwYE8uWkPx5rZmKMLRhfGAlJf3EY2YT9WDsE5NOPPK9EY50ZfIZaqgHqa8IkRE4s9YQ0YAfIKQw1nBi5mmqZb0xgGYoPBxImeasXEeNEYqpuWjI9tuc1sND50H0PecgVcPvT/sNROBRAWQAKelBgvFB+g3RgUQXrojlyJ3jy1SxqKUenbkyRDspJkBDP4sWILEaRhxOVj6e4KaPewAoCQJqhgbBg/QSQh505AJxTkyhLtuChjYiiHoEAiREDy62RUMKEdFMRBDCT9SkGAOaxzGe2mCyjNEBxNFznQeF+quQXEdl8gBQ6sHIwBBhuIAqDWEAXABVlEQOIcgxTlPuMSYdVQRQi+GMa0Sgj4BwJiK10VfDzWoNlSRY86jEKjo6DbFiFuSSEGuswBj06iLMtFagDKQeiAQLkYaLedPQREY1sVVIGzzJNyykegi9g+khwenHIYIHMwyIFKCPktQ+VvMoZO6mD50DjGcYUmP4ghVAU1qM7NOZ0zBWyKpWoItApc5naqYSdF9eOx7honwkKgkX4WWsURR160zXcZEEo9GfAigoX1wcoU4Vi6imZZcuduPmaLtNMVpf53w/ZaJo4dWj0lWDiMWFA2topcUKLV3KKYhCuYE/bejWQ7uMr6sUk2Y9zmlFrm8YjmXWkAysYDFCqF4EwoFoAdrB44yl4G/GcdlK9T6GM6rhEbhGtVvp9DEyiETj2PNYtFHGfPMvpqpJZlXdOHgbLBoC62LdzZS3alwt76P4/fiz0nbCbJsoqzgQYFzTiuKOJBZamFaNyq07kdGHgFOSblai9bbVcF9DSTMZ1jgsqYqGYpbWoBLz1NaN2LCZySRQ+1GZGcyTXPNwFx/yo9tQgHDqwUDgEImEiz8a8oAYZ9mOjloe30RSMpS6jpEBlFU1LGZPRmMjAEm12CPR1SyjILjbQM2yGkbIWRcE+IKXBSENFY1D1tfXEFd5ec1MZkOb0xJcd2PvRnXPDji29ECjOYB0xwYeeepvL+pOQD06AJ1+2TFFdDnRX8N0FR4tNl/u1lN4RjHTdsR6ThVMFeBG6EKA2WVVtReBBzPv+znoYB8Cj0uto5r+x6KK5u6srd/FGoWRAraYNJTW3yA2T4J1F+bz8ei6ZF6/bWoHUcW8kk85J7MaUbqOjC+Ke03HCEUQVXhMC03MqAvA7AgX76eaIg8J8RcLD1nwovOyXN+1rjVfret6D15rqE5Bba1hKsJMSH929fVGUegwF3o8zmTSonOYo+4OsPtE6XugnjQjlwQYnZbLgqPut1XwdnbnQqr65irZbFM05PxQU0eANA9pXCGYQgI6yPYbspoE06fPXzJ+cwygjwJSPSC4AkQfPsOKhkIKhDPcqRha1izxMpDNUDTLuDNZZl2qL9EPjH5a2DwCoFECxvDBQXUw1hlJ9B6l27s6zWik7KjASDN1TcvhKhHEpaq6GY1sqprxIHeXPkkb8CoDqUcnwUL04hI6aQglo5o44JU22AWRDnmlYHpIcPpxiODBXAQiBeijJHVIyL3xkqECUhNlCFaIEG8/+yVCXd4wbfy9XUX1kK3qQxQ1ZEbTUNmuyYlj9vt0alrqAlDqccnAEN34JG8aIgHI5qAISwxnJokAZhjGGFpJlC5lnYkqAro5yHLD56tT0ISHMYyAA1VQIzGTgUcC0GCUfy8mc7Tp/7Sc0cBYzWMzljg80eCCiSFRNEhp8FkUdWqDg7ORdW4sMVQHh07FITCBPWjDUgBaXKsD4dSDgcAh2nRZIjV0AVFNfJWuaVOnTwUIU/d1WtSaBnPpTiil5s2QB10dLA9XUA9LW08XLC/m7bSInIfb0ETOT0bJNgGqJRkbaMfxNfwyIQGbBgDqwczsK4Jso6OklwhikJ8BUimF5n2qiGkeEmi8RzKQTec1XiNnQszjK+oS595ckB4/JCVaf8PVA5cIVyaNqYp6cIaaENm4pL8aqpkQq9gp1q4eymd8M+QkVtMQrmAeKFhPRz8HzaRvAyClco5iUfILP6HW5BzXchzvqPKUhB03NLneo2mpDaqfAzHoqQFSqfHaHNkmjcdhmpIEQB5trTmqhVcPSVcNopQiDbiGaNoWJrZjobaNEmqu5DZYT7FxxOs7V57Js5gnWklCAUI9rjGgKgEWeHdVh2dKweTzyN8M2enVRBgDmscwgteRxGyiwSihcF8Vlb23zl0yMqNHQQQ07YEF+KD9tIhrYqfC6NEAgzYHIXUKAqoAa4Xx0wZarQMinVhpD503s5AS1uKsyIKRHE+fZmYn8cUIw/VyHbhOhShrwbpJeulCq5/UyOe6Hi71QSeXamCHQeqkM5B+c8mo1LDGP6GEdRiXxlsRSLGZ4lzyNP2aVyzdMjsu5d84BYJbNOCaUBN1rfAwFg1uRZ5WRcpXnxUBfGjmBnjbBlghbOtq9LslCpDIqtd0dOuJbXuQkAOPAkWwW8Yv5dycZ5g+g63ZQukq6AwOTT3YmJHe+dHaMzr00+7mwXeMbuQ3iMzEVNa1H7gKhQ2JbXf9li0CVDdOqk/K2eE1pRvpZSWZ5jpw9aA1tcAktKP3pDTE1OGFbEzpcamo5NPZRyCc3cB0VpEXpeayhUw0saSFiQbGsc85ZukxMHPoEQxtiIYAKylDLIQXWUyhFjDyiSk3fvLs5qzINzrS6cA11pq6FnztQnioTRvPrEY9L+mucwfCccDWYxvqRCYah3hikvWP4t1onCgykEbBirCguube6dMpawnXxA6T/jk+4x0eBaRu4YEqwGtZahNsq0A4Q0jzFX36juyL8SYpnk4fVw9Jdo8uyTQNj+sBm3xjJc2e3FQXfughN6X2NeMFqTk8LxiXlOwPaxqOoS0HOaoUg2pjhFOTS/E4ota2MNZRj9RUFUzernzqUUNSY0NT57lXtK9x2pmquA9W48KLStR5HHrgY4s3ZxheTjTQ6gGqK0E0VD0TqaGgpoGJL9NyLd+MX4XU0m4MazWwURUD3aypNUYK0Ep4CXMibhu9zWnLdHwlV9bgSTAlC/LtgFff1VPmdYWLewvyZvz4s0xVDbR6mOpK8A2t8euV2itZSsTQKf/44etIlBve5by56B7UVNENgDUNTq6iptnoGVEj2QDMkCxrZ0NDtV9fN1jo+UqCM1T0Zb++pjOxSdoPv74mICu0reok/ZivUVp2Bd0bq0PN9suLq22yokcF/+Pq5YvHTZqVv718qKrtX1+/Lhnq8tUGr4q8zO+qV6t88zpZ56/f/vLL/3z95s3rTYPj9Wq00ftV6G3fEjEByaZVKKUPgq3RGS7K6l1SJbdJSebjZL2RwK6IGVl9vv0HWlXsoOlRUNy/9qTuGmwzCTT3W+Q5pNDUrdmB09+ttU2bYhbrK9qnV+D1nYGGZ2RYdPLZCBE344p6pObVKkmT4qJ99rPt6fmajDxP6002/C3ynrr21VNZoQ39PcbCf7fHdp6t0nqNiF2ESe1kK/RMKnXAXF4kZfmTbEhJQYXos4ICcgjAHn9XeYx0+GqP6RpXqUDM9pM9juN8/TRG0Xyxx/ARVcl/oKefzUafxzQuccP4DnEPSYtIR4VueAGacZ/tcX3AG8Ja6+u822jyGKVCe7yXKFuj4qj8htdMP/NoxTJ7rE2Nv+eZMHT+uyu2b0WybU/UIaSjYlfcVw/5T2CmpEJXvMc5PecUBVosc5DlAucF0aaCLPdfHWX5OrkHxJl9lTH9+lpQ7+IK8lpaQoQFXVyQ7Jar5FH7EpHDqtVjktw+NmuXrvY0K5i8drmuWu9wuU2TpzacgMc0LtmZ2SYfaChM2ES3SDwmWVlzVyeYBbCMUbSfHAwlSgRxIP3HnWEN6DH5EC6RHpv34BcLHNNwTtMHEcfw1cGwaBP/iLj47w7YKEEQMcLazBAjjEKZB1YFQndcgNyMCnaH6/u8TEG8rkg4ZcPiyqq7qhO7Hp/UafNiC8TWfaE93i8Z/qNGVyinG/QxVqHIHudZmtyfb0h/qHNYHjpQ7GDaV6lgz9MPy285LurbFJcPolXMfX7GBk6jZa6qggX8lszfFmEdEzD6LmVGNNPIfNw1qOu9LE7jEneMwKohFLm4fWiEz0Va32PB4zAuccF4ndcrSa64zzsjBReo2OCyxEN6tBAJELF5cL8Zxa6udnHdnMMDejyu4evOcJApIaI99+jikCw4R199V7nmrECou1QnmByjEgeHUvJ4+og2W8E5x312wtUu300YuYBwVGaPlYUwC9i6by5atpGsJsBtrGj5kqkleCnNnadpoLYmGHw0NFhtH+yRWDq+PdqAmKQvWsYKpx7yz9l7ogYvWOrjUQeFMgd5TdP85+/sMvV1/jWvRNGVi+fYN6i9aITNCYOjL9VKdKXxJS4+njWIj/8+325uQX3T3XcM1TrwPU9L3aOqPI0Goi2KGLpvc2qeT/XmFhWf7742KXxGqMZFz3jPLt3nDWVE/r6vJzvqUUzHlI0YQKw5lMSfOAONyRaX/P58999Utn07c/89wL7vzpZnonXXrIiF/+5gtG77WyqjLg2fXQzgo+22yH/Ifobhu8M4C0TWsvXnTFrmxiUObtrtWoFxXDI7l4rMeZzm9+3TAx58qa09EU82zV3TULPxVPEFDrFAZAg0K7PYK/774rOkffXERlnr60+kqZtGJTU9fJ436qsZvcw4/HcHbEkluS26b/ZY2idj/guR7UOVCEclUqEz3k+5Gm1ftlvczT2iE8roWlST8nzTvoLzh0KHSK6kbEcjRHFx3xefR+4ZG4+p09aedi2Rlcu4ZLnVqXvxRxwn/33ntihxXOEBZvLc9vHvNVZYyE2Jg91Y0jd4xA3z8NXBcdNcHBr5bJpPS0Rtd3XO8mKTyCaBVOqO+SpJKxhrU+Lg8ltvcNZporG3b1TidCgKH0yMChx62F2tFwk5Kpj7UOIdSpF0b6D/6H640d8ahM43+sKlDik/JGRvAO9ohaIl96G0Kx/ye5yBTly51A1zl49HiVwC2Jm1akg8EbJWKTJqWCxVyprTrFTsKpfM+txnt5mXUQ1f512jiDreJpkYK9B9dMFD1EkhRbNyn52OYSpEvv3A2QoIahYKHfoo3bo4cbxx0bLdG3Fd6746Y3oLYnrrgunveEsdLUkqhzQKRQ5WwUOeoeZwQDAK+AIH+UkeIWzc5zm0/FJ2PZOB0FD3VpJ8zHpVzWl0pazbXPUaOxUu2yfNgAPjocgVJxwuI5Y5+NV+5h9QVaHivAQiiuVSB8wPBUI63EC505EgKvAKxCyWOejtmt1kvs6/JoLZOS7ZtzDjZ3Yc3zH6R1Q95IFhm2NcPre1DAh2VUcp7xp73jOOz53n9xlNT/VAkzaIZ4bjIgeL85HdkV/zAZPn4hVuJZCzNckw0YDeNjmCwrqEwHZH2nh7NVDYeFQ+sqavP5WTON7GIIJpcXtboB9NQhLBCBiV7NsStRBzd6e8YXzdYfE8QYerTsPNcaNKdyMZBzN/W7dsCVjGQ5EDzjaMpK17InsiYQgHXZBX5kaUQC5x7PcCUZovzyeq9JCMJebNyL1zT1x0eR8jxCD4hxzMHGFAR6KIL+iKXM7QCjIydiOeZREAw3YUMPatfMUlviWWdLbGP/C6TtJU0PsgwKxXHB5Y3jiF3MulDv7DOk2ViKXCJc8qOyZCG2KvySeMQPHSVzS6OmrjE4Y4LDAG3mK2U7cHZkFLoHUlQrgbWU3c3FW9gS0srtjLvFKghyHce8/C6GD6gBBeY1A3ogTy8HserYTjuHHJ8sbJ1fda6CD94CAfSVbfJSuadaMgK1oFHeqoYOxb+b0Sr9M3X1xiIIZH28fhD8N3h/60dSCjQSxziaf9o8YF+lw9oKK3wYTIWgjCuYXB3IDxj8od5LcmeosI/ooaGkfrtYBNlGUjtMvsdum2xdkdvjs4Xdo64szy3x0i0LK0EU8uI/goFg0od5G/x+7KlgI/DOFOjdPHLS6YM+xd8lTClBFh3FthAS4MAyRbaigH6yYprxJibCGYZYBiB206qikFKkilTr2mQYtH9wVCsm0ql7pFSPYV5ThboNhFLvvH6UTB5Apc9Fdb6eRplaIPKLuvHkQNBkH4tnCBCpxL86iC8WiFGRgMjaSJIQinmL8HvD3NErL/k5TiqMgFpzrBhFjmFPeCqSQnaVe7OYaS4mAUUMtFjZ6Xp6VEW/bJxZnYJyAV2UwocrLJqOM5+0EkllRuDlJF7EogFzdmvvr+tzphrhvRjzkqcj7wYPWPfiQ4TW5xKqFXQ/m1BA8ChnCYB5xpsMulDruB/Gcz9jYcVDp8AMqddkn47on5O87youvfMSJ7U2mnpAZ0OLBIVt9Z2mSazV+6NigWOu631Y/lSBtv+3d11G0yVwiZWrypN/C8wxCuLSSPphZECEe6oXVbFYtLE1DsJHV03Tmun47rqpJCJaRSZ8zfcPmQ4rLSoBdBHCjT6JoUEXa/IPsw2TMGQzgcFpDdEKuKV+INqlGJi/9RQuWM43O6BtAMX529oSf0hBbygzYFDmvQFq1wkgK9G5f4YeyP467xBjis00L6tdge2BnbE+EcOKx1L55mFSpKiNEgAKdVjyqeERYEsY8W0GkHbNmeDtBpJ3aNUSOHgmYUipzWc1QOj9Cf5JtbnLH9LTAOI7DTWIhObN7dOtpuUyzuFUAAe/zfEL5/EB92aL85UAfY57nv7L7htYik/eRAL2A8753H068RevWiAfNoS6dYlEBLRitGDaOKdJNul/KaaMaKf6DiiU6i5D8TyuyxdnbklwxLJ9limYvlX6J2OQMcFXKpB+aPKCnrAtG+KbCPIDxaONrIYUZSoQde+kOLmwdwwF9n6xSxw0vZ2ycVuuK9QAW9Rg87iRQgnm1QGuib6CG8R5E3riaif7Uj4cF2JoynX7iD4ng6LB6BPOqq00Ty0H8DY/jbHoNnD0KZk4edcMqKkEgK0hCK3HuqQgyVu2OH1I9Y9nzCHtsTyPIkr7f06oB4Dwoqdwp7B7H6YetmgR39M+Nfxa9jCMeIg+aMjVgLUMQBX7gzWi/Ss2IBb4rt34NiNF5OxjJ8dTE+416COs4rsllXYgWKXdycOJFcm+yTi7uumWtVdB9UfgiT0+NqAmNVp2JyqQNmzIKQJJTDZ5de3qMr/E/Rs9p/9QwLLK/zK7IvXVUwfhOse/8/Q8chUqGju/kyyaQrhqOCxUNcJ3Z87U8A4C66WeI7k/bDcXOX1Gn1FaOfHyUrUSrcGYOr1Z6Bt0UaJD63RVQ1pzG22uaOcZaI7+kIRS4nJhskP4kyfHVQOyinb8plki03KnCJcPmEhDP/9pNT1E2RZCWWgtZGBXMI3rLXqeLIyQiX/+WquaUmYubimEbc3l2v7myS1hXR3MKN4d7jEAZ4+rRYpmGtrmmV+8MPGzeQyzpFqkyHFuBOYQCwx21U4LB3bnIiKdItyKUuNnkbggmjBopdnHBlRXYKzJod5wsRnXFqOJ/WLpRXIiAIrxaSJ8ogXZIdVSsClE9L3QSAezsNmN8sgS+NgwAOjt3HqkioghL8usPn3VHJ3JlqoC7mMPkoYW31XfU9krp58R49Aq+wCkXOy3rz+C24rndFz9hUaNVVc1JJjymjWJ4DOn/jU4dj9+3P9ugXxigVukdYwLEVPkYMbKvsziljuPKYML8ATitU9EF2whoqlzq4HvEaXT/Um9tMytUrFNnjbBM9jLH1HxdyBj5rJ96uKPWeBxuWjKzjBewxVL4R5TQrwNABWGWr3/5QHtt0A5HeDOALPPAxc0eJtC918cBdFKg5KZLvAo6Kdo3PIx3mj7H5nOkbMey+3aI6UPU7Sj0vz4jKrYf7+iJfScXP39Tm81xEYVgeoT/T6rHsPuPy/QdusY7KDgzsxcDxODeQZZfg1fBtSOyoJ54KqsgiFcwhukiP6xADtH8xQIfYl8O2eVzuZ6CxC7R3eMUClznvTQRTDUbtb7TZ4tt98w0eSaNVpJNDA6yjQm1cDNIdIbHMNRixi6FQxCOOip+x0aibrMBnDTSYfV45cEI3jUTBfbDj/wVuQT0HTpyCB6Nx3x5tbvaNG1r9y5LyR1jZCR7/ZRysvPRMLzsvX5MCJxmYvyLKfGnw+8+jE9KJVpDATL3hOXTnyfUbI1/Rc8sYsq+5Bo7KEt9naN17V8T7dkC505Z7bzJdnJfQm+DD12X2ZoOt9J8bIRZBKHLQUxNkhWQ7ts919fmOoWAWB5SVTgbZmdWPZ52wdY7H5LGi6asvbZvM5WLfrRBBtdZo732JaqP/vDP8rTCUJjHp4tlx++hQayspnQhQuT12mvaHfJJyJPPfXRi4S+svcvDw3WO5os96FnkKpWlRwTz/DW9cmYsgbLNLWYyMDsstVbuluFmc2STauwnnj6bCFegm1eNiHxQqGALbN25mlzCu/qiTApWX97fADQ2+0HGbf7T+R11W8nsWUqHDpp1trlWI5dJ5wijnMxmZOEAL46jA5ew9+658w1AqnPK6w1L3u/pHc0J0Yf+ajLveU1edRsd17f1eY9GpMipxiBco+1dvvhSCn0Esc++nhDIQH5TbQSxz0Z9ZhZqsgaLu5AqcLqYA+R18kjucPpL2S8lK4T676MZDogi53FHTcLnDQ1RNj8ZD12jq7v7GGLyI7HH5WHft3P+qebz7fS4nJAuxMhH9MvzOaYfFg5HVVafh4w9Jdl/TB5VEC4n77uCEkYNdnQNd2e1H4EKk02JTp8La2nxZJsRy1/PHNoG5zQvR4MO+fZEDzvYl2rbuiXxEB0M4bBGH527VjSiBlg/ijR/e+rwD4xdaEahZWWRJelRXD6TRNh7qEq0YLUNWCR1mj5XDDd00q0lnZaisD8dkKhvppnn7yW2zQKlyvqY0ucOiuwIqd8feOj1MjQBg9m19phN7nX9Hghjy3x2xHa2IzV6qcI5KnSzjH3iNClXuKah8Z6T9LC/qzUVeBh5192g85FhTdxqhvc63eCWi6D8uJfzyGx/Oj/heHK3XZFEW1kLu8+EuiqtoMLaIIBsMj69wKCpPIx2sRRFF/3Ex6aAkgLzlowKHLUqT3VnYnXQfHYzyTnuOrfD+q8N5ACY7YeEkoPnksrktK9qwvLkdvrtjU80kVO6OnWVBA/E2JQed5aazknBl5aunZlVRvxd5vQX1VF+yD8EEGsXSrj2iZuk+L6GkqGiC5tOo4KCwLHnmcIV4IrONqYAIZhvD46sOFZWn0Ym7p8GeN3dP5T1dSGraMPwQgWmm011WFPWmERPWGHQfalTgiE8OwOA+L3csGmfn1eYfb30IUlI+qdTl4KtJa61ADRQ7zstVlVS1hFcocu8vjFYudXAhNrnEYcRSoTPeJke50j+pAnLnuJO6KFC2egIeTwQhXFpo6l0SpSxi5kvc+3ydPLbrEeReUEO5RPqBNzC5z658Xd9WeZWk59kqJR2D2FuE8Gzh9NHUQg/h3sI1rd8//6AbCwwZ2KJ2bDCka4utStCMTYTwbEEzFhHCswVSV5Y9GMJTPxE9j6mNmaRnCIEkswCP0TZITAvwGG2DZLYAd/CkNlUEy3T46sgfMNf5cBqcDV0ocu0dFWOof813e2yX6K7O1mgN3b0Wy1yw/kyK9UWOs6r8hgpE5lYMwFGAOKxzD2j1Pa+Hyw3KfZ4eMqBF+dKyAsR99VaFc0HlDsE8d3c4xUAyx1GBh0W+VVjkW+fwJbovIEqXbHyJOjghLAKZGHpIh/6TWkBgdP/VDZNsgg5fHTEBY/Yb4cek/I7WemqqYNz6fPLjx1u5x81XN0ynj1tcsKCXj3kmJsAAAXzx/xdKACqL5X4c/A4XaFW9Q7dYjJdTAbm4oPpqRyu2grzPU8AdpYIKaQniIDWUV0vHSfZd3mqBAN74ZWEFAfzwn5+oUdMyL6zta2ZKzH25F/bz20R0j4qF7usCs0naeE14hRhDOEhaTWzGAv+TiSm7nZGsoISOOrjw1mQm1UOGt3iJSikbgwnWRTtuaRocDT1hiJAWoBGpoZwiIXorTzMgDZhLkHmxekhKpPTCggAu+yoMx4mPCtw9fs3OC/b3dWXuWOmGi4j0tq6aNVrrpbOu5HLgEycT8j4cy/DbqEu0SXAmZZtVgNi38T6hN+nazfqnvOqTSY7b0YDtzHFQt+H+UpK903tcVuFvuwAofR54sUMzzeFR3CdsmY4DDycclwpv8VuIuX7Hd2z/FJG5AJQ+zGWHZhrm6toWsfDfHTRoidbfcPUAMplU6IYXeAGU+/wvwLhxeDWAP2e79NuaYxy3AA+zqaHcuR86gxPLHHYHgLfW3Ut7XnY9YEkgEyCrCADgPnayMd1CeyWo3MXyWeEtvZIv25RCkQdO4MaSWOZgF6OMWv2y6ct9d8UGdHBU4OAhRGUpPSTQf3ThpoHszPiDUq5JAM9Yq/YaI0L4kecldE3dCcOQaIOK0KGhaH578iJipod4uRRoFlSWPAE8GZZLPTCD575yqQslVf317au6n759BA+cPY6Vuz1Qs7yCg1aA+LYBkkEB4mAyGI9JQ49Hp8jl3KU9AjICCEUuC1VXVWn2AAAukbIrlA1JpeSUZVKxQ9+J/vwGZHTnvzuEQLKXh+kqIwQ/ct+d9esJvfEKadimYLeWZ2KIBO7QezS+yzNcd8rlOXwpZX0eB+XnzmqVBR3m3UEQGEnPFz9jS5H30MbxG8kYfdxGVlim4dS4sd7NEASt1H5zxXJMSjIp/dW4yM3vBO3m+e/z7+X2ToLoKVKT2ihEajosPo9sKavutia/LpLVdzIw6NBSLHPASsMQITtlVOB4sojgI1CxzOmckD3RAqKVCv8FpCfcWcFjCpCiOV0WfZvAOXj33cMBAsums/94b7IUX6KqLjL6qAIKTbozQuVltmjrT8RGkS65xWaguIZUPL/XJUrKPDvLi2a2RF+7UOiCl007Ynt30cUhFXrjVUdyaAHd5w1O5yiXunBqcnfXbHwFZh2+O3h91hucgSF+4xIXSnPiC18dVIDsZ2jPYot+zv48Ico3xsI/xua1+JtQ7LbmvkiK1nSRb7ryJa7+OQjjuMTF2BloDB1JQ+WLbdp3/J3BblSEhVFBH6GQkmLDEEustgd9B1zNClJ4EjoPjWeBYxqVR/8V/LeJWxTCLr/SxjwCyW2KWlUB41ZDufT7Onk8fUQSGUYFToeWJ0R27vPiScoHOC7yUH07+madmrauD9buimKJ8HIZjDOGipn1tTKpdckygADm1GOHV8DCpHcxb9OqLugFyva6QaxTMwir38mZJaZp5E5sXt4/y+WHAH9nrovLbRHYbDa9/rRK0QeU3Uu3s/kCR3wXqMC5FC4jFDmeQbHaYkoQvsDJbRb58Rd3C0dzkhXlxtt5hiucpKCAi2XPWM7ZBJCCD/l9mIhziDykW1t7GsHmmgT3CXLxUt4h+Fa5u7d5n5mTYo7GoCzjSRCTwhgmctqym8HtU0iCOh4XzbmbOM2oQ0HoT/9xZ1goWK/56bMZ9Rhp6gP6gVIp+Jb77uQ1LyowtmpcYo+RvmIHIhwVOCzcW/iJlK3PEylxvfhkJNIDuf1Hly3NHSoKVEi4RgX76RFXcklBTzizlXAAwH12WS/Za7/vk1I02fmCnVFRLNE4n5sjQtJzHp1v7nM9jonWOq5N+V6VXLqUqMd6YSfaO1b7ZtpdFOy2Sav/wzh+jMuD3U0Idvsw/qzINyruFstcOFOFc1ziJNtR3muK8MpaeYkS4PwtcTwRa90Ox09NWirpapVY7IW7v0msRM9BPGON0eftDNwGdmh8NoDquhP5KOC0+l7p9GM5ziBH3s4dVF6tHtC6TtF1Un4PjPbiMPlEemmrT8M14Zv7I8Ll0qva7JOLgsmz08ctZVQ5TbFQ5qD8pRS2rulrXR0XmuV7+zk7LQpR8Y8KHGaNLGKXtayO+e8OXoGERYsWlYRvXOKG8TRbg/i67479q9lrvXAPuTLHPsozwn12WYLf4/VafGV4+OoU3XdPWf0CFSvg4F0odMcLelekQgf/Q/7zKypkqeW/74ymP1qlMR4279F4+YWVdadR8E3bIo7hqysmecHgv7vvsS/zVJnNvStzEcTzdSqdHDbfdoYNvxRR2LBH48GGmrr/Wmx4ldZCXtDmyxLH1arHDfSPGiyVHA9lqMCrSGHDIjafZHlGFLvO2f+Bnpo3JEeYhq9OmCQkLvWBlI/O6R5dXVYL8fH1A9qgr0mBqVEfxsQjVB4cbKg/DfuyRoVdUvNpzk3kvxDDtW+BBvkZ6E8fBwNcb1c9C9KZpuNZ5lWZght4/rsDNhpUKJ/Zcp/tcb2Xn+d+7/w2d36fX+AVfRAAOM7ni5a8rvC+2qTH+VpaH/nvzqe1XRKKT6j6mRffwYNbCcYp+J0I3BOTlu65Sfm6HQzj3Mrp4+qB2HeIJfrXN6YC3RnV1nYqNN68G5vPvQ5l1V1Vcro3VP3eTpXzO3okd6TC/iEnhdJrQKMi1/3+WV5skqrC4mMMcqmDIgx8glspoPVtissHcfXgPi+pWPchUOddTp/VOM3WZEaFeRGKdkaFMTZpP0Uw0lowX1tNWX3Xt7KRnTT7YdkfJ6vv5xnp1+p7vBAtBVIPjrLGNA1zRcsnETH/QeTY+30Lk/iYZPVdwvYJxTXakHUo1I8HYfRgVTs0u2rSfcX0vSfxDtjwdQ6zYakdQHsXPw43idh8NgRGFAcu2jkuanV5HCYSkHmFfBowHFho51joEtGpWrdTF3ojmcfldR9Zj2Aa/uktojcKS+mNF7a3CmxO71nvHT+dFHlZXqE0jcJRIjafhc2I4vly1VLLUpNNoHmDNXBR4lH5LEn6+hNNPd+o6uFaJZCD8+BxldZrtB6HAQkOJCWQRzvNC+HqJrhyd+zjh4OVjQBgLucirIcMV8me1c4q9CjtkTVg9m19xBne1BumoKEU/VC5A/bkUY8dKHeIVWY1LlFZFbh9fxwklA7O7Tar8lkhqXBn1NzfalSjNXsw76iqktVDeE4XEKWH2rPEM4364xoXEQlFbk6x5B7R4xSZRaRCF1UtWv2uFv8ZluMyu28Odp70iJLr80nhu6CPeIPkqwDD110UvGjiFiZk8xmVOC+wmNF1+Op2wVG+1uiKQWa64avLtUjxMqRbbbkX3TeHw2C0TZ/EjvQfnfHIXRoVOFgsJ4JpcuJS+3glONjZh/mvl9IIFqEj7MuSx7hXRKavWc4/IU6p/+yGC+ga99nBHmar9oo91ygdMQplTj1c09OCJE2fpE5yJTuj5Pmhhml5HpOHmtdXn+iAWX6e2flh5tZBJGuiUYFbLJ4ciue0euWFeFGLfXEIvShRkUkDGr66GH5lKed+Gr663n28KsX5Gj47je8dukvqtCJabU24ESdpKQ0WAtkZuT1JNtsE3wee5ndYvE6yVFV39fjhOa+yoUFnS534Nzc8Ix32j5H5nPObMOwqaxN7s1k/m+wKIgMAxfsoNvGutk1j/E0V+elvVCodj7236g3kWAGKfXC/1eN+G4L7T3rcf1LjXkjVfUI/yw+oqlARLxgPxumh+GwRTaT/wNblHGo6uHmNfrdEMjNutvcjMvUjSsq6QM37CqGLPofKa8nX1t/VBX+KxLqX1K0v3dTATn65vQukaOf/HSZNlsEKWcTmz5AaFAeefOY8efWUreLc+RgQ+Vz50NWeyCEX7cbHVV4XKyQlHuA+L3V7pD27l9GNClxHKmcM5r+7XkE4l1L2D59dcV1VheJ+XVfiivE4z1MIX/PdRWFlK9D8GhXsjFo4fdzmRfUObdM8wrs2IjYfl70RxVTHs/kdsPvjPs+57MVKyndesqucAisOX133FX/HW2hTwT47LJ797EIvjcil+33IEJri4X1Vba+LJCs3mKWHhGimgglrxdyGa7RNY7PJkTtimavXDXZR+vsnWU3QSTkuWdo7SPkR/0AfpYv1owIn+ZFOw7pvO7ZmtZo5xoLVovJerZT1d3VXdZanZHci4+G/uwk2/UVEtyKKVpbsUaHr3k/upV/ENfnzByajUwZ1A+UuC22rvlpWENZboXB+Q6DNu0x4mWZelrx5UqnT7JOJlaxx7rPTHFG9KkWJ8t/dZ5zsge7wvRymCZW7RIuVdVqdZ3dS4Nnw3R7b57u7EglLTffN8bAIOCJyOlJLqtXDFf6nwMPcZ4cZIOLE8nGN6d5/XXr5PMk3W5bwXWdFKIE8jOejYvUgOfnlUgfMKUoyMZ1i/3H+JfuoLPMVZrHc8t0iVLSmY5OX9Ya/10LoqblKZKgpXR0S4HlwwF5Yy2yrae6mcYIA1oLVWi8hh1iWkrnvVXCHr5PiHkGbcasO87gcO/vra5Af7FlmfGPoprut0t1Xwnq2saktOShHddrbRBY8Y24rkG+EBiJwjUWXAzmnod7cTNMx7A2nq3WZ2GRoOeVaA9M9qWemrYw1cPp7hBEmHuhc2ES3eBab6GOcUuO1f/HQYrbFKqopd5nrMc5AmgrIYk67gDqMMZ07RsztNabz+eK8/FSn6W8v75K0FM1N0+iDmecdLpnFeHO03aaYBlG1eVQMi4q+nshGHXSXo8WCnXQNBM5VjzoCN2m7GbhutMSaW5/IQ7roMiE4McRQS8UOF3BCAysyd9h3mhf6ToZxQotmbkbomm3/v07u9dsTCFyRYWeAsdmIyIhDdx9eBLXuXJTZJpgWsySst6SqGio7wnELCqPfRRvyuWw1L9HPpFhf5DiryveY9IOsP19KtP6Gq4c2mEeXPslYWU6YJFWx4AtjQ4EzMMYVgU/MHd5F89NEhngKp9uEu+xdpDoxNi8C0kA+ErHF1Dgi7l1kIPP4zSzU+VppDFeCM1SIIL0zt/3S/112H9r0GvQYOC2HevS8Z5MwgpTbZMVcNWt0houyopx2m5SoAXn5ojsk6Y7dWs/aH+lJ2jyx2wF8TDJ8h8rqOv+Ost9evv3lzduXL9grmfRKSXr38sXjJs3Kv67YNCZZllds6L+9fKiq7V9fvy5Zi+WrDV4VeZnfVa9W+eZ1ss5fE1x/ev3mzWu03rwWq7dorbD88j87LGW5HiV+4A6NWzZhj46PeerX/0ASM3RMconuXqj46dfXYsVfAZ6kbf/2ElOSMnFmbz2xlHrNySmFQqyXL19QtqNHgD3rvdai5480m2ayH0mxekiK/7ZJHv87j68q5AdppN5mLJ3TeXaFCdpk2yG9pQdNjl07L7uQGlJQoRV7fNcf3RCfE2Gc17hK41CsuZQWAdFHVCVtxHMZDeEoRVMknPFoJ91x8+eOS5TRlFrlN7ymS1sApgbD3/MszhgbdN+KZNu+7KLomz2uq4f852gO/Ed5nFMjKFAu+zQ3nJpzxMGGQ/fN7hTnTzX1Wj95hD11e6v7Ia3/8sXH5PEDyu6rh99e/vsvvzgjHd9xsJ1S61kg1lAlXRB4vjPw1mMG2pfjDJJgZx10V/mizyN7QQf/E/Wm9DOZ0eHuCNeIaJj+9ZwsNo+/vfy/WKW/vjj/zxuJHjc0tiSvs/W/vWCy9NcXb178387d4d9Ojd6hf/fpEHs5aXjUU+T9OD37E+1ZoCLrezpVJ99G66S3xNuLa8tHz0RKjXr3jc9ctDQ6qVP6codBrzuj/5LhP2p0hfLmQTQdcle78CxN7s83pOtdRK4W/Z9/ccV/WaUhFmJEM597vcwfycQmTiPTzWW1S1QCHqy9lbuA5aiv2evPXzyWn46ekxheHfKIBth5STNvXqT1Pc4CNn7n5XVer9RsH7AtEuMLnwmnWnnEAj1sY4b785+dUff70TDE1nOtPqh8VvMcPC9nBUKdlz9koblOHk8f0WYb5I8iSNoFq7mVB65XNkqkSzgT4tFthIHxTwAeP5EKUXJ5mj4Thl98CY6tWfssHxGcrVHMQ+pf/Zy9z+nFtvsgPj9K0/zn7zUqK7J+f82rIGR+VivkE0oKehSIWLaCBg99H7fCdF7d6H2arSNh8t4jOOmAo6z8KR6d760moANy1wJNrR3RAJ/qzS0qPt9R2ShDmHriLV0f6dUe4TwPBuKfiXVjoqFmECOdb0ehMXZbObv90tF2W+Q/whaCcVY9tX6zc/+MHiF3QubMps+MP5uMjU1DNfOiYYbyDlNhd52HPmGI1kXmzHJtTsi4SFUxD7HwnuXFJqlcDojUuK6StIrdz6P1Bmcn+WbDnZuHxbeUUfZkR3d3OMWEy8NIF74je4fY9eURCrX+9tDQ7Y6ve2hhok2ftsthLPQhKSu7deaNO/KoKwTt6Yf8HmexTGqCj3EY0b0WKK36CNzdcrMcJAQqA8JmhFAkplt3ZAz2/bFelsGrpXu7KrNQUqstgC2TRkMGrsFemOhhb5LFCezr3990trK7ikEyQtAjFsibrTycRkL1kJ6ccEFiQQRt5elNTGRvoyD7O95e5GWVpNAZr59H6yHPULNNjoLvLHmMiC1g/bPf1EB5GvZWe04SucD8jGW7tAY7LMsoRx8/8yb1/nk5QcDD9UOBkD3+P7niJyKCCrwScHt5W5t0gtf51yTIel4w8GEJZ+04f8m/iux7xetGjvCPxSPn9xmh3skDvWoWFPKjfCk9rqtB87h4dHfyyKh6Jsw9nZW74Lp6e1ugHziBhCvUdbAPkWzHaX5P7dlnwqKLH53b3Zyyc8TYXDuzt/laH2uQou6OXFpcJ7zD0GcF+ZRXsVEO2T4CV8ndPETXXSHUGRo7d5swtLMRI47n2OJeQCmD9lbNtqOhLYSeLBWkC+yiCbuHQbGGYfyKS0ygiYLHP/C6fQbRnzcmMeivHtgjGXEl7YxAx8YZ/XCuY5z2OdWwqY4XxdXhiLUzOmjpkeXRbeuIoY1+RjFA6Itf2f1VvYlkfUTB1yG7JlZmKgw2sH+xUPa+qqPVrtz3ufpeRxeRJKvvkhW94lWQFaaC3eGhrfxeYf2W0QPnefk7vqtOkiJoz9jhCF+fL9EfNS7Q5+oBFRejbIq+ORwYvmGpN57Xu6ucmm71KryiS//Rei00GdT98/Jd/jNL8yRsS9/iCJuaL1naCGGHLmhkH7tDo893Ej6vaM0WyenjFhdsz/0ueVJhtHJatghZuANDGM7d75PyKqHPA8SY1TEmj2NYoX7IOSwZGA2CO7ovEOJtN59xjRBdo8dYUVqXaFUXReAZVI/k5GmVokZthOk7Ht8FKnAeKKY9RraEM7RBgnXOzu76N2RCdFmsO1BEybKMaknaYWvOBPq9NFrhTZLSZGPkV8myhr35C9mV0ku0ZJn06HqUEL/z8rQMIiGXbyaMSYjBQl2P2Q8iYgRZcyQWuDmq8tX3v9VJu8EP0OTNnojhO/qRYFIXpxzOAKc22Eev1Qtn0cb7If/ZjLWNqwubBmLD47snto8+y4uuf8eIbItC0B4nq+8ssRPNDRgYoUq3aBTfeUNDskXoN5dBJgVbvsjM4E29iTExDb7kMRY+NnC0blFhFN8UJWxOgY/rp+O6qnLV9XBbkaHQ33D5kOKyCkfYynKKCF8SxTzyeXh5UYm9zVDhVZAvZoQg+tLxOV1P20C7zTihZ2pTtXG1JXiS1HsgVscfXBv9Ucg13sQ4xOBxt0cjkTB3rqbTrEJFGcyLrfYaYUUTM1Cr4WZtk+wXrjFqZDdI55PFE5XVUVUV+Lau0Em+ucUZ2/FMyqyk/90jBmX7iEHIKL4hfP8wnfiOtynR0X/D6wmxv5+WNv2qFFvn9IjjKpxYJwZxYkUmviq0K7clwZHjH6h4orPq7noZ1w5xvHR26ZcMi+eYFv0Y1w7px3FSonZ5DXYU9Lg+oqSsC0R7ZwjN8+7u0YaP/4i9TPTN0B/jpjw8ksd1tk5Rk9cWcIuFHgU06C9QcV6hTQw3zQghJUNMfFcPeeP5IVp7gmDhbnV/JvETxqgCn9xmHY3C/eJfSjqtKzK4wHP1/sEsCVt0w6Fryl+DWLrVdjEqrD3jKk/yekvjmtdBsyZjiRUR200SOz5mq2AYow546NLt4Fe3v5p0yCduREojj1wRW3FL5CiX47wiFlj0GCn6SEpsQenYLk6g1CEcaRRuGOfM5gKzABOP9ExdxRADn0arXeF/opARjIKzyuv8imwfV5WI2e8hlwbH5/EpQqzcP8yGv0yye4PP3oNDIsYUxvVJ7WDk1l74LGI5aHbZ93GX1Gn1FaOfH/3upNtHsTeq65kYQ+1ojnGWDAl6CdVu2Qe/lYtMIMVjUEvu0o9ymvg9M1lZf/IJOfyEfoZI8Hl5XSRZicU4I4ulsJeDGw5J0GsaeskK7pL5SpjrjZBnJlLgFQ4bo6irGHT7bjqbbBfuanbmRLvb3zyfNJXdyDxc933N0JkTSXtZpyjy04tXWzS5g6pNEQLf1XZddbpwvhjILlFZEbud2Z7ji/4BPvgB6YUyotxrqjjEyRNlhyYmLzbyjsKKjVIg9tbID6Lw6WNVJPTV2CntSv4A7pmoNKND7M/uVuBJnubFe/QIvl4SirxdvJqnbSKf7028MLay3xxI0dOoZ8JDC1tT7QGfbyeE6kFdiRTIvJrydFl92DXHxWNL59YE945xSti8D/kK8ii9x2t0/VBvbjMuy6MPovYW+PLerWfmffJXyT2DNPzyTDT0MD5f/djUDVKOPWkDLwJ0aJh5EYbrvCRKtTlPcBEgV/Z6ZqezC6/3inMxmwRffc2wxyLKM6Lm6uGy7vyq15UD+avmBy6MwYU8Rd37MK79L8ONz5ANJwlViR6pwNH9EKsRN1bjEF9h7N3+xVccwg/+tTaA7KrfHV4xQvY7jGeyRC1sKcG0bXSCR/4RLbawDSpVhM2+P/DOQROe1R2dh0enT2mT6Qj6TAQAHmIs3tuZNywXYZRnwiJ7MwutUrlO7p8x5R03UK7E+5oUOMnA+9/PhKgTZCucIo/gPBkQJ73T739L3ga79yV5q3QE3nfkn+1l2aOyxPcZkcBucz5Btp3DHXDBfxn++OjCO5zhVPk/N3Fe/YuWJoztaT7X1ec7hpINcopnknh+eCZr6BShURq3ZpzYpR2Ki4JEvb0EMt1Rq2jaPRNeDFVw3f89XW4+Jiyic9CAN/wsB11uENvy7nWsLTbNe0E+cXk4g5PCNsmaQ9YsbveRVUWeUkxhZzJKMZ2BT95Gv3Hy3ER4kgvYEd0WQQduoWq6CRV+HhOtGKK3FgRppSqMqceX41i7ZxLTvLj6o04KVF7e38buZ7N1Xf+DTBOfmjx+Gjy2CZ+hIc9Yt0mWmSgsbV5y5L6zhsJX2g84+x7pISj3TZ79dZXuuYDnoVe74fxe476hOsN/1AgzlHeY8mPMU/EvZf/QwZciKNAbQBP2Nm6LL+alcPbObJOXK1bEPXgT3AvZ6SPpWxkrGuhwmXyZy+R99t5nopKWjqzQXpu0C9hRX3G1Cage1Y7hjQ4ZS8zk09YsTRRJ+Yzu8j2nl4nZPbI4qGjuuDiYooQD7mZ6x3+l95t3411f4ET9XzMu21pdUzuyyJL0qK4eqIpsopku0YoQ7Jmo8G5R9l/OA1X46Ya7Fhts9dPZOuc2eBHRtm6FyNg/U966zr+jOBLC0B2tVqgs4yElf/4gu+YiKHOMtdCd5UW9uaBPzTwPCbvOt3jlLl5ttbDLj0sLt9XTinZ76Iuj9ZqsnfGz0u7bJQQmH4w7nomAsAG5s2hbbc8FhM5juIu5TaSqN4s8IlA+NZo46HHPr6Hve39Iyor2ItAL32JRTLknNpbcKXAntJ8a6Dkpn9+LvN56aqC2bvRbHeFPZ0TefH1qV50gWY6hUKjsQUbVQa1MEoe3n+qJieUz0VF7oR6eG7tF9OJZc25DvOfBtGws8BUdP88Gxac/zV/uQGyx/UObGbfdG3vk6xLqh50wtolfvXsjIQg5JGQ1r0hbtdgTz2HFwNUm3o2IqsnhGzmpc8edJ3VRoGz1BL2b5Ym4QXhJlIzl4etfvKXyOnlsV6XwHfbXRHHf0F+ZkU17RWQhPc9WKenqZLF8o8ZOH+dp7Jo21icyn2mEo0bnGWmrG+YZYdvYrCMjDTkIq3tjIzVGFD+ma0OSniE0NU3VLU9NYHXLU1O7xR8nKTvjk8kZMSTFsX0rTHVM1gSx1Gti7awnfifhEv1MivVFTha48hsqEGGtsNCRkwe0+p7XQ3h17F2j1EC0S7GdEaCIUXINMrm7wykOToTXW/3bKGNkMTR0G0PfsygQ0SMnZP7HdorXtBMstHaciaBdimaYSuMLTHVYfkdrFemCe3ry48fbaMhOH7e4YDunj3k25HmIiPe/UBJn7DxfvsNEuVXv0O3wwLrfPZIezdGKrQfv83Qdaa5k5BEZgUN+nGTfo22lBLzRRIzHe34SG2X77E5stOe3SaQFqdXQzCho4//iyERNjL0C/7N52ZnGzSercVK7SdBHYzdVA5eo5O7oB2qjLc2jEp84MuKIvSb7zt4mit/1i5rWLVFsd+pFgmNF8nY7xWY7E4emLUq6VyFCuK0bl+Ekvq6ps6Xu7oEGv124RJsEZ1wmS498SO8TelOq3dx+yqs+/98ERyXdrvBLSWz795hM17N5MWHxhwNZVffW22phAVn+wmLNOr/jO2bsP0PW6YbmPnlDzaD5+1Ki9TdcPXiykFA9uCujR+Wiu1vmZNZnwqCdOcPNstc7Qyo8IQeGHanDD4smdvOdl11XWTK8JDBFQ4eMbM22ETcMl2SwW3o3OJql1mOMd2fkCmXUro7VwwZdvO59RGXJJQQPTkTVzQizzQJdwzMov16wn4n268cTNY5lQXPtYtkr7THugdMclOwu+NTHjX1Dk58uzjGaWUYy9Wlot19q1uupSTZubWraTXZkF/+orstPE3zP+rzsUEUxjD4QFs+GrD5BT45Trf9t2hTa7OlNz1fNlar9hF7EjMOjbss+sVGeS+KXBRdoRkUTO7gkcrvOuxOSXTceee/m8/LyLB513BA1KHiVYThO0iQbsvB4mW/l5FE1MTdhcQ8DeP/HTaC37cbFeWL/DNAD3rIj1OcheQtq8usiWX3H2X3EM0AW8zatUcIO8lCsk8buPYtI6OZYhToBeEZejH5IXvc7mpoRzqdkv/Hk3gMH26Oqi4wmfkfPJo9JhOtSkSZucfsnht/pEiVlnp3lRcMrcYz0luMQ2/9a7Pd9kNqGIDjdMxLywAU+h5Pc3dGNTxx0R+sNzhSRYGLeMefsMyNFEeNW2K5EmzgskDkL9TlJime1SIZry4ukaA2HIM9T4xfyO2bl64YcrfKTHH68urj+X/jYY7rLEkQ6UUFz1cfLjTvN6rl3ak666PJM9Bx0dO74ICuwMd3FJ5jYVpbCt0IcA+d5eZ08nj4ibqQ+aAiSEzKd93nxFBi4Mt8zU86Ucn340V80n9P7QtLgPBZNGcW02a58VMbhBZ64Eujg6ljVBb3o1cZ9P6+jFnF07tIjY4gem72Th2/jUT8Tdjh5WqVo/Ii31wxQNBeowLk6qMBu4acefIYt6DTO9oWAiVZq0NEv3clx7UaGK5yknic249o7f6WBUZx8+pDfPxNJ40YEGKEWMygh2O9EwbZXUXeRJf//9q6tN25cSf+VwTwuFiebWRxgschZwEmciYFk4rGdDHZfBKWbtoWopV5JncQH2P++EnXjpUgWKVKXTl5m4maxWFX8VLxXNb+eCSzbt4ddzgztJPa5wyTWODN2CfR4mTXEFr4U3cXn421qTd6RrySdGiguLyrsbREUxyaTkNc3AEdMFP3fHKLoB/SPuOfMD8RXusQaqaQoSOGL3yJ7laici0Vz7pTtpu3Etokg38alFJJEWNR7ulJEw/uyj+7PxAexKnl9H7L43AVK8GC90Jkc73uOSc91QW+d9y77TIA5/SjzTZEf3GHI156Yb8NdDLbutDi5gbJ1eMy2U96QeOLJRreGf/nURo7xxGx4w7j26+dDxLgz8QLOQZ39xJb2sCMU7ADpdvdI9qeU3MXllzPpbeNK9O8OK9GL+nvQrzz+7pK/K88uvx8biAE33sQluTV7unchMcWMFg0+/o9r/z88rd4Rrf9RDwU3pyzqqk/KGF5j6/ghuyyKaU68E8k17bhKI4fFYpNlhW55u8oyMKD/uswmS1Oz8C/Lc0fLnGj6SA/rSyoQCxtvX+VV+TbZ74lFYCnoxDh/aLzGNSl2zHzC4YZdz0mxUzNZ25v82ydSsO6tyL997X9RuP42bFrjOXzt56Znlfi2VXDasqblEeQ+Q7/UuMlTlysbXO1JE6+reuaWhpg1fSx+AmpGQN2mpwfvTH2c9boE6MaHz2r6N9md261LH1irVWiTpPkGRc3YO08ukNu0vQ3r7Sw02O4eyYF8ioukYXUmSKM62XQnKpWkpYPD8AQRIh5rWXMNBxbK+UxAEmTAAo7fpg+CZWp9HA5waS63QceNUzH8lk2N6m358C5/yK+TXRMBex13ud9Wh/RlvmfGr2nnq+0JZP84/A9SfcuLL7775rpIDnHxRD+dPouZywMgiMvEx0iU5eX3WsvsgdCQ2VPlg5lZiIm/fN9x/1E8ocOxji4JH8/b/npzyDR8nbt4lzcMLIyCv2/yJi8OdZcxUcs9sXdO9Qp8nqfPaVI+LnGH3+vBFzBA+4xF/zpv4s9fZvu6M6cOgXZzsPcxfZp6Jg5o7bsQwebTL+Pdl6us5r/7co6Xgzw8J1/20fLit5DmOPR/H2en+5hOtYs7cqh99tlsOQVxB5+SJl+InMxsMuPAj97617Y/+/h8+7hzez+7+Hy7+Iak1KptT59JDw/D9XMvs4XfpnAJ7YaLvCxvSZr+7EE/PYh3jmwG63MxO6uTOsmcNuYXbn31fZee9mTPn9J7vxk3NNOm3DS14HI80jfBpwAMoEunA22vpGkts4p8Fxczlv3wPsmSw+lAvZN7DGFcnuHvM7XUcr8hZVUkXQJQD5Zq3qFp8ghYhsFDe5k/T+RE9jSdzkVVxbvHMwolwOhmvyTmKk97i1CrFT+QZpOW7Vm3eHWGGaDL4dWbxOX+U1tryq1MNo9CrcvnJIuLJ6fjKeMY4vKo931yIA3LiYxdvsQz+f6uiyQvJkahax4meb8l3zBFHCPZ32TPvYt6l4cQ9IYc0yc7aS3YhpD4lZQmfSrHl7udb5aYV2cuHro5yfdzju/zcOq2dgt3RTIxLk7NxMtL63YKs3NM68vXnjbck2zfbFLHaTp0msc7vaykZzJOgMkc+WH27/YH793GiHluwPPGXiLyLu91XqheSODOisu6FyyVRU46y9IYi8WFcXtP7LZM8Z8JqPZrch+f0qp2bhSBda0A392r+HCMk4dzOe2EPgvHy77woOfGDDXSzX8Pw/myCv5Ys33Y9KMdhbgszOqJWzsItq+e5RwEUyPtrH4a5+NNiGa+NNfH4WvuBGzRDVtZz4HdDcvtvoHXbx55/bslL7Qj+YN8K9+R5sM9wzs6sHJeQ/koJqOo59PfylQSrl1aRN3tIv7l8MT503Qn4Hc9CFyg8m223/xdi35P4vJUkC4525l8H6bx1iVkYtiAjDcNLkJfkA51Ot5h6HX9uWTlGbnZnzCaFUa3T9nuB7gjjUlfOFoievnUchl34/71l6vyIx3g//OXu3qK5LI7l5+KHQHfz1qL1/LSi8fC2uFhiu6e+FRzPpflxaoKVJ38BXen6GF0/c1dV6DqdF9Im5gc0BMwIrXT1aRYKy2P26oAHv1MSH31Ms9VG32oRXndR4Gvll9+P+ZF9Zoc0/ycMirUf947HfEPFcPmonH5gDw8Mb4q6bux3fT10/8kxylMRsA1PKbtxaAOAVye5mLOABz4Il542z/xrqrjXRFn5SGhAeumWxXiOOlSQ/1VtdNh2yvd+JNQ1Oaoi3kpe1R8VVfRfWxKNoBNvpL3zINex9Mlq/Mpy7Gm87BnMtAE8fRv8rQeesKwrk3f/KvT3DeY8febrb1xkX9NaquEvEF9VXYeqoPolKMGD/FA2viv9Yy0iQDrsK4U6k8Ltp6kNV58zY7rPxtXisklboONejl1nzywdywnZmwoT2l1ld1Ll8ac2H24vy/JpOsO9NRpCoOXcbV7vE3+OWkkv64/QhplaBUncU1AShrD2W4K4HaLrJ71XtTspp5ApCTOThbzZ/z7GVJ007U2BiL7FMRlwBX5RfIIDNZj2wXqmEdtsGHLlZzMwstL80EnO3GE6iGC3vDPZbq3LS7dzjNCd3rboFN/Ay1aDnYig4lp0UZVLLt5qBkmrFELoj7dkkPf9iywvdo15dSrXFtun8vUnmTFt5NgrBmiJ18nJR38+if/Ll3Z80B/oF1bTn3JNWZnSabqtK+Sld8STmPVkL157f5g2LYzu6a22pes+NY7tF3NED3Zce/+dxc7HU6q+0ZHXjfmNlea15RKAXr5nWRoK4ccM6fOhW0Hzslz4BWMnuuf817nafopb2L1LRr1HZP7eeJHVit6kZXfXEJdsXVDdEJzna3PJHyu9u/0u0sqOeqkY3zZliEmuxrqQXDdCVcVETMiIm8jtjVDgONlmj/8KODw1ZeNza7z0uFR31gz4OzohnxNyLe3JD3en9LMcSG6iY7lFHae3PTVJ4nyV1x2Fg+xccgKeu69uVSsSn9jR9tP3txNc5F0PNlxui3QIvO/SXlXWzH1wOqP3JKTCusXZZnvEtqz/TEKDR3dXva6ISW9lxb1+WIE8F9m+1+a6euYUKaX6Jak938bf3x/SqvkmCa7WoR//Pr8V/GT+ZC1uSZ/uaDnXs1uRrmL97I5ajX2ShkAyXl5QAJetn+RmqSpydtnpK/yrKyKuDa3/M0n2S45xqloD4EQ6R4aTQeWYslrciRZcySv0xvTLpsFSG5/aEboAZM9XjxjQIXAWvJPGg2QyrYhoLFiyyjjS88DYpxOm8CXdMrFrrnL6Bb4VJhuFmtzvSwXzgI97RmmTj6eMAggJZPMAEz8ma6ifdwh7grAehcXD0RcJI7AUAJB1/E/IEitAbI0QA07pXOBM0/TbQzOjaQ8yOgPmx+CqRrbGHWHDdVIFtq2pwKipJVRkqD/OcwYie1FD2jpFEGNgjX5YngRbqf0cVT7ILqJeabGc+B6VCyaBV3wXSO1WANFEMwJJpgBfYi7VoqGMZerVoJJ04QM6HVFP/8QKLRBwWLwU9/Smwt3VVwRemM725EIvqW4Koyx8vLY4ks2jylOHQssLQal/oqeRxD929/+9lzquZFTf/GS5TT8tnUAgLdKV971GtBO/4ZXBwb7T3RGSHDCLQaM4erT8BTENLXua4B7SjMNMuLFbkiUwH6mV3gGVGmvsSua1F+/WwhZhgmyhYM4W1zZ9PECsFI/b5gbVS+TtHmrCT/tcMKUYfiy8nvbRQOqLd74y6OhSz4fwUqsdPDqhAZFGcrOZvDqNbIZvBbDVX8tcBsHHL20nAzjj5s/6BhUwbS1/GFH9yQmoQmIo+7/6jOPrpw7dOh/m+fkgxOYF0QoCnMCAlkoDJIEfTAtdrSrgRP4ZE7qTKgXfxQ4QRZaD5zGO7XLzJj6l50e/ZJp4ty/2eUmOsOPW/cp8JNkVfcv7E2G96gXx9rkTUT+TnzEmWpfl+vH8cdZnIv0DhySJTC2BpVnAJf+3buiTf3b6MVhZjomtXEXZwwzqy5fAmaa+AiLwawbXjfly6Apm1R2Np7MZnq2Rkc2IMzgxpafiC+Prxmn4i7w0sadmBVd/T9uyP+ekoI0j2PVW9pr8l2MwKA4XPnZ+DBWKxs/tvQmQlT3fPKVFE93TUh0Zd+yRFyncgVWa0ALb+gLF0pVw2DDalxjZFsaEy9P2T4lTTyDi6oqks+nirTBS6OxxDTIMZRAB7Olc+5AKTXTCykRhxwaVTYOilG1rhgZxtrrgW6H1c1sjrp+MNubl7nhnOvOFcHMK8CMo+VPlOgbXQk+hn0QRXDKdW1tCUJDyDqjjS1RI5t9rdXgajPD2oKgmt9Z2Z37LOyqmozJ9zE9eypmPPpjm+XY8QVbPwLktEHBYR2XCjhUqJWY2qFhnY5SJLA8CNSs+9+PB7Julq2wKuxtZoRbAdjmH+ns3ds6Jua3R7JL7pMdLRq2OrYDNlh+SC4V5ZkAUKHeFqAIi/6hTWCP0Yt9lmfGAw4IoZ7+anRFSNkThnkK6A6hqe+Edcpi2ocZrNS54rUNDZwf0jdPBtviDlunwdKYZ7IvmGMFrmUWMcoM3sVmSs9ktsCoZDFDWDCSGgAu3OUiRZ8CnfkjQAzb4UuiDE5aMy/QPsVFEmfV4Ftf5YfPSUYJF78gopENgpaW/JyulegUxUixphsnOvxtZm2+eqDOP+5OxejSS3UEPP88xTRo/scsUWOUI2KxwBecpXtUG2jV0GPFXhv+tusTMZA8W++3ZZfXQ868JSkRAl0+9zbkatE467ajB1iuZq9RVOJTnJ4GkOo1DIOLeZFL1cWI2RGGxLATnsJAudXWAs8ig6Vhvfh0csbnGAtNDDc3+4tu48MxJa/zb1max2J+VmYPpifg9l+GH1eOBli95ZHAG38xLNwlpKj1a+I9q7MwM9eKXTK9UEwEGcUG6TlZmF/PIo3LqA+mMVa6FcBq/UvKZUA047LRDj9LLxT7nMfbCIjXS8vJMP64+YB4gyqYthYPiDeEbLXKtbeqSJ2mAXbGNGWzhLSC1LIZ5paPCLvdXHlrBN1ck6rJwGsqLAe+y+8VKbI4vThVjw3H9hKTkLxx1R5PpwEnl55w8x5Qq54NIBfD4pu8OB1oDGPfwFNvJAxtcpyYXzePi1GX7YDgLj8mu7lRQBuVYdD9fB44aJXZDhAi+t/fi/x0VKKAIZE6r/t5loGINiiLEAg6KsMEBA+qoVGuNbgQQG77HguJlwWcDr4v5/U4lHwFkw+V2FO6LiSEZp+9WPbrrPMXKtZiIPpQ7IPk+dHNXWibHJPul62n92nVwDTEG3zh3t/Eonle0Mw5zcWjZvEZbh9j82MZP5C3SS1N8RTBwUFXGkuVlRyUhyc4m2iqnFqYdhcPpwpiDfhW7H3EDwAxvE9ZCl9UwmUHPnrWumZEDVLKEgQ8ZJ0NO6MiWwLM+q91LAObGa912AFn6Wsdvyf31au42EfXp2L3GJdk/1dSPSp0cO9Gw/3DXgqO2fhjOE8yV9zlQRcUJsCuWBwi3FwHVsi1SwP5GkhyUJ4ZJj1WCPCENut5T19xXVj7yH4KK50NrQlqs82RnHHG9eiy06Y/8oqsf57dSClL0P66bQyNiqx/nn1DvtVov85rBmXvnDaxPwkIzokDlm9+7xLSahM7mRDOvA6Chun4euAymxtyxQrXLcvdO7x9TI5NMqpVj2S9kHxAv+HHbQNo0GP9w1gvKt0xguV27bXAyJE2HPiCMAEVbTrWE4jQ2xJDhWUPaRsxjn7P6A1D1M9j+l8Foy84W6lORdZkRCQB7hoHmg8zIgtTG67kDObArD6bmP0OD3jm9ypW2Nyeb7ECnmD9Jee2ORXkVVxUTKq/kGkpDTARJRKmJGLh+aSPlHTDtLmCdJEShDYxSq0BZnOOVU7oWny4krC1/jP4NQBrxhN5J1wtfTD/6pHsvuQnMeSZ9LPag0mUnCuTS+d52wyqpRctZFAzgz3DAFKhIcrdiVUXXPbtTkWt8cN1/ES3Hq+ypOHraQdStzvNNyys38TCbW8rSvqg2mR6YjX46A8y9Br56udgGwegUlrRwp6QuAHELyhtDkzEuovhs4HB17on3uUPEfPvpiPVOw0CHbfjIJbNAkimVZU0ofYtdDYLgztWKUxzgoirgNomVp7LoWrO9aYtnBZfavrHT7gYkCJ0zgMym4EKfeZ7e/pc7oqkTTU5c/gPtm35OTVfunlYyDptAiS1fl/jirwnZXOBM3pT5If5UMI3LuyG8UWbx4egEKZFtjPWApC7/Cc8VgKPsSuWm9Xe3ydp/QuJZgrNMDTIMxp/3fr57KgKanWz8L2Pi10qBDZstDB6Bkq0cITNQXRhdZPOEr5QNlMgOA362MxGmrrLHaZVedEEJ08OcfF0+X33GGcP5Kb+Il6dirqJ3ZMGXh0BD63+R7yXoSLwJ2LtL4EwAekVBg+tHpiGNB2wDmjQP35iYgFMcJZfDAwv492Xq6yWZfcl6CI3yOijEJ4TSUmz+cmuSrNNrJhVuFv/nY/1gW7GGyBTMLf0RZA/T+RE9peHOEkvqirePdLD9jeJZqptnxoqCORAyYUEZiDF5jNOwXqhtn2SBWffjNhRK/xOHzyNI+KCw3MFszg4RngVxgIhS22q4OhCNcfKtwZsMW5Mqcq0jv0hHJotDBbzaUzNBRNzHPOiqmW7rx1sdLt7JPtTSu7i8ov64SNLxK39uAL8KpKTQcilwZWEeceo1DkMXnidMA12EibZQyPjwlCpG0rz9mIjqMP0fg2WwIUXHxCILQwzIlr3vRewMXohZ/b3S063rvM0/ZRXNd67ffSmo0am9Dx22NGof68aMN7lkVjPuNnR1YVzSPVlFkdzYvvcqlYqRDytmrRp0WswA8z0lrdqcwl8NT9cZOU3zf4YQyL2av/zPInQp2DM1waFwlwrwtYo4qKJQV/lBzpSIh0YU2Vu38U2LeYGHX4/I4+lNLVVczPDCM5u6preNWCKWUskefJLVslfZ8ZPL9tyO/Rp/mDpjpgqc7sjtmluJ579/YzckdLUVs3NDKPm33LGHKEXpbRB44/znO/YI8mTO4LNsw789LIteIOSnh7dkK8J+faWpMf7U5o14TSwaz1F/dnXfCo5gBNNgOiMXBiuR6zaXhKHXIHp/LqjUvb5YojyeyQNGWOFYOIqrwNZTm5tUV+GB/P5eK0NuaoN3KxxhNP2btHYY2jeuzOXdZ3qqfmo6hqk6O+M5XvyJinK6nVcxZ/jUj7HaWrdkmp4WUETjrY/M53Z/d4cUh3if/y6/5zXfR1/TscqksMRGMffX8UVeaCxAGT2bCnYCEtgaKr+V7ORCDQzlEBNDIUG9u/yXZwm/yT7vseBhgAaqEmAzNR4nD2c6Lsfuc2hCGxqKMWoR26rgu7Flvmp2IGtgWRKJSVKgxTXpDgkZVljvN/kliSQSaDWZSpDy/yDDKlVvhhqkacw6ZmnKaQb/RnUh5YguPYHFiDvvlDVQl+OtNUwDVGaa6DQWWwgQjaraU/fkLGF4X2W1MBQAvEfCk0KNJeRQEc4lIDi94UmB9gFr3xPqscc+nREAtAdCjSmNqvaPddu7Gs9nELfjVAOtsiTGBocd5iktsYiqJmx1PQV9RMp+RPqS8Dvpy80sB/TrEr8xyKogbHUBDP1gKsfbdFD7XWyq04F1N9DCWiivhDXA5pWBAJNf0QdUfQ+prhG908TPjgpyAF2cCCVrtc4QpMIJE2+kuLpLjlA2vPFYKMcBc7abGBYlcFZGo3NWTLbxocYb2+StIIHMmMVlGhSLZykmo9LotDBsqdC47Kr+D7OTvcxhbTGODyVTg6WEi2LQQhz6zwFTvPbI9kl98mOLoGYEI4qG6joddaA66DtAlf/0D0okQdHLTk4VmprOEmHlstGImyf3sXQeo0t1PQWLce18ykukjgbA0i+yg+fkyxW9AumkkYubT2DvH+eYvrLxyyBhh2+GJKBp3CzDt4khqG3/b/9dyRWVAuEk8Qal6KeXTRWrFk6cguMdDVME4PhOZM8KRiKwAnBUGrar0lIcV0k4FyeKQP3asZiQyPjpRWpjbEIaqIpNXK//F4P51mcXpyqx2ZHrXVNyv0EPTkkhb6GQToaNUmxgGHKoHZpcRmhFjGUVrWrxxZqGsLt8FFiVSNa/h0Fhv/vRX46qhrpCjUtdRSGlrqgvFIj3e8Q/64IuaTg06Aq1xQ8mW5RwVMapICTsUpSwGSQFDAlUgpNy/rWcN2ocC9MmbI7UesWJhUp3EhbpmykLTY0AqYclJoDqaCGQULEnpJiCTwWqfaRUItdPpuZshV1p/EURpNyqY4AY3LlsBk5EqN6YmYBQEWRBFZTpDJtA8nR7uX9IJkG3BiSyWwbV82gVIQoMXCzJmVsaqD3FZQwDhTEluIg5MAJYN7eZiPZylvcbCm4zc0S4JtqA0PrmmspDE22RMZTLEgzpUYYTYDQnfA0hKfRzEZ4QuOChI/7ByxBeALdzFEgNfXhGGRO7r2xDOy3sdjkH7l3pbJv5IpBv8hRGGGZKhcCTBkMxRQ5xf9YqBthyqBGmGLTPIpkpF5s6Zy7TALOqyQq0xKx5kHo+vUzeFQqlINLRZ7EeMaUg6cA3e/wmVKOOGcYo2LJw9JQBB8o9qUY0YddD1iDoVipCHbbRBnKRmpYSQmenKmILXaJ78jhmMKuBCYz7RqPlMjTMI0EMonudAzdcr9NqW5YotDtdmKbvSEN2V59jCkSwHMLnsZk5CIvy5p5qm5VJgGNLFGZjNzOfZRn3kI5aGCexLhDC0a7ALZqQTp4zxYkxQuib97YqHlLjYtsI2+hccXglhlHYfxiD8c4eYD81lgEf6F9qckztTMgnVMSKUB/JBIh9j3fkaoihcE9qwhVe6IQrdEEcVn70b9I8vAI9alQDqvPkeAafJ3U4C5htWUSTbMMlWkQfsp2mjGYLQWHYJbAuPcshpoA9ptFEniPWaRCtTxE3VA0O5Sr2xxILK/qGS7Q6ckxV/iihhR/FMvdY1LfvFLQma9G3ZCynjHTK9SIm1+dtuoLZhKF7iJbR0TMDfc7xZprCzKJbss5ujge04Ts7/KOPrGQQj07kChwMnTkZgn6G0OIY2fk6XM00qFBOdx4xF02xV865aKR449ZpZASsk0kEtXdUZ4KMRIODzTB4W8oVY15AwHiwqC6Ka5UdW0Q25T64ZYKawCpBnIAtY1EJjEQbUsNMq8S9HfJo/EmOlNHc6t8rCA+omDyLQnX4msR1Ffe+Zqa6+4tF8z19We8+ljTsFf/zXaBqYMZBXriMFpE+2rB3hzSGM/6sTK67cwuWwZXUa2q6qEB1dT0eEDLCfLtIFftgwHvpryLiwdSOZiyq6g2gFJhnYLrNGE9jGm/R54gxCfIPgxpVYYefrip1r7uiFqOsHIsyUQhpRr8y5ShnuLNib2K/PQ86ufnpJvUJ1p/gq+sVhJ+f0EV1b+q0HARFi4AM9XrkCAGVHsRfGWNJwGUVai3FpOxb1eigS9gHpAwgCmg9zitCbTPbOxV79agOqVFEv/qCutpWk/1GstdRcF0akV5Qu+9NKPqw8Jy2GrQOE81sXnWAM4YtIor9lI4BkENoXaCamIv/bgGM7xM0iZY8cBZYwSBNJwJUBiaoHQfw2NkrdZaog35CQgPcDkGqle19mboX05qp8YyUYjpsfj6k9ZUvux0mCZzDwuj4YEhMFuGKTVTYP41Yzv/hR9E8vXAx5BtdaHIu/r9ZqxZfTggh6wGJP9q1B/e5Gn6XaLx3+PiXn37Wave5tqrqdvK14zymGpqpcRnz1Qp1ZNmsCZoFrkwrHnUYz+mmqc+X5l5+kMYK+yIlUIiB/I9UllIw9igRqzk36UuZBbgJTw4f9RXCIkT4Mk/x0T3ht95sI34x/rKoZan0ygDBA9otdAEBXDFlbvayof4ERs9QGkNVHWjinLMA1ZZdRgDPS9FCAOYtSkMgQcDdyYxz+aUVYJN61w7wItZrAxyhqYYprLj7EBpCZk2xFxGEWODNYTHmYyomhkPEmkwNMxkAi76h2bVA9L5X/lAcUVoZW28EOeeF2KfsE0oIaCu41ktCBVKRmB5EDOZPxKQPNiHsoxJFHFizMYxVAxmJn3sF5YbMqCLw1mZJnaNIvAOeJLmwEZzEmO2C84gCK58HCANb0U8H99o1TduC2Ett5lNtdIvQL45qr3ipKYOt80q3Y/ldlqZ0iDG0FxSUlMblVJoA6ixDpNo4kdhNwvQLGbYR0DE3mItahM9K4iJzYM4pnawT3Rr5uQDkjnZlGOhNhAUO42aQhcTbasmdoDp/NjEWCu8ibTTSXRdo/JarXHzxlWakQZZiZTNok2q5zOLUTB8uUA1OuZw8Bn3iRDSUc7uDcOeA0S38eGYkjFAoVpxgVIzdRHCJLbTFkUIxBlVHiIkRvzFdVllBaX5RpD7JXwp8iOtrg7qOEV9zdglE/kfrcKr2oex1F6ZkonUIrtfmRKDbdKa44/+bgZin+7oK5ghPuG6nOkj8fyeRKEp4v7o7E9wljKRLpaq9r4lrmIINGHiy1JuVnFi7U03RIvV2gmgCmEUKawtrcr86kddGpLWrK9AFkxhLsDuqDEcONdR5YiNcavQl6UxiMxF0h1FBiPkyrXlin57tmOp7daWZrKgy/Qm/RJZtrovlqHzKPrM3y0Nd4t4NgDSqQV3fTPABXmmteAYzq6K6nyTQBHCLYVTD4o5HY3X69T3+OAKmtXb5Ht8UIRrjos2YrUn03R2R9qlpZ7cr6swxxBFW20DkcSv4lIE8LGep0XPKL9mKSsT+V/Khle1j7seXdfrs8e4JPu/kuqRaUFW3FTFmzpcXTGyPK2qjBrvbgjuux35q80AV/CkCFhT+cWjQvZ7Ms1Htt/R9uFr+fULS5lniPhvcIgMSQCHyGYrGOuBiQjsVQRyC2inQlr6EBMjTS4FygSTHMGPWYzfhbmSX3QsY5o+bYP6mxAo/CotJra47cNEeHrdwWaliEa+ai15Qk+CgzWl8VWbQcN1IdSH3DAu+UTCLa35uFwgBocHUoZxdUCOk+5L1qUumbDBjehsJa3//g4dF0BMxWJ4sqQjN64IvDxRUiWh6b5+Q2IZDwbSfRlq4hAfx+Km0CwYlbT+141zmwFO3RMBiYUAz4Gtq8GLKhdSCxxTeiM9L/nGCDJRkct4w6f8ia6ypEriVDOX1FXwPY+EExt1Q48hWdF0Y/RzZbkps12UdQOpq+UET8Jx2aYcolyNOZYiKd+SbDkduWYoh/NBtUO6PsmTio+Kh2eT6IYukC7EqDWP2iZ1g6spqudHLSk7l/kAGaYOdo4MpYMYD+K0eR1cLt6xWcCiN0V+0NlDRx7CIHDCs25io81fNtkUd7mFIRjirZthyNYWadZvMpH/hZuUca6tqUwm5+LZUyDKMezbQUpzX7tfwpKy03U+XpUTziVeaU6jdSWHuHi6/L57jLMHclObdsxoBixLjJV0RuFTrHUGgdOn8SsUNutbuywBk7pNNAL9A609T70ttRVp3LSDobFOCNdnSGNHGWFz0vkzk2bdbqrif/m+rInAfGXRmwR2pBpqzd0F5yv82vxst+0DCEy6tUlmifiEZ1qj8LRqxaAkbFQfXXI1lWlUBgmGDq4NLEjYSl7VWgliuIxbEZ/JVraRhlozngD5ddthRZM3l8cdlFmsBZ42YZirOcakZ9HAWmUMgNa7IgAPOcEbw0aTtM0tbwObWSlqdkhe5VlZFXHSuNF6OTaMxn3s4bs8kjMyAQsaX7zNkwCHQMX8OKnIVNUOmKbsUx7MzmaqQFiSIdcrZZUUY1GTMDm3sEDh0nTJVpvIMTzqgCxlt/3rKVVOsGmGHd9l6c010OmFR7/7WkBtJrEatve5XGzANHwax/B4AlLRtTN1TY65aYYdQ6PrzTXQ6YVHh15fQO1u2STlyEOPaor6mhfJ/tuaYSRVqikvQc35Bj12E1dgZXS+JlJ1tb7bMZ8T3IymdeM6M3Lx3TfZyOZAKwKh/+0e/6q/eNYyaQxf9zIphrIXz9osoN0P9Z9VXsQP5H2+J2lJf33x7OZU1z6Q9q/XpEweRhYvap4ZoYmRR6Y9zVV230Q4p/kjBYl6kr54yBtexfu4ii+KKmkCOdbFu/pbovlt6bWKZgPkM9lfZR9O1fFU1SqTw+eU22V98Uzf/otnkswv2shypQ8VajGTWgXyIXt5StL9IPebOC2F5aaKxava+r+T+ve2L+tPsyIPTwOnP/IMyagzX71IJdm+/uT65PXlh+w2/kpcZPtYknfkId49XTd5q+gtERUTc0fwZn/xOokfivhQdjzG+vWfNYb3h+//9f94lJU2u6MIAA== + + + dbo + + \ 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 8764a6a9ed..b328653e66 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -366,6 +366,10 @@ 201509150931528_ExportFramework2.cs + + + 201511121139009_ExportFramework3.cs + @@ -687,6 +691,9 @@ 201509150931528_ExportFramework2.cs + + 201511121139009_ExportFramework3.cs + diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExportTask.cs similarity index 96% rename from src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs rename to src/Libraries/SmartStore.Services/DataExchange/DataExportTask.cs index 54ed0768e6..0622be6566 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExportTask.cs @@ -2,7 +2,7 @@ using SmartStore.Core.Localization; using SmartStore.Services.Tasks; -namespace SmartStore.Services.DataExchange.Internal +namespace SmartStore.Services.DataExchange { // note: namespace persisted in ScheduleTask.Type public partial class DataExportTask : ITask diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs similarity index 99% rename from src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs rename to src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index 43cf433f7a..b409401da6 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -21,6 +21,7 @@ using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; +using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; @@ -33,7 +34,7 @@ using SmartStore.Utilities; using SmartStore.Utilities.Threading; -namespace SmartStore.Services.DataExchange.Internal +namespace SmartStore.Services.DataExchange { public partial class DataExporter : IDataExporter { diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs similarity index 99% rename from src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs rename to src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs index e3de6b5e56..73cb0ae431 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs @@ -21,10 +21,11 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Html; using SmartStore.Services.Catalog; +using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Seo; -namespace SmartStore.Services.DataExchange.Internal +namespace SmartStore.Services.DataExchange { public partial class DataExporter { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index 414e5c073a..f2860ed481 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -104,7 +104,7 @@ public virtual ExportProfile InsertExportProfile(Provider provi task.LastEndUtc = task.LastStartUtc = task.LastSuccessUtc = null; } - task.Name = string.Concat(name, " export task"); + task.Name = string.Concat(name, " task"); _scheduleTaskService.InsertTask(task); @@ -196,6 +196,9 @@ public virtual void DeleteExportProfile(ExportProfile profile) if (profile == null) throw new ArgumentNullException("profile"); + if (profile.IsSystemProfile) + throw new SmartException(_localizationService.GetResource("Admin.DataExchange.Export.CannotDeleteSystemProfile")); + int scheduleTaskId = profile.SchedulingTaskId; var folder = profile.GetExportFolder(); @@ -224,7 +227,8 @@ from x in _exportProfileRepository.Table.Expand(x => x.ScheduleTask) } query = query - .OrderBy(x => x.ProviderSystemName) + .OrderBy(x => x.IsSystemProfile) + .ThenBy(x => x.ProviderSystemName) .ThenBy(x => x.Name); return query; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs index 401b9a165c..8733ac6a1e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CategoryXmlExportProvider.cs @@ -10,8 +10,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports XML formatted category data to a file /// [SystemName("Exports.SmartStoreCategoryXml")] - [FriendlyName("SmartStore XML category export")] - [IsHidden(true)] + [FriendlyName("Category XML Export")] public class CategoryXmlExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs index 133ea6e3a7..75437762ee 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXlsxExportProvider.cs @@ -14,8 +14,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports Excel formatted customer data to a file /// [SystemName("Exports.SmartStoreCustomerXlsx")] - [FriendlyName("SmartStore Excel customer export")] - [IsHidden(true)] + [FriendlyName("Customer Excel Export")] public class CustomerXlsxExportProvider : ExportProviderBase { private string[] Properties diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs index 7b7611fb2a..a5f7a56048 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/CustomerXmlExportProvider.cs @@ -9,8 +9,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports XML formatted customer data to a file /// [SystemName("Exports.SmartStoreCustomerXml")] - [FriendlyName("SmartStore XML customer export")] - [IsHidden(true)] + [FriendlyName("Customer XML Export")] public class CustomerXmlExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs index 74eb5551a7..e4bb40c427 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ManufacturerXmlExportProvider.cs @@ -10,8 +10,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports XML formatted manufacturer data to a file /// [SystemName("Exports.SmartStoreManufacturerXml")] - [FriendlyName("SmartStore XML manufacturer export")] - [IsHidden(true)] + [FriendlyName("Manufacturer XML Export")] public class ManufacturerXmlExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs index b36b4286d9..f6b34744d0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXlsxExportProvider.cs @@ -12,8 +12,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports Excel formatted order data to a file /// [SystemName("Exports.SmartStoreOrderXlsx")] - [FriendlyName("SmartStore Excel order export")] - [IsHidden(true)] + [FriendlyName("Order Excel Export")] public class OrderXlsxExportProvider : ExportProviderBase { private string[] Properties diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs index 6a878ddb84..85069df675 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/OrderXmlExportProvider.cs @@ -13,8 +13,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports XML formatted order data to a file /// [SystemName("Exports.SmartStoreOrderXml")] - [FriendlyName("SmartStore XML order export")] - [IsHidden(true)] + [FriendlyName("Order XML Export")] public class OrderXmlExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs index e908fa8001..284388bbb0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXlsxExportProvider.cs @@ -18,8 +18,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports Excel formatted product data to a file /// [SystemName("Exports.SmartStoreProductXlsx")] - [FriendlyName("SmartStore Excel product export")] - [IsHidden(true)] + [FriendlyName("Product Excel Export")] public class ProductXlsxExportProvider : ExportProviderBase { private readonly ILanguageService _languageService; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs index 4c2f86ccfd..a5f88db4b4 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/ProductXmlExportProvider.cs @@ -9,8 +9,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports XML formatted product data to a file /// [SystemName("Exports.SmartStoreProductXml")] - [FriendlyName("SmartStore XML product export")] - [IsHidden(true)] + [FriendlyName("Product XML Export")] public class ProductXmlExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs index bf8b192421..ff2873ee92 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Providers/SubscriberCsvExportProvider.cs @@ -11,8 +11,7 @@ namespace SmartStore.Services.DataExchange.Providers /// Exports CSV formatted newsletter subscription data to a file /// [SystemName("Exports.SmartStoreNewsSubscriptionCsv")] - [FriendlyName("SmartStore CSV newsletter subscription export")] - [IsHidden(true)] + [FriendlyName("Newsletter Subscribers CSV Export")] public class SubscriberCsvExportProvider : ExportProviderBase { public static string SystemName diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 83fe5c10e4..ec4af99cbd 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -168,9 +168,9 @@ - + - + @@ -198,7 +198,7 @@ - + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml index 8e7528f68e..a80721be22 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml @@ -9,7 +9,7 @@ Google Merchant Center XML feed - Allows you to export product data in the XML feed format of Google Merchant Center (GMC). + Allows to export product data in XML feed format of Google Merchant Center (GMC). diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 354da99fe9..f15c4fe3a0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -104,6 +104,8 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile { model.Id = profile.Id; model.Name = profile.Name; + model.SystemName = profile.SystemName; + model.IsSystemProfile = profile.IsSystemProfile; model.ProviderSystemName = profile.ProviderSystemName; model.FolderName = profile.FolderName; model.FileNamePattern = profile.FileNamePattern; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index 493636f238..7ea59f0921 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -24,6 +24,12 @@ public partial class ExportProfileModel : EntityModelBase [SmartResourceDisplayName("Admin.DataExchange.Export.Name")] public string Name { get; set; } + [SmartResourceDisplayName("Admin.DataExchange.Export.SystemName")] + public string SystemName { get; set; } + + [SmartResourceDisplayName("Admin.DataExchange.Export.IsSystemProfile")] + public bool IsSystemProfile { get; set; } + [SmartResourceDisplayName("Admin.DataExchange.Export.ProviderSystemName")] public string ProviderSystemName { get; set; } public List AvailableProviders { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index 55bac26fec..ecf22fd744 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -35,9 +35,12 @@ - + @if (!Model.IsSystemProfile) + { + + } @@ -73,6 +76,22 @@ @Html.ValidationMessageFor(x => x.Name)
+ @Html.SmartLabelFor(x => x.SystemName) + +
@(Model.SystemName.NaIfEmpty())
+
+ @Html.SmartLabelFor(x => x.IsSystemProfile) + + @T(Model.IsSystemProfile ? "Admin.Common.Yes" : "Admin.Common.No") +
@Html.SmartLabelFor(x => x.Enabled) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml index 740e0447ce..6448df05a3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml @@ -31,6 +31,7 @@ @T("Common.Enabled") @T("Admin.DataExchange.Export.EntityType") @T("Admin.DataExchange.Export.FileExtension")@T("Admin.DataExchange.Export.IsSystemProfile") @T("Admin.System.ScheduleTasks.LastStart") @T("Admin.System.ScheduleTasks.NextRun") @T("Admin.Common.Actions") @profile.Name + @if (profile.SystemName.HasValue()) + { +
@profile.SystemName
+ }
@if (profile.Provider.ConfigurationUrl.HasValue()) { @profile.Provider.FriendlyName.NaIfEmpty() } + else + { + @profile.Provider.FriendlyName.NaIfEmpty() + }
@profile.ProviderSystemName
@@ -62,6 +71,9 @@ @Html.IconForFileExtension(profile.Provider.FileExtension, true) + @Html.SymbolForBool(profile.IsSystemProfile) +
@Html.Partial("~/Administration/Views/ScheduleTask/_LastRun.cshtml", profile.TaskModel) From dda09131ef478a0a10b7e4d7381438723df69204 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 12 Nov 2015 20:20:53 +0100 Subject: [PATCH 077/732] Cleaned, renamed and (re)moved a bunch of extension methods --- .../Extensions/CollectionExtensions.cs | 4 - .../Extensions/DateTimeExtensions.cs | 1 - .../Extensions/EnumerableExtensions.cs | 2 +- .../Extensions/HtmlTextWriterExtensions.cs | 7 +- .../Extensions/HttpExtensions.cs | 3 +- .../Extensions/MiscExtensions.cs | 29 +- .../Extensions/StringExtensions.cs | 1 - .../Utilities/Reflection/FastProperty.cs | 10 +- .../Extensions/MiscExtensions.cs | 43 +- .../Extensions/DateTimeExtensions.cs | 154 ++++++ .../Extensions/Extensions.cs | 455 ------------------ .../Extensions/HtmlSelectListExtensions.cs | 66 +++ .../Extensions/RouteExtensions.cs | 9 +- .../Extensions/TelerikExtensions.cs | 233 +++++++++ .../Plugins/PluginExtensions.cs | 32 ++ .../SmartStore.Web.Framework.csproj | 5 +- 16 files changed, 509 insertions(+), 545 deletions(-) create mode 100644 src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs delete mode 100644 src/Presentation/SmartStore.Web.Framework/Extensions/Extensions.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs create mode 100644 src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs diff --git a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs index e5ab2c829a..b6fec3780a 100644 --- a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs @@ -7,10 +7,8 @@ namespace SmartStore { - public static class CollectionExtensions { - public static void AddRange(this ICollection initial, IEnumerable other) { if (other == null) @@ -31,7 +29,5 @@ public static bool IsNullOrEmpty(this ICollection source) { return (source == null || source.Count == 0); } - } - } diff --git a/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs index fc0cf3eacb..6ec7fc1cf9 100644 --- a/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs @@ -5,7 +5,6 @@ namespace SmartStore { - public static class DateTimeExtensions { private static readonly DateTime MinDate = new DateTime(1900, 1, 1); diff --git a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs index 34b4babed5..bae0a18392 100644 --- a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs @@ -170,7 +170,7 @@ public static void AddRange(this NameValueCollection initial, NameValueCollectio /// /// Builds an URL query string /// - /// Namer value collection + /// Name value collection /// Encoding type. Can be null. /// Whether to encode keys and values /// The query string without leading a question mark diff --git a/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs index 26edc0d0e3..27b16ca907 100644 --- a/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs @@ -4,11 +4,9 @@ using System.Web.UI; namespace SmartStore -{ - +{ public static class HtmlTextWriterExtensions { - public static void AddAttributes(this HtmlTextWriter writer, IDictionary attributes) { if (attributes.Any>()) @@ -20,8 +18,5 @@ public static void AddAttributes(this HtmlTextWriter writer, IDictionary 0; - } - - public static string GetDataType(this DataTable dt, string columnName) - { - dt.DefaultView.RowFilter = "ColumnName='" + columnName + "'"; - return dt.Rows[0]["DataType"].ToString(); - } - - public static int CountExecute(this OleDbConnection conn, string sqlCount) - { - using (OleDbCommand cmd = new OleDbCommand(sqlCount, conn)) - { - return (int)cmd.ExecuteScalar(); - } - } - public static object SafeConvert(this TypeConverter converter, string value) { try @@ -79,6 +59,7 @@ public static object SafeConvert(this TypeConverter converter, string value) { exc.Dump(); } + return null; } @@ -128,14 +109,6 @@ public static T GetMergedDataValue(this IMergedData mergedData, string key, T return defaultValue; } - public static bool IsRouteEqual(this RouteData routeData, string controller, string action) - { - if (routeData == null) - return false; - - return routeData.GetRequiredString("controller").IsCaseInsensitiveEqual(controller) && routeData.GetRequiredString("action").IsCaseInsensitiveEqual(action); - } - /// /// Append grow if string builder is empty. Append delimiter and grow otherwise. /// diff --git a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs index 4a7f28e209..10a5fcc557 100644 --- a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs @@ -11,7 +11,6 @@ namespace SmartStore { - public static class StringExtensions { public const string CarriageReturnLineFeed = "\r\n"; diff --git a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs index 65a726248f..ada0e55b7a 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Reflection/FastProperty.cs @@ -31,7 +31,7 @@ public class FastProperty private Action _valueSetter; /// - /// Initializes a fast . + /// Initializes a . /// This constructor does not cache the helper. For caching, use . /// public FastProperty(PropertyInfo property) @@ -100,7 +100,7 @@ public void SetValue(object instance, object value) /// underlying type. /// /// the instance to extract property accessors for. - /// a cached array of all public property getters from the underlying type of target instance. + /// A cached array of all public property getters from the underlying type of target instance. /// public static IReadOnlyDictionary GetProperties(object instance) { @@ -111,8 +111,8 @@ public static IReadOnlyDictionary GetProperties(object ins /// Creates and caches fast property helpers that expose getters for every public get property on the /// specified type. /// - /// the type to extract property accessors for. - /// a cached array of all public property getters from the type of target instance. + /// The type to extract property accessors for. + /// A cached array of all public property getters from the type of target instance. /// public static IReadOnlyDictionary GetProperties(Type type) { @@ -337,7 +337,7 @@ public static Action MakeFastPropertySetter(PropertyInfo propert /// is returned. /// /// - /// The implementation of PropertyHelper will cache the property accessors per-type. This is + /// The implementation of FastProperty will cache the property accessors per-type. This is /// faster when the the same type is used multiple times with ObjectToDictionary. /// public static IDictionary ObjectToDictionary(object value, Func keySelector = null) diff --git a/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs index ba396e84fe..70efa11f18 100644 --- a/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs @@ -1,49 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Data.OleDb; -using System.Data.SqlClient; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Linq; namespace SmartStore { - public static class MiscExtensions { - public static bool IsDatabaseAvailable(this SqlConnectionStringBuilder sb) - { - bool res = false; - try - { - using (SqlConnection conn = new SqlConnection(sb.ToString())) - { - conn.Open(); - res = (conn.State == System.Data.ConnectionState.Open); - conn.Close(); - } - } - catch (Exception) - { - } - return res; - } - - public static T GetValue(this OleDbDataReader reader, string name, T defaultValue = default(T)) + public static bool IsEntityFrameworkProvider(this IQueryProvider provider) { - try - { - object value; - if (reader != null && name.HasValue() && (value = reader[name]) != null && value != DBNull.Value) - { - return (T)Convert.ChangeType(value, typeof(T)); - } - } - catch (Exception exc) - { - exc.Dump(); - } - return defaultValue; + return provider.GetType().FullName == "System.Data.Objects.ELinq.ObjectQueryProvider"; } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs new file mode 100644 index 0000000000..3277e60cb6 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs @@ -0,0 +1,154 @@ +using System; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Localization; +using SmartStore.Services.Helpers; +using SmartStore.Web.Framework.Localization; + +namespace SmartStore.Web.Framework +{ + public static class DateTimeExtensions + { + /// + /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) + /// + /// Source (UTC format) + /// Formatted date and time string + public static string RelativeFormat(this DateTime source) + { + return RelativeFormat(source, string.Empty); + } + + /// + /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) + /// + /// Source (UTC format) + /// Default format string (in case relative formatting is not applied) + /// Formatted date and time string + public static string RelativeFormat(this DateTime source, string defaultFormat) + { + return RelativeFormat(source, false, defaultFormat); + } + + /// + /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) + /// + /// Source (UTC format) + /// A value indicating whether we should convet DateTime instance to user local time (in case relative formatting is not applied) + /// Default format string (in case relative formatting is not applied) + /// Formatted date and time string + public static string RelativeFormat(this DateTime source, bool convertToUserTime, string defaultFormat) + { + string result = ""; + Localizer T = EngineContext.Current.Resolve().Get; + + var ts = new TimeSpan(DateTime.UtcNow.Ticks - source.Ticks); + double delta = ts.TotalSeconds; + + if (delta > 0) + { + if (delta < 60) // 60 (seconds) + { + result = ts.Seconds == 1 ? T("Time.OneSecondAgo") : T("Time.SecondsAgo", ts.Seconds); + } + else if (delta < 120) //2 (minutes) * 60 (seconds) + { + result = T("Time.OneMinuteAgo"); + } + else if (delta < 2700) // 45 (minutes) * 60 (seconds) + { + result = String.Format(T("Time.MinutesAgo"), ts.Minutes); + } + else if (delta < 5400) // 90 (minutes) * 60 (seconds) + { + result = T("Time.OneHourAgo"); + } + else if (delta < 86400) // 24 (hours) * 60 (minutes) * 60 (seconds) + { + int hours = ts.Hours; + if (hours == 1) + hours = 2; + result = T("Time.HoursAgo", hours); + } + else if (delta < 172800) // 48 (hours) * 60 (minutes) * 60 (seconds) + { + result = T("Time.Yesterday"); + } + else if (delta < 2592000) // 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) + { + result = String.Format(T("Time.DaysAgo"), ts.Days); + } + else if (delta < 31104000) // 12 (months) * 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) + { + int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); + result = months <= 1 ? T("Time.OneMonthAgo") : T("Time.MonthsAgo", months); + } + else + { + int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); + result = years <= 1 ? T("Time.OneYearAgo") : T("Time.YearsAgo", years); + } + } + else + { + DateTime tmp1 = source; + if (convertToUserTime) + { + tmp1 = EngineContext.Current.Resolve().ConvertToUserTime(tmp1, DateTimeKind.Utc); + } + //default formatting + if (!String.IsNullOrEmpty(defaultFormat)) + { + result = tmp1.ToString(defaultFormat); + } + else + { + result = tmp1.ToString(); + } + } + return result; + } + + public static string Prettify(this TimeSpan ts) + { + Localizer T = EngineContext.Current.Resolve().Get; + double seconds = ts.TotalSeconds; + + try + { + int secsTemp = Convert.ToInt32(seconds); + string label = T("Time.SecondsAbbr"); + int remainder = 0; + string remainderLabel = ""; + + if (secsTemp > 59) + { + remainder = secsTemp % 60; + secsTemp /= 60; + label = T("Time.MinutesAbbr"); + remainderLabel = T("Time.SecondsAbbr"); + } + + if (secsTemp > 59) + { + remainder = secsTemp % 60; + secsTemp /= 60; + label = (secsTemp == 1) ? T("Time.HourAbbr") : T("Time.HoursAbbr"); + remainderLabel = T("Time.MinutesAbbr"); + } + + if (remainder == 0) + { + return string.Format("{0:#,##0.#} {1}", secsTemp, label); + } + else + { + return string.Format("{0:#,##0} {1} {2} {3}", secsTemp, label, remainder, remainderLabel); + } + } + catch + { + return "(-)"; + } + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/Extensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/Extensions.cs deleted file mode 100644 index 504cc92ab6..0000000000 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/Extensions.cs +++ /dev/null @@ -1,455 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Web.Mvc; -using SmartStore.Core; -using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Infrastructure; -using SmartStore.Core.Localization; -using SmartStore.Core.Plugins; -using SmartStore.Services.Configuration; -using SmartStore.Services.Helpers; -using SmartStore.Services.Localization; -using SmartStore.Web.Framework.Localization; -using Telerik.Web.Mvc; -using Telerik.Web.Mvc.Extensions; -using Telerik.Web.Mvc.UI.Fluent; - -namespace SmartStore.Web.Framework -{ - - [Serializable] - public class GridStateInfo - { - public GridState State { get; set; } - public string Path { get; set; } - } - - - public static class Extensions - { - - public static GridBuilder PreserveGridState(this GridBuilder builder) where T : class - { - var grid = builder.ToComponent(); - - if (!grid.DataBinding.Ajax.Enabled) - return builder; - - if (grid.Id.IsEmpty()) - throw new SmartException("A grid with preservable state must have a valid Id or Name"); - - var urlHelper = new UrlHelper(grid.ViewContext.RequestContext); - - var gridId = "GridState." + grid.Id + "__" + grid.ViewContext.RouteData.GenerateRouteIdentifier(); - - grid.AppendCssClass("grid-preservestate"); - grid.HtmlAttributes.Add("data-statepreserver-href", urlHelper.Action("SetGridState", "Common", new { area = "admin" })); - grid.HtmlAttributes.Add("data-statepreserver-key", gridId); - - // Try restore state from a previous request - var info = (GridStateInfo)grid.ViewContext.TempData[gridId]; - - if (info == null) - return builder; - - var state = info.State; - var command = GridCommand.Parse(state.Page, state.Size, state.OrderBy, state.GroupBy, state.Filter); - - if (grid.Paging.Enabled) - { - var pathChanged = !info.Path.Equals(grid.ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase); - if (!pathChanged) - { - if (command.PageSize > 0) - grid.Paging.PageSize = command.PageSize; - if (command.Page > 0) - grid.Paging.CurrentPage = command.Page; - } - } - - if (grid.Sorting.Enabled) - { - foreach (var sort in command.SortDescriptors) - { - var existingSort = grid.Sorting.OrderBy.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(sort.Member)); - if (existingSort != null) - { - grid.Sorting.OrderBy.Remove(existingSort); - } - grid.Sorting.OrderBy.Add(sort); - } - } - - if (grid.Grouping.Enabled) - { - foreach (var group in command.GroupDescriptors) - { - var existingGroup = grid.Grouping.Groups.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(group.Member)); - if (existingGroup != null) - { - grid.Grouping.Groups.Remove(existingGroup); - } - grid.Grouping.Groups.Add(group); - } - } - - if (grid.Filtering.Enabled) - { - foreach (var filter in command.FilterDescriptors) - { - var compositeFilter = filter as CompositeFilterDescriptor; - if (compositeFilter == null) - { - compositeFilter = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And }; - compositeFilter.FilterDescriptors.Add(filter); - } - grid.Filtering.Filters.Add(compositeFilter); - } - } - - // persist again for the next request - grid.ViewContext.TempData[gridId] = info; - - return builder; - } - - public static IEnumerable ForCommand(this IEnumerable current, GridCommand command) - { - var queryable = current.AsQueryable() as IQueryable; - if (command.FilterDescriptors.Any()) - { - queryable = queryable.Where(command.FilterDescriptors.AsEnumerable()).AsQueryable() as IQueryable; - } - - IList temporarySortDescriptors = new List(); - - if (!command.SortDescriptors.Any() && queryable.Provider.IsEntityFrameworkProvider()) - { - // The Entity Framework provider demands OrderBy before calling Skip. - SortDescriptor sortDescriptor = new SortDescriptor - { - Member = GetFirstSortableProperty(queryable.ElementType) - }; - command.SortDescriptors.Add(sortDescriptor); - temporarySortDescriptors.Add(sortDescriptor); - } - - if (command.GroupDescriptors.Any()) - { - command.GroupDescriptors.Reverse().Each(groupDescriptor => - { - SortDescriptor sortDescriptor = new SortDescriptor - { - Member = groupDescriptor.Member, - SortDirection = groupDescriptor.SortDirection - }; - - command.SortDescriptors.Insert(0, sortDescriptor); - temporarySortDescriptors.Add(sortDescriptor); - }); - } - - if (command.SortDescriptors.Any()) - { - queryable = queryable.Sort(command.SortDescriptors); - } - - return queryable as IQueryable; - } - - public static IEnumerable PagedForCommand(this IEnumerable current, GridCommand command) - { - return current.Skip((command.Page - 1) * command.PageSize).Take(command.PageSize); - } - - public static bool IsEntityFrameworkProvider(this IQueryProvider provider) - { - return provider.GetType().FullName == "System.Data.Objects.ELinq.ObjectQueryProvider"; - } - - private static string GetFirstSortableProperty(Type type) - { - PropertyInfo firstSortableProperty = type.GetProperties().Where(property => property.PropertyType.IsPredefinedSimpleType()).FirstOrDefault(); - - if (firstSortableProperty == null) - { - throw new NotSupportedException("Cannot find property to sort by."); - } - - return firstSortableProperty.Name; - } - - public static GridBoundColumnBuilder Centered(this GridBoundColumnBuilder columnBuilder) where T:class - { - return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); - } - - public static GridTemplateColumnBuilder Centered(this GridTemplateColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); - } - - public static GridBoundColumnBuilder RightAlign(this GridBoundColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); - } - - public static GridTemplateColumnBuilder RightAlign(this GridTemplateColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); - } - - public static SelectList ToSelectList(this TEnum enumObj, bool markCurrentAsSelected = true) where TEnum : struct - { - if (!typeof(TEnum).IsEnum) - throw new ArgumentException("An Enumeration type is required.", "enumObj"); - - var localizationService = EngineContext.Current.Resolve(); - var workContext = EngineContext.Current.Resolve(); - - var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum)) - select new { ID = Convert.ToInt32(enumValue), Name = enumValue.GetLocalizedEnum(localizationService, workContext) }; - object selectedValue = null; - if (markCurrentAsSelected) - selectedValue = Convert.ToInt32(enumObj); - return new SelectList(values, "ID", "Name", selectedValue); - } - - - public static string GetValueFromAppliedFilter(this IFilterDescriptor filter, string valueName, FilterOperator? filterOperator = null) - { - if (filter is CompositeFilterDescriptor) - { - foreach (IFilterDescriptor childFilter in ((CompositeFilterDescriptor)filter).FilterDescriptors) - { - var val1 = GetValueFromAppliedFilter(childFilter, valueName, filterOperator); - if (!String.IsNullOrEmpty(val1)) - return val1; - } - } - else - { - var filterDescriptor = (FilterDescriptor)filter; - if (filterDescriptor != null && - filterDescriptor.Member.Equals(valueName, StringComparison.InvariantCultureIgnoreCase)) - { - if (!filterOperator.HasValue || filterDescriptor.Operator == filterOperator.Value) - return Convert.ToString(filterDescriptor.Value); - } - } - - return ""; - } - - public static string GetValueFromAppliedFilters(this IList filters, string valueName, FilterOperator? filterOperator = null) - { - foreach (var filter in filters) - { - var val1 = GetValueFromAppliedFilter(filter, valueName, filterOperator); - if (!String.IsNullOrEmpty(val1)) - return val1; - } - return ""; - } - - /// - /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) - /// - /// Source (UTC format) - /// Formatted date and time string - public static string RelativeFormat(this DateTime source) - { - return RelativeFormat(source, string.Empty); - } - - /// - /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) - /// - /// Source (UTC format) - /// Default format string (in case relative formatting is not applied) - /// Formatted date and time string - public static string RelativeFormat(this DateTime source, string defaultFormat) - { - return RelativeFormat(source, false, defaultFormat); - } - - /// - /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) - /// - /// Source (UTC format) - /// A value indicating whether we should convet DateTime instance to user local time (in case relative formatting is not applied) - /// Default format string (in case relative formatting is not applied) - /// Formatted date and time string - public static string RelativeFormat(this DateTime source, bool convertToUserTime, string defaultFormat) - { - string result = ""; - Localizer T = EngineContext.Current.Resolve().Get; - - var ts = new TimeSpan(DateTime.UtcNow.Ticks - source.Ticks); - double delta = ts.TotalSeconds; - - if (delta > 0) - { - if (delta < 60) // 60 (seconds) - { - result = ts.Seconds == 1 ? T("Time.OneSecondAgo") : T("Time.SecondsAgo", ts.Seconds); - } - else if (delta < 120) //2 (minutes) * 60 (seconds) - { - result = T("Time.OneMinuteAgo"); - } - else if (delta < 2700) // 45 (minutes) * 60 (seconds) - { - result = String.Format(T("Time.MinutesAgo"), ts.Minutes); - } - else if (delta < 5400) // 90 (minutes) * 60 (seconds) - { - result = T("Time.OneHourAgo"); - } - else if (delta < 86400) // 24 (hours) * 60 (minutes) * 60 (seconds) - { - int hours = ts.Hours; - if (hours == 1) - hours = 2; - result = T("Time.HoursAgo", hours); - } - else if (delta < 172800) // 48 (hours) * 60 (minutes) * 60 (seconds) - { - result = T("Time.Yesterday"); - } - else if (delta < 2592000) // 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) - { - result = String.Format(T("Time.DaysAgo"), ts.Days); - } - else if (delta < 31104000) // 12 (months) * 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) - { - int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); - result = months <= 1 ? T("Time.OneMonthAgo") : T("Time.MonthsAgo", months); - } - else - { - int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); - result = years <= 1 ? T("Time.OneYearAgo") : T("Time.YearsAgo", years); - } - } - else - { - DateTime tmp1 = source; - if (convertToUserTime) - { - tmp1 = EngineContext.Current.Resolve().ConvertToUserTime(tmp1, DateTimeKind.Utc); - } - //default formatting - if (!String.IsNullOrEmpty(defaultFormat)) - { - result = tmp1.ToString(defaultFormat); - } - else - { - result = tmp1.ToString(); - } - } - return result; - } - - public static string Prettify(this TimeSpan ts) - { - Localizer T = EngineContext.Current.Resolve().Get; - double seconds = ts.TotalSeconds; - - try - { - int secsTemp = Convert.ToInt32(seconds); - string label = T("Time.SecondsAbbr"); - int remainder = 0; - string remainderLabel = ""; - - if (secsTemp > 59) - { - remainder = secsTemp % 60; - secsTemp /= 60; - label = T("Time.MinutesAbbr"); - remainderLabel = T("Time.SecondsAbbr"); - } - - if (secsTemp > 59) - { - remainder = secsTemp % 60; - secsTemp /= 60; - label = (secsTemp == 1) ? T("Time.HourAbbr") : T("Time.HoursAbbr"); - remainderLabel = T("Time.MinutesAbbr"); - } - - if (remainder == 0) - { - return string.Format("{0:#,##0.#} {1}", secsTemp, label); - } - else - { - return string.Format("{0:#,##0} {1} {2} {3}", secsTemp, label, remainder, remainderLabel); - } - } - catch - { - return "(-)"; - } - } - - /// - /// Get a list of all stores - /// - /// codehint: sm-add - public static IList ToSelectListItems(this IEnumerable stores) - { - var lst = new List(); - - foreach (var store in stores) - { - lst.Add(new SelectListItem - { - Text = store.Name, - Value = store.Id.ToString() - }); - } - return lst; - } - - public static void SelectValue(this List lst, string value, string defaultValue = null) - { - if (lst != null) - { - var itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(value)); - - if (itm == null && defaultValue != null) - itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(defaultValue)); - - if (itm != null) - itm.Selected = true; - } - } - - /// - /// Determines whether a plugin is installed and activated for a particular store. - /// - public static bool IsPluginReady(this IPluginFinder pluginFinder, ISettingService settingService, string systemName, int storeId) - { - try - { - var pluginDescriptor = pluginFinder.GetPluginDescriptorBySystemName(systemName); - - if (pluginDescriptor != null && pluginDescriptor.Installed) - { - if (storeId == 0 || settingService.GetSettingByKey(pluginDescriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true)) - return true; - } - } - catch (Exception exc) - { - exc.Dump(); - } - return false; - } - } -} diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs new file mode 100644 index 0000000000..33455155a2 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Mvc; +using SmartStore.Core; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Infrastructure; +using SmartStore.Services.Localization; + +namespace SmartStore.Web.Framework +{ + public static class HtmlSelectListExtensions + { + public static SelectList ToSelectList(this TEnum enumObj, bool markCurrentAsSelected = true) where TEnum : struct + { + if (!typeof(TEnum).IsEnum) + throw new ArgumentException("An Enumeration type is required.", "enumObj"); + + var localizationService = EngineContext.Current.Resolve(); + var workContext = EngineContext.Current.Resolve(); + + var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum)) + select new { ID = Convert.ToInt32(enumValue), Name = enumValue.GetLocalizedEnum(localizationService, workContext) }; + + object selectedValue = null; + if (markCurrentAsSelected) + selectedValue = Convert.ToInt32(enumObj); + + return new SelectList(values, "ID", "Name", selectedValue); + } + + /// + /// Get a list of all stores + /// + public static IList ToSelectListItems(this IEnumerable stores) + { + var lst = new List(); + + foreach (var store in stores) + { + lst.Add(new SelectListItem + { + Text = store.Name, + Value = store.Id.ToString() + }); + } + return lst; + } + + public static void SelectValue(this List lst, string value, string defaultValue = null) + { + if (lst != null) + { + var itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(value)); + + if (itm == null && defaultValue != null) + itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(defaultValue)); + + if (itm != null) + itm.Selected = true; + } + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/RouteExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/RouteExtensions.cs index bfbffe4c03..f6cfa11a01 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/RouteExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/RouteExtensions.cs @@ -11,7 +11,6 @@ namespace SmartStore { public static class RouteExtensions { - public static string GetAreaName(this RouteData routeData) { object obj2; @@ -49,5 +48,13 @@ public static string GenerateRouteIdentifier(this RouteData routeData) return "{0}{1}.{2}".FormatInvariant(area.HasValue() ? area + "." : "", controller, action); } + public static bool IsRouteEqual(this RouteData routeData, string controller, string action) + { + if (routeData == null) + return false; + + return routeData.GetRequiredString("controller").IsCaseInsensitiveEqual(controller) && routeData.GetRequiredString("action").IsCaseInsensitiveEqual(action); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs new file mode 100644 index 0000000000..08cd2d8951 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web.Mvc; +using SmartStore.Core; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Localization; +using SmartStore.Core.Plugins; +using SmartStore.Services.Configuration; +using SmartStore.Services.Helpers; +using SmartStore.Services.Localization; +using SmartStore.Web.Framework.Localization; +using Telerik.Web.Mvc; +using Telerik.Web.Mvc.Extensions; +using Telerik.Web.Mvc.UI.Fluent; + +namespace SmartStore.Web.Framework +{ + [Serializable] + public class GridStateInfo + { + public GridState State { get; set; } + public string Path { get; set; } + } + + public static class TelerikExtensions + { + public static GridBuilder PreserveGridState(this GridBuilder builder) where T : class + { + var grid = builder.ToComponent(); + + if (!grid.DataBinding.Ajax.Enabled) + return builder; + + if (grid.Id.IsEmpty()) + throw new SmartException("A grid with preservable state must have a valid Id or Name"); + + var urlHelper = new UrlHelper(grid.ViewContext.RequestContext); + + var gridId = "GridState." + grid.Id + "__" + grid.ViewContext.RouteData.GenerateRouteIdentifier(); + + grid.AppendCssClass("grid-preservestate"); + grid.HtmlAttributes.Add("data-statepreserver-href", urlHelper.Action("SetGridState", "Common", new { area = "admin" })); + grid.HtmlAttributes.Add("data-statepreserver-key", gridId); + + // Try restore state from a previous request + var info = (GridStateInfo)grid.ViewContext.TempData[gridId]; + + if (info == null) + return builder; + + var state = info.State; + var command = GridCommand.Parse(state.Page, state.Size, state.OrderBy, state.GroupBy, state.Filter); + + if (grid.Paging.Enabled) + { + var pathChanged = !info.Path.Equals(grid.ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase); + if (!pathChanged) + { + if (command.PageSize > 0) + grid.Paging.PageSize = command.PageSize; + if (command.Page > 0) + grid.Paging.CurrentPage = command.Page; + } + } + + if (grid.Sorting.Enabled) + { + foreach (var sort in command.SortDescriptors) + { + var existingSort = grid.Sorting.OrderBy.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(sort.Member)); + if (existingSort != null) + { + grid.Sorting.OrderBy.Remove(existingSort); + } + grid.Sorting.OrderBy.Add(sort); + } + } + + if (grid.Grouping.Enabled) + { + foreach (var group in command.GroupDescriptors) + { + var existingGroup = grid.Grouping.Groups.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(group.Member)); + if (existingGroup != null) + { + grid.Grouping.Groups.Remove(existingGroup); + } + grid.Grouping.Groups.Add(group); + } + } + + if (grid.Filtering.Enabled) + { + foreach (var filter in command.FilterDescriptors) + { + var compositeFilter = filter as CompositeFilterDescriptor; + if (compositeFilter == null) + { + compositeFilter = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And }; + compositeFilter.FilterDescriptors.Add(filter); + } + grid.Filtering.Filters.Add(compositeFilter); + } + } + + // persist again for the next request + grid.ViewContext.TempData[gridId] = info; + + return builder; + } + + public static IEnumerable ForCommand(this IEnumerable current, GridCommand command) + { + var queryable = current.AsQueryable() as IQueryable; + if (command.FilterDescriptors.Any()) + { + queryable = queryable.Where(command.FilterDescriptors.AsEnumerable()).AsQueryable() as IQueryable; + } + + IList temporarySortDescriptors = new List(); + + if (!command.SortDescriptors.Any() && queryable.Provider.IsEntityFrameworkProvider()) + { + // The Entity Framework provider demands OrderBy before calling Skip. + SortDescriptor sortDescriptor = new SortDescriptor + { + Member = GetFirstSortableProperty(queryable.ElementType) + }; + command.SortDescriptors.Add(sortDescriptor); + temporarySortDescriptors.Add(sortDescriptor); + } + + if (command.GroupDescriptors.Any()) + { + command.GroupDescriptors.Reverse().Each(groupDescriptor => + { + SortDescriptor sortDescriptor = new SortDescriptor + { + Member = groupDescriptor.Member, + SortDirection = groupDescriptor.SortDirection + }; + + command.SortDescriptors.Insert(0, sortDescriptor); + temporarySortDescriptors.Add(sortDescriptor); + }); + } + + if (command.SortDescriptors.Any()) + { + queryable = queryable.Sort(command.SortDescriptors); + } + + return queryable as IQueryable; + } + + public static IEnumerable PagedForCommand(this IEnumerable current, GridCommand command) + { + return current.Skip((command.Page - 1) * command.PageSize).Take(command.PageSize); + } + + + public static GridBoundColumnBuilder Centered(this GridBoundColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); + } + + public static GridTemplateColumnBuilder Centered(this GridTemplateColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); + } + + public static GridBoundColumnBuilder RightAlign(this GridBoundColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); + } + + public static GridTemplateColumnBuilder RightAlign(this GridTemplateColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); + } + + public static string GetValueFromAppliedFilter(this IFilterDescriptor filter, string valueName, FilterOperator? filterOperator = null) + { + if (filter is CompositeFilterDescriptor) + { + foreach (IFilterDescriptor childFilter in ((CompositeFilterDescriptor)filter).FilterDescriptors) + { + var val1 = GetValueFromAppliedFilter(childFilter, valueName, filterOperator); + if (!String.IsNullOrEmpty(val1)) + return val1; + } + } + else + { + var filterDescriptor = (FilterDescriptor)filter; + if (filterDescriptor != null && + filterDescriptor.Member.Equals(valueName, StringComparison.InvariantCultureIgnoreCase)) + { + if (!filterOperator.HasValue || filterDescriptor.Operator == filterOperator.Value) + return Convert.ToString(filterDescriptor.Value); + } + } + + return ""; + } + + public static string GetValueFromAppliedFilters(this IList filters, string valueName, FilterOperator? filterOperator = null) + { + foreach (var filter in filters) + { + var val1 = GetValueFromAppliedFilter(filter, valueName, filterOperator); + if (!String.IsNullOrEmpty(val1)) + return val1; + } + return ""; + } + + private static string GetFirstSortableProperty(Type type) + { + PropertyInfo firstSortableProperty = type.GetProperties().Where(property => property.PropertyType.IsPredefinedSimpleType()).FirstOrDefault(); + + if (firstSortableProperty == null) + { + throw new NotSupportedException("Cannot find property to sort by."); + } + + return firstSortableProperty.Name; + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs new file mode 100644 index 0000000000..0925a59f65 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs @@ -0,0 +1,32 @@ +using System; +using SmartStore.Core.Plugins; +using SmartStore.Services.Configuration; + +namespace SmartStore.Web.Framework +{ + public static class PluginExtensions + { + /// + /// Determines whether a plugin is installed and activated for a particular store. + /// + public static bool IsPluginReady(this IPluginFinder pluginFinder, ISettingService settingService, string systemName, int storeId) + { + try + { + var pluginDescriptor = pluginFinder.GetPluginDescriptorBySystemName(systemName); + + if (pluginDescriptor != null && pluginDescriptor.Installed) + { + if (storeId == 0 || settingService.GetSettingByKey(pluginDescriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true)) + return true; + } + } + catch (Exception exc) + { + exc.Dump(); + } + + return false; + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index 1c59563458..2ae06313d2 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -232,12 +232,14 @@ + + @@ -248,6 +250,7 @@ + @@ -308,7 +311,7 @@ - + From ef722954594c39f1f9aa7c99ea8515ca7607ee4c Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 12 Nov 2015 20:52:29 +0100 Subject: [PATCH 078/732] Export: Added RowExportingEvent --- .../DataExchange/DataExporter.cs | 15 +++--- .../DataExchange/DynamicEntityHelper.cs | 54 +++++++++++++++++-- .../DataExchange/Events/RowExportingEvent.cs | 17 ++++++ .../SmartStore.Services.csproj | 1 + 4 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index b409401da6..b09801ff15 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -1074,21 +1074,22 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc string prefix = null; string suffix = null; var extension = Path.GetExtension(ctx.Result.Files.First().FileName); + var provider = request.Provider.Value; - if (request.Provider.Value.EntityType == ExportEntityType.Product) + if (provider.EntityType == ExportEntityType.Product) prefix = T("Admin.Catalog.Products"); - else if (request.Provider.Value.EntityType == ExportEntityType.Order) + else if (provider.EntityType == ExportEntityType.Order) prefix = T("Admin.Orders"); - else if (request.Provider.Value.EntityType == ExportEntityType.Category) + else if (provider.EntityType == ExportEntityType.Category) prefix = T("Admin.Catalog.Categories"); - else if (request.Provider.Value.EntityType == ExportEntityType.Manufacturer) + else if (provider.EntityType == ExportEntityType.Manufacturer) prefix = T("Admin.Catalog.Manufacturers"); - else if (request.Provider.Value.EntityType == ExportEntityType.Customer) + else if (provider.EntityType == ExportEntityType.Customer) prefix = T("Admin.Customers"); - else if (request.Provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) + else if (provider.EntityType == ExportEntityType.NewsLetterSubscription) prefix = T("Admin.Promotions.NewsLetterSubscriptions"); else - prefix = request.Provider.Value.EntityType.ToString(); + prefix = provider.EntityType.ToString(); var selectedEntityCount = (request.EntitiesToExport == null ? 0 : request.EntitiesToExport.Count); diff --git a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs index 73cb0ae431..7faca463f9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs @@ -21,6 +21,7 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Html; using SmartStore.Services.Catalog; +using SmartStore.Services.DataExchange.Events; using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Seo; @@ -973,6 +974,14 @@ private List Convert(DataExporterContext ctx, Product product) result.Add(dynObject); } + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Product, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } @@ -1042,6 +1051,14 @@ private List Convert(DataExporterContext ctx, Order order) result.Add(dynObject); + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Order, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } @@ -1073,13 +1090,21 @@ private List Convert(DataExporterContext ctx, Manufacturer manufacturer result.Add(dynObject); + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Manufacturer, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } private List Convert(DataExporterContext ctx, Category category) { var result = new List(); - + var productCategories = ctx.CategoryExportContext.ProductCategories.Load(category.Id); dynamic dynObject = ToDynamic(ctx, category); @@ -1104,6 +1129,14 @@ private List Convert(DataExporterContext ctx, Category category) result.Add(dynObject); + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Category, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } @@ -1140,16 +1173,31 @@ private List Convert(DataExporterContext ctx, Customer customer) result.Add(dynObject); + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Customer, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } private List Convert(DataExporterContext ctx, NewsLetterSubscription subscription) { var result = new List(); - dynamic dynObject = ToDynamic(ctx, subscription); - result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.NewsLetterSubscription, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + return result; } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs b/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs new file mode 100644 index 0000000000..31922ae3b9 --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SmartStore.Core.Domain.DataExchange; + +namespace SmartStore.Services.DataExchange.Events +{ + public class RowExportingEvent + { + public dynamic Row { get; internal set; } + public ExportEntityType EntityType { get; internal set; } + public DataExportRequest ExportRequest { get; internal set; } + public IExportExecuteContext ExecuteContext { get; internal set; } + } +} diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index ec4af99cbd..0c121d3896 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -166,6 +166,7 @@ + From c468baf706b86c3965f424f16c140f3b97ddb438 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 12 Nov 2015 21:06:22 +0100 Subject: [PATCH 079/732] Export: some experimenting with RowExportingEvent --- .../DataExchange/Events/RowExportingEvent.cs | 4 +++ .../SmartStore.GoogleMerchantCenter/Events.cs | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs b/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs index 31922ae3b9..0b1dbcc017 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Events/RowExportingEvent.cs @@ -7,6 +7,10 @@ namespace SmartStore.Services.DataExchange.Events { + // TODO: Another event message must be implemented, say 'ColumnsBuildingEvent' + // The consumer of this event (most likely a plugin) could push a list of specific column headers + // into global the export definition. + public class RowExportingEvent { public dynamic Row { get; internal set; } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs index 5ca8aeb5dc..4d0019134f 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs @@ -5,12 +5,16 @@ using SmartStore.GoogleMerchantCenter.Services; using SmartStore.Web.Framework.Events; using SmartStore.Web.Framework.Mvc; +using SmartStore.Services.DataExchange.Events; +using SmartStore.Core.Domain.DataExchange; +using SmartStore.Core.Domain.Catalog; namespace SmartStore.GoogleMerchantCenter { public class Events : IConsumer, - IConsumer + IConsumer/*, + IConsumer*/ { private readonly IGoogleFeedService _googleService; @@ -18,7 +22,7 @@ public Events(IGoogleFeedService googleService) { this._googleService = googleService; } - + public void HandleEvent(TabStripCreated eventMessage) { if (eventMessage.TabStripName == "product-edit") @@ -33,6 +37,30 @@ public void HandleEvent(TabStripCreated eventMessage) } } + //public void HandleEvent(RowExportingEvent eventMessage) + //{ + // if (eventMessage.EntityType != ExportEntityType.Product) + // return; + + // var row = eventMessage.Row; + // var product = eventMessage.Row.Entity as Product; + + // if (product == null) + // return; + + // var gmc = _googleService.GetGoogleProductRecord(product.Id); + // if (gmc == null) + // return; + + // row["_GMC_AgeGroup"] = gmc.AgeGroup; + // row["_GMC_Color"] = gmc.Color; + // row["_GMC_Gender"] = gmc.Gender; + // row["_GMC_Size"] = gmc.Size; + // row["_GMC_Taxonomy"] = gmc.Taxonomy; + // row["_GMC_Material"] = gmc.Material; + // row["_GMC_Pattern"] = gmc.Pattern; + //} + public void HandleEvent(ModelBoundEvent eventMessage) { if (!eventMessage.BoundModel.CustomProperties.ContainsKey("GMC")) From 5f74aecb89db44a7eee2d3884b1838d35d691c2c Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 13 Nov 2015 12:46:05 +0100 Subject: [PATCH 080/732] Export preview for all supported entity types --- .../201511121139009_ExportFramework3.cs | 1 + .../DataExchange/DataExporter.cs | 11 +- .../DataExchange/DynamicEntityHelper.cs | 21 ++ .../Controllers/ExportController.cs | 138 +++++++-- .../Models/DataExchange/ExportPreviewModel.cs | 78 +++++ .../Administration/Views/Category/List.cshtml | 9 +- .../Views/Export/Preview.cshtml | 286 +++++++++++++----- .../Views/Manufacturer/List.cshtml | 58 ++-- 8 files changed, 468 insertions(+), 134 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs index 34a97d19bc..132d36fee6 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs @@ -86,6 +86,7 @@ public void Seed(SmartObjectContext context) }; profile = profiles.Add(profile); + context.SaveChanges(); task.Alias = profile.Id.ToString(); diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index b09801ff15..d839d3d839 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -540,6 +540,9 @@ private IQueryable GetManufacturerQuery(DataExporterContext ctx, i var query = _manufacturerService.Value.GetManufacturers(showHidden, storeId); + if (ctx.Request.EntitiesToExport.Count > 0) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); + query = query.OrderBy(x => x.DisplayOrder); if (skip > 0) @@ -573,6 +576,9 @@ private IQueryable GetCategoryQuery(DataExporterContext ctx, int skip, var query = _categoryService.Value.GetCategories(null, showHidden, null, true, storeId); + if (ctx.Request.EntitiesToExport.Count > 0) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); + query = query .OrderBy(x => x.ParentCategoryId) .ThenBy(x => x.DisplayOrder); @@ -647,9 +653,10 @@ private IQueryable GetNewsLetterSubscriptionQuery(DataEx var query = _subscriptionRepository.Value.TableUntracked; if (storeId > 0) - { query = query.Where(x => x.StoreId == storeId); - } + + if (ctx.Request.EntitiesToExport.Count > 0) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); query = query .OrderBy(x => x.StoreId) diff --git a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs index 7faca463f9..cb964a59f8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs @@ -392,6 +392,8 @@ private dynamic ToDynamic(DataExporterContext ctx, Customer customer) result._GenericAttributes = null; result._HasNewsletterSubscription = false; + result._FullName = null; + return result; } @@ -1171,6 +1173,24 @@ private List Convert(DataExporterContext ctx, Customer customer) dynObject._HasNewsletterSubscription = ctx.NewsletterSubscriptions.Contains(customer.Email, StringComparer.CurrentCultureIgnoreCase); + var attrFirstName = genericAttributes.FirstOrDefault(x => x.Key == SystemCustomerAttributeNames.FirstName); + var attrLastName = genericAttributes.FirstOrDefault(x => x.Key == SystemCustomerAttributeNames.LastName); + + string firstName = (attrFirstName == null ? "" : attrFirstName.Value); + string lastName = (attrLastName == null ? "" : attrLastName.Value); + + if (firstName.IsEmpty() && lastName.IsEmpty()) + { + var address = customer.Addresses.FirstOrDefault(); + if (address != null) + { + firstName = address.FirstName; + lastName = address.LastName; + } + } + + dynObject._FullName = firstName.Grow(lastName, " "); + result.Add(dynObject); _services.EventPublisher.Publish(new RowExportingEvent @@ -1198,6 +1218,7 @@ private List Convert(DataExporterContext ctx, NewsLetterSubscription su ExecuteContext = ctx.ExecuteContext }); + return result; } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index f15c4fe3a0..c0e200ccc8 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -9,10 +9,13 @@ using SmartStore.Core; using SmartStore.Core.Domain; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.DataExchange; +using SmartStore.Core.Domain.Messages; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Domain.Stores; using SmartStore.Core.Plugins; using SmartStore.Services; using SmartStore.Services.Catalog; @@ -50,6 +53,7 @@ public class ExportController : AdminControllerBase private readonly DataExchangeSettings _dataExchangeSettings; private readonly ITaskScheduler _taskScheduler; private readonly IDataExporter _dataExporter; + private readonly Lazy _customerSettings; public ExportController( ICommonServices services, @@ -65,7 +69,8 @@ public ExportController( IDateTimeHelper dateTimeHelper, DataExchangeSettings dataExchangeSettings, ITaskScheduler taskScheduler, - IDataExporter dataExporter) + IDataExporter dataExporter, + Lazy customerSettings) { _services = services; _exportService = exportService; @@ -81,6 +86,7 @@ public ExportController( _dataExchangeSettings = dataExchangeSettings; _taskScheduler = taskScheduler; _dataExporter = dataExporter; + _customerSettings = customerSettings; } #region Utilities @@ -736,7 +742,8 @@ public ActionResult Preview(int id) GridPageSize = DataExporter.PageSize, EntityType = provider.Value.EntityType, TotalRecords = totalRecords, - LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()) + LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()), + UsernamesEnabled = _customerSettings.Value.UsernamesEnabled }; return View(model); @@ -754,8 +761,20 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) { var productModel = new List(); var orderModel = new List(); + var categoryModel = new List(); + var manuModel = new List(); + var customerModel = new List(); + var subscriberModel = new List(); + + object gridData = null; + Dictionary allCategories = null; + IList allStores = null; var request = new DataExportRequest(profile, provider); + var normalizedTotal = totalRecords; + + if (profile.Limit > 0 && normalizedTotal > profile.Limit) + normalizedTotal = profile.Limit; var data = _dataExporter.Preview(request, command.Page - 1, totalRecords); @@ -764,7 +783,8 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) if (provider.Value.EntityType == ExportEntityType.Product) { var product = item.Entity as Product; - var pm = new ExportPreviewProductModel + + productModel.Add(new ExportPreviewProductModel { Id = product.Id, ProductTypeId = product.ProductTypeId, @@ -776,13 +796,11 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) Published = product.Published, StockQuantity = product.StockQuantity, AdminComment = item.AdminComment - }; - - productModel.Add(pm); + }); } else if (provider.Value.EntityType == ExportEntityType.Order) { - var om = new ExportPreviewOrderModel + orderModel.Add(new ExportPreviewOrderModel { Id = item.Id, HasNewPaymentNotification = item.HasNewPaymentNotification, @@ -794,33 +812,105 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) StoreName = (item.Store == null ? "".NaIfEmpty() : item.Store.Name), CreatedOn = _dateTimeHelper.ConvertToUserTime(item.CreatedOnUtc, DateTimeKind.Utc), OrderTotal = item.OrderTotal - }; + }); + } + else if (provider.Value.EntityType == ExportEntityType.Category) + { + var category = item.Entity as Category; + + if (allCategories == null) + { + allCategories = _categoryService.GetAllCategories(showHidden: true, applyNavigationFilters: false) + .ToDictionary(x => x.Id); + } - orderModel.Add(om); + categoryModel.Add(new ExportPreviewCategoryModel + { + Id = category.Id, + Breadcrumb = category.GetCategoryBreadCrumb(_categoryService, allCategories), + FullName = item.FullName, + Alias = item.Alias, + Published = category.Published, + DisplayOrder = category.DisplayOrder, + LimitedToStores = category.LimitedToStores + }); + } + else if (provider.Value.EntityType == ExportEntityType.Manufacturer) + { + manuModel.Add(new ExportPreviewManufacturerModel + { + Id = item.Id, + Name = item.Name, + Published = item.Published, + DisplayOrder = item.DisplayOrder, + LimitedToStores = item.LimitedToStores + }); } - } + else if (provider.Value.EntityType == ExportEntityType.Customer) + { + var customer = item.Entity as Customer; + var customerRoles = item.CustomerRoles as List; + var customerRolesString = string.Join(", ", customerRoles.Select(x => x.Name)); - var normalizedTotal = totalRecords; + customerModel.Add(new ExportPreviewCustomerModel + { + Id = customer.Id, + Active = customer.Active, + CreatedOn = _dateTimeHelper.ConvertToUserTime(customer.CreatedOnUtc, DateTimeKind.Utc), + CustomerRoleNames = customerRolesString, + Email = customer.Email, + FullName = item._FullName, + LastActivityDate = _dateTimeHelper.ConvertToUserTime(customer.LastActivityDateUtc, DateTimeKind.Utc), + Username = customer.Username + }); + } + else if (provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) + { + var subscription = item.Entity as NewsLetterSubscription; - if (profile.Limit > 0 && normalizedTotal > profile.Limit) - normalizedTotal = profile.Limit; + if (allStores == null) + allStores = _services.StoreService.GetAllStores(); + + var store = allStores.FirstOrDefault(x => x.Id == subscription.StoreId); + + subscriberModel.Add(new ExportPreviewNewsLetterSubscriptionModel + { + Id = subscription.Id, + Active = subscription.Active, + CreatedOn = _dateTimeHelper.ConvertToUserTime(subscription.CreatedOnUtc, DateTimeKind.Utc), + Email = subscription.Email, + StoreName = (store == null ? "".NaIfEmpty() : store.Name) + }); + } + } if (provider.Value.EntityType == ExportEntityType.Product) { - return new JsonResult - { - Data = new GridModel { Data = productModel, Total = normalizedTotal } - }; + gridData = new GridModel { Data = productModel, Total = normalizedTotal }; } - - if (provider.Value.EntityType == ExportEntityType.Order) + else if (provider.Value.EntityType == ExportEntityType.Order) { - return new JsonResult - { - Data = new GridModel { Data = orderModel, Total = normalizedTotal } - }; + gridData = new GridModel { Data = orderModel, Total = normalizedTotal }; } - } + else if (provider.Value.EntityType == ExportEntityType.Category) + { + gridData = new GridModel { Data = categoryModel, Total = normalizedTotal }; + } + else if (provider.Value.EntityType == ExportEntityType.Manufacturer) + { + gridData = new GridModel { Data = manuModel, Total = normalizedTotal }; + } + else if (provider.Value.EntityType == ExportEntityType.Customer) + { + gridData = new GridModel { Data = customerModel, Total = normalizedTotal }; + } + else if (provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) + { + gridData = new GridModel { Data = subscriberModel, Total = normalizedTotal }; + } + + return new JsonResult { Data = gridData }; + } return new EmptyResult(); } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs index 4dbcde6c6a..eff05703f6 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs @@ -14,6 +14,7 @@ public class ExportPreviewModel : EntityModelBase public int TotalRecords { get; set; } public ExportEntityType EntityType { get; set; } public bool LogFileExists { get; set; } + public bool UsernamesEnabled { get; set; } } public class ExportPreviewProductModel : EntityModelBase @@ -71,4 +72,81 @@ public class ExportPreviewOrderModel : EntityModelBase [SmartResourceDisplayName("Admin.Orders.Fields.OrderTotal")] public decimal OrderTotal { get; set; } } + + public class ExportPreviewCategoryModel : EntityModelBase + { + [SmartResourceDisplayName("Admin.Catalog.Products.Categories.Fields.Category")] + public string Breadcrumb { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Categories.Fields.FullName")] + public string FullName { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Categories.Fields.Alias")] + public string Alias { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Categories.Fields.Published")] + public bool Published { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Categories.Fields.DisplayOrder")] + public int DisplayOrder { get; set; } + + [SmartResourceDisplayName("Admin.Common.Store.LimitedTo")] + public bool LimitedToStores { get; set; } + } + + public class ExportPreviewManufacturerModel : EntityModelBase + { + [SmartResourceDisplayName("Admin.Catalog.Manufacturers.Fields.Name")] + public string Name { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Manufacturers.Fields.Published")] + public bool Published { get; set; } + + [SmartResourceDisplayName("Admin.Catalog.Manufacturers.Fields.DisplayOrder")] + public int DisplayOrder { get; set; } + + [SmartResourceDisplayName("Admin.Common.Store.LimitedTo")] + public bool LimitedToStores { get; set; } + } + + public class ExportPreviewCustomerModel : EntityModelBase + { + public bool UsernamesEnabled { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.Username")] + public string Username { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.FullName")] + public string FullName { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.Email")] + public string Email { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.CustomerRoles")] + public string CustomerRoleNames { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.Active")] + public bool Active { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.CreatedOn")] + public DateTime CreatedOn { get; set; } + + [SmartResourceDisplayName("Admin.Customers.Customers.Fields.LastActivityDate")] + public DateTime LastActivityDate { get; set; } + } + + public class ExportPreviewNewsLetterSubscriptionModel : EntityModelBase + { + [SmartResourceDisplayName("Admin.Promotions.NewsLetterSubscriptions.Fields.Email")] + public string Email { get; set; } + + [SmartResourceDisplayName("Admin.Promotions.NewsLetterSubscriptions.Fields.Active")] + public bool Active { get; set; } + + [SmartResourceDisplayName("Admin.Promotions.NewsLetterSubscriptions.Fields.CreatedOn")] + public DateTime CreatedOn { get; set; } + + [SmartResourceDisplayName("Admin.Common.Store")] + public string StoreName { get; set; } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml index 9a3b2abd9f..08d7f81a83 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/List.cshtml @@ -65,13 +65,18 @@ columns.Bound(x => x.FullName); columns.Bound(x => x.Alias); columns.Bound(x => x.Published) - .Width(100) + .Width(160) .Template(item => @Html.SymbolForBool(item.Published)) .ClientTemplate(@Html.SymbolForBool("Published")) .Centered(); columns.Bound(x => x.DisplayOrder) - .Width(100) + .Width(160) .Centered(); + columns.Bound(x => x.LimitedToStores) + .Width(160) + .Template(item => @Html.SymbolForBool(item.LimitedToStores)) + .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Centered(); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Category")) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml index 2bca030bc9..9cd9abb59e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml @@ -100,81 +100,215 @@ } -@if (Model.EntityType == ExportEntityType.Product) -{ -
+
+ @if (Model.EntityType == ExportEntityType.Product) + { @(Html.Telerik().Grid() - .Name("export-grid-product") - .DataKeys(keys => keys.Add(x => x.Id) - .RouteKey("Id")) - .Columns(columns => - { - columns.Bound(x => x.Id) - .ClientTemplate("") - .Title("") - .Width(50) - .HtmlAttributes(new { style = "text-align:center" }) - .HeaderHtmlAttributes(new { style = "text-align:center" }); - columns.Bound(x => x.Id) - .Width(80) - .Centered(); - columns.Bound(x => x.Name) - .ClientTemplate(@Html.LabeledProductName("Id", "Name")); - columns.Bound(x => x.Sku); - columns.Bound(x => x.Price) - .Format("{0:0.00}") - .RightAlign(); - columns.Bound(x => x.StockQuantity) - .Centered(); - columns.Bound(x => x.Published) - .Width(100) - .ClientTemplate(@Html.SymbolForBool("Published")) - .Centered(); - columns.Bound(x => x.AdminComment); - }) - .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) - .ClientEvents(events => events.OnDataBound("OnDataBound")) - .EnableCustomBinding(true)) -
-} -else if (Model.EntityType == ExportEntityType.Order) -{ -
+ .Name("export-grid-product") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.Name) + .ClientTemplate(@Html.LabeledProductName("Id", "Name")); + columns.Bound(x => x.Sku); + columns.Bound(x => x.Price) + .Format("{0:0.00}") + .RightAlign(); + columns.Bound(x => x.StockQuantity) + .Centered(); + columns.Bound(x => x.Published) + .Width(160) + .ClientTemplate(@Html.SymbolForBool("Published")) + .Centered(); + columns.Bound(x => x.AdminComment); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else if (Model.EntityType == ExportEntityType.Order) + { @(Html.Telerik().Grid() - .Name("export-grid-order") - .DataKeys(keys => keys.Add(x => x.Id) - .RouteKey("Id")) - .Columns(columns => - { - columns.Bound(x => x.Id) - .ClientTemplate("") - .Title("") - .Width(50) - .HtmlAttributes(new { style = "text-align:center" }) - .HeaderHtmlAttributes(new { style = "text-align:center" }); - columns.Bound(x => x.Id) - .Width(80) - .Centered(); - columns.Bound(x => x.OrderNumber) - .ClientTemplate(@Html.LabeledOrderNumber()); - columns.Bound(x => x.OrderStatus); - columns.Bound(x => x.PaymentStatus); - columns.Bound(x => x.ShippingStatus); - columns.Bound(x => x.CustomerEmail); - columns.Bound(x => x.StoreName); - columns.Bound(x => x.CreatedOn); - columns.Bound(x => x.OrderTotal) - .Format("{0:0.00}") - .RightAlign(); - }) - .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) - .ClientEvents(events => events.OnDataBound("OnDataBound")) - .EnableCustomBinding(true)) -
-} -else -{ -
@T("Admin.DataExchange.Export.NoPreview")
-} \ No newline at end of file + .Name("export-grid-order") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.OrderNumber) + .ClientTemplate(@Html.LabeledOrderNumber()); + columns.Bound(x => x.OrderStatus); + columns.Bound(x => x.PaymentStatus); + columns.Bound(x => x.ShippingStatus); + columns.Bound(x => x.CustomerEmail); + columns.Bound(x => x.StoreName); + columns.Bound(x => x.CreatedOn); + columns.Bound(x => x.OrderTotal) + .Format("{0:0.00}") + .RightAlign(); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else if (Model.EntityType == ExportEntityType.Category) + { + @(Html.Telerik().Grid() + .Name("export-grid-order") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.Breadcrumb); + columns.Bound(x => x.FullName); + columns.Bound(x => x.Alias); + columns.Bound(x => x.Published) + .Width(160) + .ClientTemplate(@Html.SymbolForBool("Published")) + .Centered(); + columns.Bound(x => x.DisplayOrder) + .Width(160) + .Centered(); + columns.Bound(x => x.LimitedToStores) + .Width(160) + .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Centered(); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else if (Model.EntityType == ExportEntityType.Manufacturer) + { + @(Html.Telerik().Grid() + .Name("export-grid-order") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.Name); + columns.Bound(x => x.Published) + .Width(160) + .ClientTemplate(@Html.SymbolForBool("Published")) + .Centered(); + columns.Bound(x => x.DisplayOrder) + .Width(160) + .Centered(); + columns.Bound(x => x.LimitedToStores) + .Width(160) + .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Centered(); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else if (Model.EntityType == ExportEntityType.Customer) + { + @(Html.Telerik().Grid() + .Name("export-grid-order") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.Email); + columns.Bound(x => x.Username) + .Visible(Model.UsernamesEnabled); + columns.Bound(x => x.FullName); + columns.Bound(x => x.CustomerRoleNames); + columns.Bound(x => x.Active) + .ClientTemplate(@Html.SymbolForBool("Active")) + .Width(160) + .Centered(); + columns.Bound(x => x.CreatedOn); + columns.Bound(x => x.LastActivityDate); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else if (Model.EntityType == ExportEntityType.NewsLetterSubscription) + { + @(Html.Telerik().Grid() + .Name("export-grid-order") + .DataKeys(keys => keys.Add(x => x.Id) + .RouteKey("Id")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); + columns.Bound(x => x.Id) + .Width(80) + .Centered(); + columns.Bound(x => x.Email); + columns.Bound(x => x.Active) + .ClientTemplate(@Html.SymbolForBool("Active")) + .Centered() + .Width(160); + columns.Bound(x => x.StoreName); + columns.Bound(x => x.CreatedOn); + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) + .ClientEvents(events => events.OnDataBound("OnDataBound")) + .EnableCustomBinding(true)) + } + else + { +
@T("Admin.DataExchange.Export.NoPreview")
+ } +
\ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml index 87310a67ee..be3c64facb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml @@ -40,45 +40,43 @@
+

+
@(Html.Telerik().Grid(Model.Manufacturers.Data) - .Name("manufacturers-grid") - .ClientEvents(events => events - .OnDataBinding("onDataBinding")) - .Columns(columns => - { - columns.Bound(x => x.Name).Width(300) - .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) - .ClientTemplate("\"><#= Name #>"); - - // .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) - //.ClientTemplate("'><#= Name #>"); - - columns.Bound(x => x.Published) - .Width(100) - .Template(item => @Html.SymbolForBool(item.Published)) - .ClientTemplate(@Html.SymbolForBool("Published")) - .Centered(); - columns.Bound(x => x.DisplayOrder) - .Width(100) - .Centered(); - columns.Bound(x => x.Id) - .Width(50) - .Centered() - .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id })) - .ClientTemplate("\">" + T("Admin.Common.Edit").Text + "") - .Title(T("Admin.Common.Edit").Text); - }) - .Pageable(settings => settings.Total(Model.Manufacturers.Total).PageSize(gridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Manufacturer")) + .Name("manufacturers-grid") + .ClientEvents(events => events + .OnDataBinding("onDataBinding")) + .Columns(columns => + { + columns.Bound(x => x.Name) + .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) + .ClientTemplate("\"><#= Name #>"); + columns.Bound(x => x.Published) + .Width(160) + .Template(item => @Html.SymbolForBool(item.Published)) + .ClientTemplate(@Html.SymbolForBool("Published")) + .Centered(); + columns.Bound(x => x.DisplayOrder) + .Width(160) + .Centered(); + columns.Bound(x => x.LimitedToStores) + .Width(160) + .Template(item => @Html.SymbolForBool(item.LimitedToStores)) + .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Centered(); + }) + .Pageable(settings => settings.Total(Model.Manufacturers.Total).PageSize(gridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Manufacturer")) .PreserveGridState() - .EnableCustomBinding(true)) + .EnableCustomBinding(true))
+ From afce7e6f3eac3cf6ae6b47a0cb774333be42f0be Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 10:42:22 +0100 Subject: [PATCH 090/732] Minor changes --- .../Data/IDbContextExtensions.cs | 28 --------- .../SmartStore.Data/ObjectContextBase.cs | 60 +++++++++++-------- .../DataExchange/DataExporter.cs | 45 +++++++++----- .../Deployment/EmailFilePublisher.cs | 14 +++-- .../Deployment/FileSystemFilePublisher.cs | 9 --- .../Deployment/FtpFilePublisher.cs | 9 --- .../Deployment/HttpFilePublisher.cs | 8 --- .../DataExchange/Deployment/IFilePublisher.cs | 3 - .../DataExchange/DynamicEntityHelper.cs | 25 +++----- .../Internal/DataExporterContext.cs | 10 +--- 10 files changed, 79 insertions(+), 132 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs index 0bc3d4dc7f..0889cd6684 100644 --- a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs +++ b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs @@ -28,34 +28,6 @@ public static void DetachEntities(this IDbContext ctx, IEnumerable ctx.DetachEntity(x)); } - public static void Attach(this IDbContext ctx, TEntity entity) where TEntity : BaseEntity - { - var dbSet = ctx.Set(); - var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); - - if (alreadyAttached == null) - { - dbSet.Attach(entity); - } - } - - public static void Attach(this IDbContext ctx, IEnumerable entities) where TEntity : BaseEntity - { - Guard.ArgumentNotNull(() => ctx); - - var dbSet = ctx.Set(); - - entities.Each(entity => - { - var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); - - if (alreadyAttached == null) - { - dbSet.Attach(entity); - } - }); - } - /// /// Changes the object state to unchanged /// diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.cs index 5f722e155f..278d88d984 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.cs @@ -2,27 +2,26 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Data.SqlClient; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.Validation; +using System.Data.SqlClient; using System.Linq; -using System.Linq.Expressions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Smo; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; -using Microsoft.SqlServer.Management.Common; -using Microsoft.SqlServer.Management.Smo; using SmartStore.Core.Events; namespace SmartStore.Data { - /// - /// Object context - /// + /// + /// Object context + /// [DbConfigurationType(typeof(SmartDbConfiguration))] public abstract class ObjectContextBase : DbContext, IDbContext { @@ -496,32 +495,41 @@ protected internal bool IsSqlServer2012OrHigher() return s_isSqlServer2012OrHigher.Value; } - /// - /// Attach an entity to the context or return an already attached entity (if it was already attached) - /// - /// TEntity - /// Entity - /// Attached entity - protected virtual TEntity AttachEntity(TEntity entity) where TEntity : BaseEntity, new() - { - // little hack here until Entity Framework really supports stored procedures - // otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context - var dbSet = Set(); - var alreadyAttached = dbSet.Local.Where(x => x.Id == entity.Id).FirstOrDefault(); + private TEntity Attach(DbSet dbSet, TEntity entity) where TEntity : BaseEntity + { + var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); + if (alreadyAttached == null) { - // attach new entity dbSet.Attach(entity); return entity; } - else - { - // entity is already loaded. - return alreadyAttached; - } + + return alreadyAttached; + } + + /// + /// Attach an entity to the context or return an already attached entity (if it was already attached) + /// + /// TEntity + /// Entity + /// Attached entity + public TEntity AttachEntity(TEntity entity) where TEntity : BaseEntity + { + // little hack here until Entity Framework really supports stored procedures + // otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context + + return Attach(Set(), entity); } - public bool IsAttached(TEntity entity) where TEntity : BaseEntity + public void AttachEntities(IEnumerable entities) where TEntity : BaseEntity + { + var dbSet = Set(); + + entities.Each(entity => Attach(dbSet, entity)); + } + + public bool IsAttached(TEntity entity) where TEntity : BaseEntity { if (entity != null) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index 3ccd178409..1fb126c0de 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -19,6 +19,7 @@ using SmartStore.Core.Infrastructure; using SmartStore.Core.Localization; using SmartStore.Core.Logging; +using SmartStore.Data; using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; @@ -69,10 +70,10 @@ public partial class DataExporter : IDataExporter private readonly Lazy _shipmentService; private readonly Lazy _genericAttributeService; private readonly Lazy _emailAccountService; + private readonly Lazy _queuedEmailService; private readonly Lazy _emailSender; private readonly Lazy _deliveryTimeService; private readonly Lazy _quantityUnitService; - private readonly Lazy _typeFinder; private readonly Lazy>_customerRepository; private readonly Lazy> _subscriptionRepository; @@ -107,10 +108,10 @@ public DataExporter( Lazy shipmentService, Lazy genericAttributeService, Lazy emailAccountService, - Lazy emailSender, + Lazy queuedEmailService, + Lazy emailSender, Lazy deliveryTimeService, Lazy quantityUnitService, - Lazy typeFinder, Lazy> customerRepository, Lazy> subscriptionRepository, Lazy> orderRepository, @@ -142,10 +143,10 @@ public DataExporter( _shipmentService = shipmentService; _genericAttributeService = genericAttributeService; _emailAccountService = emailAccountService; + _queuedEmailService = queuedEmailService; _emailSender = emailSender; _deliveryTimeService = deliveryTimeService; _quantityUnitService = quantityUnitService; - _typeFinder = typeFinder; _customerRepository = customerRepository; _subscriptionRepository = subscriptionRepository; @@ -210,10 +211,11 @@ private void DetachAllEntitiesAndClear(DataExporterContext ctx) try { // now again attach what is globally required + var dataContextBase = _dbContext as ObjectContextBase; - _dbContext.Attach(ctx.Request.Profile); + dataContextBase.AttachEntity(ctx.Request.Profile); - _dbContext.Attach(ctx.Stores.Values); + dataContextBase.AttachEntities(ctx.Stores.Values); } catch (Exception exception) { @@ -421,10 +423,6 @@ private void Deploy(DataExporterContext ctx) var containerManager = EngineContext.Current.ContainerManager; var allFiles = System.IO.Directory.GetFiles(ctx.FolderContent, "*.*", SearchOption.AllDirectories); - var publishers = _typeFinder.Value.FindClassesOfType(ignoreInactivePlugins: true) - .Select(x => containerManager.ResolveUnregistered(x) as IFilePublisher) - .ToList(); - var context = new ExportDeploymentContext { Log = ctx.Log, @@ -439,17 +437,33 @@ private void Deploy(DataExporterContext ctx) else context.DeploymentFiles = allFiles; - foreach (var publisher in publishers.Where(x => x.DeploymentType == deployment.DeploymentType)) + try { - try + if (deployment.DeploymentType == ExportDeploymentType.Email) { + var publisher = new EmailFilePublisher(_emailAccountService.Value, _queuedEmailService.Value); publisher.Publish(context, deployment); } - catch (Exception exception) + else if (deployment.DeploymentType == ExportDeploymentType.FileSystem) { - ctx.Log.Error("Deployment \"{0}\" of type {1} failed: {2}".FormatInvariant( - deployment.Name, deployment.DeploymentType.ToString(), exception.Message), exception); + var publisher = new FileSystemFilePublisher(); + publisher.Publish(context, deployment); + } + else if (deployment.DeploymentType == ExportDeploymentType.Ftp) + { + var publisher = new FtpFilePublisher(); + publisher.Publish(context, deployment); } + else if (deployment.DeploymentType == ExportDeploymentType.Http) + { + var publisher = new HttpFilePublisher(); + publisher.Publish(context, deployment); + } + } + catch (Exception exception) + { + ctx.Log.Error("Deployment \"{0}\" of type {1} failed: {2}".FormatInvariant( + deployment.Name, deployment.DeploymentType.ToString(), exception.Message), exception); } } } @@ -1096,7 +1110,6 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.DeliveryTimes.Clear(); ctx.CategoryPathes.Clear(); ctx.Categories.Clear(); - ctx.PropertiesCache.Clear(); ctx.Request.CustomData.Clear(); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/EmailFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/EmailFilePublisher.cs index bc9903e734..d6ee4b4f2c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Deployment/EmailFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/EmailFilePublisher.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using SmartStore.Core.Domain; -using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Messages; using SmartStore.Core.Email; using SmartStore.Core.Infrastructure; @@ -14,12 +13,15 @@ namespace SmartStore.Services.DataExchange.Deployment { public class EmailFilePublisher : IFilePublisher { - public ExportDeploymentType DeploymentType + private IEmailAccountService _emailAccountService; + private IQueuedEmailService _queuedEmailService; + + public EmailFilePublisher( + IEmailAccountService emailAccountService, + IQueuedEmailService queuedEmailService) { - get - { - return ExportDeploymentType.Email; - } + _emailAccountService = emailAccountService; + _queuedEmailService = queuedEmailService; } public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/FileSystemFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/FileSystemFilePublisher.cs index 392ab82680..2fba5924a0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Deployment/FileSystemFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/FileSystemFilePublisher.cs @@ -1,7 +1,6 @@ using System.IO; using System.Web; using SmartStore.Core.Domain; -using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; using SmartStore.Utilities; @@ -9,14 +8,6 @@ namespace SmartStore.Services.DataExchange.Deployment { public class FileSystemFilePublisher : IFilePublisher { - public ExportDeploymentType DeploymentType - { - get - { - return ExportDeploymentType.FileSystem; - } - } - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) { string folderDestination = null; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/FtpFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/FtpFilePublisher.cs index 4fad3cc146..bba604f38a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Deployment/FtpFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/FtpFilePublisher.cs @@ -3,21 +3,12 @@ using System.Linq; using System.Net; using SmartStore.Core.Domain; -using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; namespace SmartStore.Services.DataExchange.Deployment { public class FtpFilePublisher : IFilePublisher { - public ExportDeploymentType DeploymentType - { - get - { - return ExportDeploymentType.Ftp; - } - } - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) { var bytesRead = 0; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/HttpFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/HttpFilePublisher.cs index fadcf8a8d3..b2ab9001cb 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Deployment/HttpFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/HttpFilePublisher.cs @@ -10,14 +10,6 @@ namespace SmartStore.Services.DataExchange.Deployment { public class HttpFilePublisher : IFilePublisher { - public ExportDeploymentType DeploymentType - { - get - { - return ExportDeploymentType.Http; - } - } - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) { var succeeded = 0; diff --git a/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs index 9f13724efb..36b9d7eb6a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Deployment/IFilePublisher.cs @@ -1,13 +1,10 @@ using SmartStore.Core.Domain; -using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Logging; namespace SmartStore.Services.DataExchange.Deployment { public interface IFilePublisher { - ExportDeploymentType DeploymentType { get; } - void Publish(ExportDeploymentContext context, ExportDeployment deployment); } diff --git a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs index a007c8763e..f88aa7cda1 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -26,6 +25,7 @@ using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Seo; +using SmartStore.Utilities.Reflection; namespace SmartStore.Services.DataExchange { @@ -953,28 +953,17 @@ private List Convert(DataExporterContext ctx, Product product) if (!ctx.IsPreview && ctx.Projection.AttributeCombinationAsProduct && combinations.Where(x => x.IsActive).Count() > 0) { var productType = typeof(Product); - IEnumerable properties = null; var productValues = new Dictionary(); - // fetch all cloneable product properties once per export - if (ctx.PropertiesCache.ContainsKey(productType)) - { - properties = ctx.PropertiesCache[productType]; - } - else - { - properties = productType - .GetProperties() - .Where(x => x.CanRead && x.CanWrite && 0 == x.GetCustomAttributes(typeof(NotMappedAttribute), true).Length); - - ctx.PropertiesCache.Add(productType, properties); - } + var properties = FastProperty.GetProperties(product.GetUnproxiedType()) + .Values + .Where(x => x.Property.CanRead && x.Property.CanWrite); // fetch all property values once per product foreach (var property in properties) { - productValues.Add(property.Name, property.GetValue(product, null)); - } + productValues.Add(property.Name, property.GetValue(product)); + } foreach (var combination in combinations.Where(x => x.IsActive)) { @@ -982,7 +971,7 @@ private List Convert(DataExporterContext ctx, Product product) foreach (var property in properties) { - property.SetValue(productClone, productValues[property.Name], null); + property.SetValue(productClone, productValues[property.Name]); } var dynObject = ToDynamic(ctx, productClone, combinations, combination); diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index 6dbaf059d4..0d44c9843e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using SmartStore.Core; using SmartStore.Core.Domain.Catalog; @@ -41,7 +39,6 @@ public DataExporterContext( RecordsPerStore = new Dictionary(); EntityIdsLoaded = new List(); EntityIdsPerSegment = new List(); - PropertiesCache = new Dictionary>(); Result = new DataExportResult { @@ -69,11 +66,6 @@ public void SetLoadedEntityIds(IEnumerable ids) /// public List EntityIdsPerSegment { get; set; } - /// - /// For faster reflection - /// - public Dictionary> PropertiesCache { get; set; } - public int RecordCount { get; set; } public Dictionary RecordsPerStore { get; set; } public string ProgressInfo { get; set; } From 7883c7c7f607a37f164281ebeeeff2c258fa5384 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 11:43:36 +0100 Subject: [PATCH 091/732] Removed necessity of handling with volatile export profiles --- .../DataExchange/ExportProfileService.cs | 38 +++++-------------- .../DataExchange/IExportProfileService.cs | 7 ---- .../Controllers/ExportController.cs | 2 +- 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs index faa5dcefb5..df8f6b6832 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportProfileService.cs @@ -47,32 +47,6 @@ public ExportProfileService( #region Export profiles - public virtual ExportProfile CreateVolatileProfile(Provider provider) - { - var name = provider.GetName(_localizationService); - var seoName = SeoHelper.GetSeName(name, true, false).Replace("/", "").Replace("-", ""); - - var profile = new ExportProfile - { - Id = 0, - Name = name, - FolderName = seoName.ToValidPath().Truncate(_dataExchangeSettings.MaxFileNameLength), - FileNamePattern = _defaultFileNamePattern, - ProviderSystemName = provider.Metadata.SystemName, - Enabled = true, - SchedulingTaskId = 0, - PerStore = false, - CreateZipArchive = false, - Cleanup = false, - ScheduleTask = null, // volatile schedule task impossible cause of database accesses by core - Deployments = new List() - }; - - // profile.Projection and profile.Filtering should be null here - - return profile; - } - public virtual ExportProfile InsertExportProfile( string providerSystemName, string name, @@ -90,7 +64,6 @@ public virtual ExportProfile InsertExportProfile( ScheduleTask task = null; ExportProfile profile = null; - var seoName = SeoHelper.GetSeName(name, true, false).Replace("/", "").Replace("-", ""); if (cloneProfile == null) { @@ -155,10 +128,19 @@ public virtual ExportProfile InsertExportProfile( profile.IsSystemProfile = isSystemProfile; profile.Name = name; - profile.FolderName = seoName.ToValidPath().Truncate(_dataExchangeSettings.MaxFileNameLength); profile.ProviderSystemName = providerSystemName; profile.SchedulingTaskId = task.Id; + var folderName = providerSystemName + .Replace("Exports.", "") + .Replace("Feeds.", "") + .Replace("/", "") + .Replace("-", ""); + + profile.FolderName = SeoHelper.GetSeName(folderName, true, false) + .ToValidPath() + .Truncate(_dataExchangeSettings.MaxFileNameLength); + _exportProfileRepository.Insert(profile); task.Alias = profile.Id.ToString(); diff --git a/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs index 4e583b38f2..27b353d532 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/IExportProfileService.cs @@ -8,13 +8,6 @@ namespace SmartStore.Services.DataExchange { public interface IExportProfileService { - /// - /// Creates a volatile export profile - /// - /// Export provider - /// New export profile - ExportProfile CreateVolatileProfile(Provider provider); - /// /// Inserts an export profile /// diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index ce5494ab60..5fafb8d0df 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -460,7 +460,7 @@ public ActionResult List() if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) return AccessDeniedView(); - var providers = _exportService.LoadAllExportProviders().ToList(); + var providers = _exportService.LoadAllExportProviders(0, false).ToList(); var profiles = _exportService.GetExportProfiles().ToList(); var model = new List(); From a1428df101e727e4865e29680e1798c4aed5e682 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 12:06:42 +0100 Subject: [PATCH 092/732] Added new permission record "ManageUrlRecords" --- .../Migrations/201511121139009_ExportFramework3.cs | 12 ++++++++++++ .../Security/StandardPermissionProvider.cs | 10 ++++++---- .../Controllers/UrlRecordController.cs | 13 ++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs index b35af29179..093398f968 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs @@ -7,6 +7,8 @@ namespace SmartStore.Data.Migrations using Core.Domain; using System.Linq; using Utilities; + using Core.Domain.Security; + using Core.Domain.Customers; public partial class ExportFramework3 : DbMigration, ILocaleResourcesProvider, IDataSeeder { @@ -31,6 +33,16 @@ public void Seed(SmartObjectContext context) { context.MigrateLocaleResources(MigrateLocaleResources); + var permissionMigrator = new PermissionMigrator(context); + + permissionMigrator.AddPermission(new PermissionRecord + { + Name = "Admin area. Manage Url Records", + SystemName = "ManageUrlRecords", + Category = "Standard" + }, new string[] { SystemCustomerRoleNames.Administrators }); + + var systemProfilesInfos = new List { new SystemProfileInfo { SystemName = "SmartStoreCategoryXml", ProviderSystemName = "Exports.SmartStoreCategoryXml", Name = "Category XML Export" }, diff --git a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs index 7018c51146..4f37e81bb0 100644 --- a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs +++ b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs @@ -50,10 +50,10 @@ public partial class StandardPermissionProvider : IPermissionProvider public static readonly PermissionRecord UploadPictures = new PermissionRecord { Name = "Admin area. Upload Pictures", SystemName = "UploadPictures", Category = "Configuration" }; public static readonly PermissionRecord ManageScheduleTasks = new PermissionRecord { Name = "Admin area. Manage Schedule Tasks", SystemName = "ManageScheduleTasks", Category = "Configuration" }; public static readonly PermissionRecord ManageExports = new PermissionRecord { Name = "Admin area. Manage Exports", SystemName = "ManageExports", Category = "Configuration" }; + public static readonly PermissionRecord ManageUrlRecords = new PermissionRecord { Name = "Admin area. Manage Url Records", SystemName = "ManageUrlRecords", Category = "Standard" }; - - //public store permissions - public static readonly PermissionRecord DisplayPrices = new PermissionRecord { Name = "Public store. Display Prices", SystemName = "DisplayPrices", Category = "PublicStore" }; + //public store permissions + public static readonly PermissionRecord DisplayPrices = new PermissionRecord { Name = "Public store. Display Prices", SystemName = "DisplayPrices", Category = "PublicStore" }; public static readonly PermissionRecord EnableShoppingCart = new PermissionRecord { Name = "Public store. Enable shopping cart", SystemName = "EnableShoppingCart", Category = "PublicStore" }; public static readonly PermissionRecord EnableWishlist = new PermissionRecord { Name = "Public store. Enable wishlist", SystemName = "EnableWishlist", Category = "PublicStore" }; public static readonly PermissionRecord PublicStoreAllowNavigation = new PermissionRecord { Name = "Public store. Allow navigation", SystemName = "PublicStoreAllowNavigation", Category = "PublicStore" }; @@ -105,7 +105,8 @@ public virtual IEnumerable GetPermissions() UploadPictures, ManageScheduleTasks, ManageExports, - DisplayPrices, + ManageUrlRecords, + DisplayPrices, EnableShoppingCart, EnableWishlist, PublicStoreAllowNavigation, @@ -163,6 +164,7 @@ public virtual IEnumerable GetDefaultPermissions() UploadPictures, ManageScheduleTasks, ManageExports, + ManageUrlRecords, DisplayPrices, EnableShoppingCart, EnableWishlist, diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index 1eb07d10a5..358b174f27 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -14,7 +14,6 @@ namespace SmartStore.Admin.Controllers { - // TODO: add new permisssion record "ManageUrlRecords" [AdminAuthorize] public class UrlRecordController : AdminControllerBase { @@ -82,7 +81,7 @@ private void PrepareUrlRecordModel(UrlRecordModel model, UrlRecord urlRecord, bo public ActionResult List(string entityName, int? entityId) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); var model = new UrlRecordListModel @@ -106,7 +105,7 @@ public ActionResult List(string entityName, int? entityId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, UrlRecordListModel model) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); var allLanguages = _languageService.GetAllLanguages(true); @@ -152,7 +151,7 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) public ActionResult Edit(int id) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); var urlRecord = _urlRecordService.GetUrlRecordById(id); @@ -168,7 +167,7 @@ public ActionResult Edit(int id) [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")] public ActionResult Edit(UrlRecordModel model, bool continueEditing) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); var urlRecord = _urlRecordService.GetUrlRecordById(model.Id); @@ -206,7 +205,7 @@ public ActionResult Edit(UrlRecordModel model, bool continueEditing) [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); var urlRecord = _urlRecordService.GetUrlRecordById(id); @@ -231,7 +230,7 @@ public ActionResult DeleteConfirmed(int id) [HttpPost] public ActionResult DeleteSelected(ICollection selectedIds) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageMaintenance)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) return AccessDeniedView(); if (selectedIds != null) From 2d81510e80dff032894a7eaefd4dd1e5ad94f38f Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 12:09:19 +0100 Subject: [PATCH 093/732] Minor change --- .../Migrations/201511121139009_ExportFramework3.cs | 2 +- .../SmartStore.Services/Security/StandardPermissionProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs index 093398f968..b3b39a74de 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs @@ -39,7 +39,7 @@ public void Seed(SmartObjectContext context) { Name = "Admin area. Manage Url Records", SystemName = "ManageUrlRecords", - Category = "Standard" + Category = "Configuration" }, new string[] { SystemCustomerRoleNames.Administrators }); diff --git a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs index 4f37e81bb0..886196b240 100644 --- a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs +++ b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs @@ -50,7 +50,7 @@ public partial class StandardPermissionProvider : IPermissionProvider public static readonly PermissionRecord UploadPictures = new PermissionRecord { Name = "Admin area. Upload Pictures", SystemName = "UploadPictures", Category = "Configuration" }; public static readonly PermissionRecord ManageScheduleTasks = new PermissionRecord { Name = "Admin area. Manage Schedule Tasks", SystemName = "ManageScheduleTasks", Category = "Configuration" }; public static readonly PermissionRecord ManageExports = new PermissionRecord { Name = "Admin area. Manage Exports", SystemName = "ManageExports", Category = "Configuration" }; - public static readonly PermissionRecord ManageUrlRecords = new PermissionRecord { Name = "Admin area. Manage Url Records", SystemName = "ManageUrlRecords", Category = "Standard" }; + public static readonly PermissionRecord ManageUrlRecords = new PermissionRecord { Name = "Admin area. Manage Url Records", SystemName = "ManageUrlRecords", Category = "Configuration" }; //public store permissions public static readonly PermissionRecord DisplayPrices = new PermissionRecord { Name = "Public store. Display Prices", SystemName = "DisplayPrices", Category = "PublicStore" }; From 97eecfbaf4d9b15f15ae217600da356168e20163 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 14:21:28 +0100 Subject: [PATCH 094/732] Export framework should not create empty files --- src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index 1fb126c0de..eb1a0d2306 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -383,7 +383,7 @@ private bool CallProvider(DataExporterContext ctx, string streamId, string metho ctx.Request.Provider.Value.OnExecuted(ctx.ExecuteContext); } - if (ctx.IsFileBasedExport && path.HasValue()) + if (ctx.IsFileBasedExport && path.HasValue() && ctx.ExecuteContext.DataStream.Length > 0) { if (!ctx.ExecuteContext.DataStream.CanSeek) ctx.Log.Warning("Data stream seems to be closed!"); From 96156cf5001dc4cfb19df3fd7b1fc0fdb2a1d088 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 17 Nov 2015 15:31:50 +0100 Subject: [PATCH 095/732] Perf: Statically cache types of payment method filters and shipping method filters --- .../Payments/PaymentService.cs | 27 +++++++++++++++---- .../Shipping/ShippingService.cs | 25 ++++++++++++++--- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs index 6a5587bc72..9280f99bb0 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs @@ -25,10 +25,13 @@ public partial class PaymentService : IPaymentService #region Constants private const string PAYMENTMETHOD_ALL_KEY = "SmartStore.paymentmethod.all"; - + #endregion - #region Fields + #region Fields + + private readonly static object _lock = new object(); + private static IList _paymentMethodFilterTypes = null; private readonly IRepository _paymentMethodRepository; private readonly PaymentSettings _paymentSettings; @@ -722,11 +725,25 @@ public virtual string GetMaskedCreditCardNumber(string creditCardNumber) public virtual IList GetAllPaymentMethodFilters() { - return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + if (_paymentMethodFilterTypes == null) + { + lock (_lock) + { + if (_paymentMethodFilterTypes == null) + { + _paymentMethodFilterTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + .ToList(); + } + } + } + + var paymentMethodFilters = _paymentMethodFilterTypes .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentMethodFilter) .ToList(); + + return paymentMethodFilters; } - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs b/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs index 78d21c628b..6b35961b79 100644 --- a/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/ShippingService.cs @@ -22,9 +22,12 @@ namespace SmartStore.Services.Shipping { public partial class ShippingService : IShippingService { - #region Fields + #region Fields - private readonly IRepository _shippingMethodRepository; + private readonly static object _lock = new object(); + private static IList _shippingMethodFilterTypes = null; + + private readonly IRepository _shippingMethodRepository; private readonly ICacheManager _cacheManager; private readonly ILogger _logger; private readonly IProductAttributeParser _productAttributeParser; @@ -461,10 +464,24 @@ public virtual GetShippingOptionResponse GetShippingOptions(IList GetAllShippingMethodFilters() { - return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + if (_shippingMethodFilterTypes == null) + { + lock (_lock) + { + if (_shippingMethodFilterTypes == null) + { + _shippingMethodFilterTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + .ToList(); + } + } + } + + var shippingMethodFilters = _shippingMethodFilterTypes .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IShippingMethodFilter) .ToList(); - } + + return shippingMethodFilters; + } #endregion From 96a50f743bea96947c3970300284e03fba81c862 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Tue, 17 Nov 2015 18:40:38 +0100 Subject: [PATCH 096/732] Export framework: minor refactoring --- .../SmartStore.Core/Data/IDbContext.cs | 20 ++++++--- .../Data/IDbContextExtensions.cs | 5 +++ .../SmartStore.Data/ObjectContextBase.cs | 41 +++++-------------- .../DataExchange/DataExporter.cs | 24 +++++------ .../DataExchange/DynamicEntityHelper.cs | 23 ++++++++--- 5 files changed, 59 insertions(+), 54 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Data/IDbContext.cs b/src/Libraries/SmartStore.Core/Data/IDbContext.cs index cec8a1a2ce..cb276a03ac 100644 --- a/src/Libraries/SmartStore.Core/Data/IDbContext.cs +++ b/src/Libraries/SmartStore.Core/Data/IDbContext.cs @@ -87,12 +87,20 @@ IList ExecuteStoredProcedureList(string commandText, params ob /// true when the entity is attched already, false otherwise bool IsAttached(TEntity entity) where TEntity : BaseEntity; - /// - /// Detaches an entity from the current object context if it's attached - /// - /// Type of entity - /// The entity instance to detach - void DetachEntity(TEntity entity) where TEntity : BaseEntity; + /// + /// Attaches an entity to the context or returns an already attached entity (if it was already attached) + /// + /// Type of entity + /// Entity + /// Attached entity + TEntity Attach(TEntity entity) where TEntity : BaseEntity; + + /// + /// Detaches an entity from the current object context if it's attached + /// + /// Type of entity + /// The entity instance to detach + void DetachEntity(TEntity entity) where TEntity : BaseEntity; /// /// Detaches all entities of type TEntity from the current object context diff --git a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs index 0889cd6684..c4c1c82196 100644 --- a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs +++ b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs @@ -167,5 +167,10 @@ public static void LoadReference( } } + public static void AttachRange(this IDbContext ctx, IEnumerable entities) where TEntity : BaseEntity + { + entities.Each(x => ctx.Attach(x)); + } + } } diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.cs index 278d88d984..5db3d6b0ef 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.cs @@ -191,7 +191,7 @@ private IEnumerable ToParameters(params object[] parameters) { for (int i = 0; i < result.Count; i++) { - result[i] = AttachEntity(result[i]); + result[i] = Attach(result[i]); } } } @@ -224,7 +224,7 @@ private IEnumerable ToParameters(params object[] parameters) { for (int i = 0; i < result.Count; i++) { - result[i] = AttachEntity(result[i]); + result[i] = Attach(result[i]); } } // close up the reader, we're done saving results @@ -336,13 +336,14 @@ public IDictionary GetModifiedProperties(BaseEntity entity) // be aware of the entity state. you cannot get modified properties for detached entities. if (entry.State != System.Data.Entity.EntityState.Detached) { - var modifiedPropertyNames = from p in entry.CurrentValues.PropertyNames - where entry.Property(p).IsModified - select p; + var modifiedProperties = from p in entry.CurrentValues.PropertyNames + let prop = entry.Property(p) + where prop.IsModified + select prop; - foreach (var name in modifiedPropertyNames) + foreach (var prop in modifiedProperties) { - props.Add(name, entry.Property(name).OriginalValue); + props.Add(prop.Name, prop.OriginalValue); } } @@ -495,8 +496,9 @@ protected internal bool IsSqlServer2012OrHigher() return s_isSqlServer2012OrHigher.Value; } - private TEntity Attach(DbSet dbSet, TEntity entity) where TEntity : BaseEntity - { + public TEntity Attach(TEntity entity) where TEntity : BaseEntity + { + var dbSet = Set(); var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); if (alreadyAttached == null) @@ -508,27 +510,6 @@ private TEntity Attach(DbSet dbSet, TEntity entity) where TEnt return alreadyAttached; } - /// - /// Attach an entity to the context or return an already attached entity (if it was already attached) - /// - /// TEntity - /// Entity - /// Attached entity - public TEntity AttachEntity(TEntity entity) where TEntity : BaseEntity - { - // little hack here until Entity Framework really supports stored procedures - // otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context - - return Attach(Set(), entity); - } - - public void AttachEntities(IEnumerable entities) where TEntity : BaseEntity - { - var dbSet = Set(); - - entities.Each(entity => Attach(dbSet, entity)); - } - public bool IsAttached(TEntity entity) where TEntity : BaseEntity { if (entity != null) diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index eb1a0d2306..5f7650cf10 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -211,11 +211,8 @@ private void DetachAllEntitiesAndClear(DataExporterContext ctx) try { // now again attach what is globally required - var dataContextBase = _dbContext as ObjectContextBase; - - dataContextBase.AttachEntity(ctx.Request.Profile); - - dataContextBase.AttachEntities(ctx.Stores.Values); + _dbContext.Attach(ctx.Request.Profile); + _dbContext.AttachRange(ctx.Stores.Values); } catch (Exception exception) { @@ -439,24 +436,27 @@ private void Deploy(DataExporterContext ctx) try { + IFilePublisher publisher = null; + if (deployment.DeploymentType == ExportDeploymentType.Email) { - var publisher = new EmailFilePublisher(_emailAccountService.Value, _queuedEmailService.Value); - publisher.Publish(context, deployment); + publisher = new EmailFilePublisher(_emailAccountService.Value, _queuedEmailService.Value); } else if (deployment.DeploymentType == ExportDeploymentType.FileSystem) { - var publisher = new FileSystemFilePublisher(); - publisher.Publish(context, deployment); + publisher = new FileSystemFilePublisher(); } else if (deployment.DeploymentType == ExportDeploymentType.Ftp) { - var publisher = new FtpFilePublisher(); - publisher.Publish(context, deployment); + publisher = new FtpFilePublisher(); } else if (deployment.DeploymentType == ExportDeploymentType.Http) { - var publisher = new HttpFilePublisher(); + publisher = new HttpFilePublisher(); + } + + if (publisher != null) + { publisher.Publish(context, deployment); } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs index f88aa7cda1..9e4940d4e7 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DynamicEntityHelper.cs @@ -540,7 +540,9 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product) return result; } - private dynamic ToDynamic(DataExporterContext ctx, Product product, + private dynamic ToDynamic( + DataExporterContext ctx, + Product product, ICollection combinations, ProductVariantAttributeCombination combination) { @@ -655,7 +657,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, .Select(x => ToDynamic(ctx, x)) .ToList(); - dynObject.ProductAttributeCombinations = combinations + dynObject.ProductAttributeCombinations = (combinations ?? Enumerable.Empty()) .Select(x => { dynamic dyn = ToDynamic(ctx, x); @@ -948,13 +950,20 @@ private List Convert(DataExporterContext ctx, Product product) { var result = new List(); - var combinations = ctx.ProductExportContext.AttributeCombinations.Load(product.Id); + var hasAttributeCombinations = false; - if (!ctx.IsPreview && ctx.Projection.AttributeCombinationAsProduct && combinations.Where(x => x.IsActive).Count() > 0) + if (!ctx.IsPreview && ctx.Projection.AttributeCombinationAsProduct) + { + hasAttributeCombinations = _dbContext.QueryForCollection(product, (Product p) => p.ProductVariantAttributeCombinations).Where(c => c.IsActive).Any(); + } + + if (hasAttributeCombinations) { var productType = typeof(Product); var productValues = new Dictionary(); + // TODO: work with Entry().CurrentValues instead of Reflection. + // First attach the object, determine all values and detach again. var properties = FastProperty.GetProperties(product.GetUnproxiedType()) .Values .Where(x => x.Property.CanRead && x.Property.CanWrite); @@ -965,7 +974,9 @@ private List Convert(DataExporterContext ctx, Product product) productValues.Add(property.Name, property.GetValue(product)); } - foreach (var combination in combinations.Where(x => x.IsActive)) + var combinations = ctx.ProductExportContext.AttributeCombinations.Load(product.Id); + + foreach (var combination in combinations) { var productClone = new Product(); @@ -980,7 +991,7 @@ private List Convert(DataExporterContext ctx, Product product) } else { - var dynObject = ToDynamic(ctx, product, combinations, null); + var dynObject = ToDynamic(ctx, product, null, null); result.Add(dynObject); } From fd08f0367ae431ed67e4776ec930a341450275a0 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 18 Nov 2015 11:25:41 +0100 Subject: [PATCH 097/732] Changed folder for export profiles --- .gitignore | 1 + .../SmartStore.Services/DataExchange/ExportExtensions.cs | 8 ++++++-- .../DataExchange/Internal/DataExporterContext.cs | 3 +-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3b997b1c64..460b0b95df 100644 --- a/.gitignore +++ b/.gitignore @@ -200,6 +200,7 @@ Kopie von* /build /src/Presentation/SmartStore.Web/Plugins /src/Presentation/SmartStore.Web/App_Data/_temp/ +/src/Presentation/SmartStore.Web/App_Data/ExportProfiles/ /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.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index cf86f0d411..f46fb9094a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -42,9 +42,13 @@ public static string GetName(this Provider provider, ILocalizat /// /// Export profile /// Folder path - public static string GetExportFolder(this ExportProfile profile, bool content = false) + public static string GetExportFolder(this ExportProfile profile, bool content = false, bool create = false) { - var path = Path.Combine(FileSystemHelper.TempDir(), @"Profile\Export\{0}{1}".FormatInvariant(profile.FolderName, content ? @"\Content" : "")); + var path = CommonHelper.MapPath(string.Concat("~/App_Data/ExportProfiles/", profile.FolderName, content ? "/Content" : "")); + + if (create && !System.IO.Directory.Exists(path)) + System.IO.Directory.CreateDirectory(path); + return path; } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index 0d44c9843e..031406df3a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -10,7 +10,6 @@ using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Logging; -using SmartStore.Utilities; namespace SmartStore.Services.DataExchange.Internal { @@ -27,7 +26,7 @@ public DataExporterContext( Projection = XmlHelper.Deserialize(request.Profile.Projection); IsPreview = isPreview; - FolderContent = FileSystemHelper.TempDir(@"Profile\Export\{0}\Content".FormatInvariant(request.Profile.FolderName)); + FolderContent = request.Profile.GetExportFolder(true, true); FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; Categories = new Dictionary(); From 86b8d4485487debfd34c088fb581a6afbec9da3c Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 18 Nov 2015 18:04:25 +0100 Subject: [PATCH 098/732] Added column with export file number in export profile list --- .../201511121139009_ExportFramework3.cs | 6 +- .../DataExchange/DataExporter.cs | 22 ++--- .../DataExchange/ExportExtensions.cs | 36 +++++++- .../Internal/DataExporterContext.cs | 15 ---- .../Controllers/ExportController.cs | 83 +++++++++++++++++-- .../Models/DataExchange/ExportProfileModel.cs | 20 +++++ .../Administration/SmartStore.Admin.csproj | 2 + .../Administration/Views/Export/Edit.cshtml | 2 +- .../Administration/Views/Export/List.cshtml | 53 ++++++++++-- .../Views/Export/ProfileFileCount.cshtml | 13 +++ .../Views/Export/ProfileFileDetails.cshtml | 23 +++++ .../Views/Export/_Tab.Deployment.cshtml | 1 - 12 files changed, 235 insertions(+), 41 deletions(-) create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileCount.cshtml create mode 100644 src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileDetails.cshtml diff --git a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs index b3b39a74de..acf47b44eb 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201511121139009_ExportFramework3.cs @@ -197,7 +197,11 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.DataExchange.Export.MissingSystemProfile", "The system export profile {0} was not found.", "Das System-Exportprofil {0} wurde nicht gefunden."); - } + + builder.AddOrUpdate("Admin.DataExchange.Export.ExportFiles", + "Export files", + "Exportdateien"); + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index 5f7650cf10..25a0651cbd 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -415,22 +415,21 @@ private bool CallProvider(DataExporterContext ctx, string streamId, string metho return (ctx.ExecuteContext.Abort != ExportAbortion.Hard); } - private void Deploy(DataExporterContext ctx) + private void Deploy(DataExporterContext ctx, string zipPath) { - var containerManager = EngineContext.Current.ContainerManager; var allFiles = System.IO.Directory.GetFiles(ctx.FolderContent, "*.*", SearchOption.AllDirectories); var context = new ExportDeploymentContext { Log = ctx.Log, FolderContent = ctx.FolderContent, - ZipPath = ctx.ZipPath + ZipPath = zipPath }; foreach (var deployment in ctx.Request.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled)) { if (deployment.CreateZip) - context.DeploymentFiles = new string[] { ctx.ZipPath }; + context.DeploymentFiles = new string[] { zipPath }; else context.DeploymentFiles = allFiles; @@ -977,11 +976,14 @@ private void ExportCoreOuter(DataExporterContext ctx) if (ctx.Request.Profile == null || !ctx.Request.Profile.Enabled) return; - FileSystemHelper.Delete(ctx.LogPath); + var logPath = ctx.Request.Profile.GetExportLogPath(); + var zipPath = ctx.Request.Profile.GetExportZipPath(); + + FileSystemHelper.Delete(logPath); + FileSystemHelper.Delete(zipPath); FileSystemHelper.ClearDirectory(ctx.FolderContent, false); - FileSystemHelper.Delete(ctx.ZipPath); - using (var logger = new TraceLogger(ctx.LogPath)) + using (var logger = new TraceLogger(logPath)) { try { @@ -1053,14 +1055,14 @@ private void ExportCoreOuter(DataExporterContext ctx) { if (ctx.Request.Profile.CreateZipArchive || ctx.Request.Profile.Deployments.Any(x => x.Enabled && x.CreateZip)) { - ZipFile.CreateFromDirectory(ctx.FolderContent, ctx.ZipPath, CompressionLevel.Fastest, true); + ZipFile.CreateFromDirectory(ctx.FolderContent, zipPath, CompressionLevel.Fastest, true); } if (ctx.Request.Profile.Deployments.Any(x => x.Enabled)) { SetProgress(ctx, T("Common.Deployment")); - Deploy(ctx); + Deploy(ctx, zipPath); } } @@ -1127,7 +1129,7 @@ private void ExportCoreOuter(DataExporterContext ctx) // post process order entities if (ctx.EntityIdsLoaded.Count > 0 && ctx.Request.Provider.Value.EntityType == ExportEntityType.Order && ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) { - using (var logger = new TraceLogger(ctx.LogPath)) + using (var logger = new TraceLogger(logPath)) { try { diff --git a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs index f46fb9094a..4de3aadd7c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ExportExtensions.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Text; using SmartStore.Core.Domain; using SmartStore.Core.Domain.Stores; @@ -57,12 +59,40 @@ public static string GetExportFolder(this ExportProfile profile, bool content = /// /// Export profile /// Log file path - public static string GetExportLogFilePath(this ExportProfile profile) + public static string GetExportLogPath(this ExportProfile profile) { - var path = Path.Combine(profile.GetExportFolder(), "log.txt"); - return path; + return Path.Combine(profile.GetExportFolder(), "log.txt"); + } + + /// + /// Gets the ZIP path for a profile + /// + /// Export profile + /// ZIP file path + public static string GetExportZipPath(this ExportProfile profile) + { + return Path.Combine(profile.GetExportFolder(), profile.FolderName + ".zip"); } + /// + /// Gets existing export files for an export profile + /// + /// Export profile + /// List of file names + public static List GetExportFiles(this ExportProfile profile) + { + var exportFolder = profile.GetExportFolder(true); + + if (System.IO.Directory.Exists(exportFolder)) + { + return System.IO.Directory.EnumerateFiles(exportFolder, "*", SearchOption.TopDirectoryOnly) + .OrderBy(x => x) + .ToList(); + } + + return new List(); + } + /// /// Resolves the file name pattern for an export profile /// diff --git a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs index 031406df3a..0b09514a17 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Internal/DataExporterContext.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using SmartStore.Core; @@ -27,7 +26,6 @@ public DataExporterContext( IsPreview = isPreview; FolderContent = request.Profile.GetExportFolder(true, true); - FolderRoot = System.IO.Directory.GetParent(FolderContent).FullName; Categories = new Dictionary(); CategoryPathes = new Dictionary(); @@ -87,20 +85,7 @@ public bool Supports(ExportFeatures feature) public TraceLogger Log { get; set; } public Store Store { get; set; } - public string FolderRoot { get; private set; } public string FolderContent { get; private set; } - public string ZipName - { - get { return Request.Profile.FolderName + ".zip"; } - } - public string ZipPath - { - get { return Path.Combine(FolderRoot, ZipName); } - } - public string LogPath - { - get { return Path.Combine(FolderRoot, "log.txt"); } - } public bool IsFileBasedExport { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 5fafb8d0df..aba647e6cf 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -16,12 +16,12 @@ using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Domain.Stores; +using SmartStore.Core.IO; using SmartStore.Core.Plugins; using SmartStore.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Customers; using SmartStore.Services.DataExchange; -using SmartStore.Services.DataExchange.Internal; using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Localization; @@ -106,6 +106,20 @@ private string GetThumbnailUrl(Provider provider) return url; } + private ExportProfileDetailsModel PrepareProfileDetailsModel(ExportProfile profile) + { + var zipPath = profile.GetExportZipPath(); + + var model = new ExportProfileDetailsModel + { + Id = profile.Id, + ZipPath = (System.IO.File.Exists(zipPath) ? zipPath : null), + ExportFiles = profile.GetExportFiles() + }; + + return model; + } + private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile, Provider provider) { model.Id = profile.Id; @@ -120,13 +134,15 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile model.ScheduleTaskName = profile.ScheduleTask.Name.NaIfEmpty(); model.IsTaskRunning = profile.ScheduleTask.IsRunning; model.IsTaskEnabled = profile.ScheduleTask.Enabled; - model.LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()); + model.LogFileExists = System.IO.File.Exists(profile.GetExportLogPath()); model.HasActiveProvider = (provider != null); model.FileNamePatternDescriptions = T("Admin.DataExchange.Export.FileNamePatternDescriptions").Text.SplitSafe(";"); model.Provider = new ExportProfileModel.ProviderModel(); model.Provider.ThumbnailUrl = GetThumbnailUrl(provider); + model.Details = PrepareProfileDetailsModel(profile); + if (provider != null) { var descriptor = provider.Metadata.PluginDescriptor; @@ -145,7 +161,7 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile model.Provider.EntityTypeName = provider.Value.EntityType.GetLocalizedEnum(_services.Localization, _services.WorkContext); model.Provider.FileExtension = provider.Value.FileExtension; } - } + } private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile profile, Provider provider) { @@ -478,6 +494,41 @@ public ActionResult List() return View(model); } + public ActionResult ProfileListDetails(int profileId) + { + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) + { + var profile = _exportService.GetExportProfileById(profileId); + if (profile != null) + { + var model = PrepareProfileDetailsModel(profile); + + return Json(new + { + exportFileCount = this.RenderPartialViewToString("ProfileFileCount", model) + }, JsonRequestBehavior.AllowGet); + } + } + + return new EmptyResult(); + } + + public ActionResult ProfileFileDetails(int profileId) + { + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) + { + var profile = _exportService.GetExportProfileById(profileId); + if (profile != null) + { + var model = PrepareProfileDetailsModel(profile); + + return PartialView(model); + } + } + + return new EmptyResult(); + } + public ActionResult Create() { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) @@ -742,7 +793,7 @@ public ActionResult Preview(int id) GridPageSize = DataExporter.PageSize, EntityType = provider.Value.EntityType, TotalRecords = totalRecords, - LogFileExists = System.IO.File.Exists(profile.GetExportLogFilePath()), + LogFileExists = System.IO.File.Exists(profile.GetExportLogPath()), UsernamesEnabled = _customerSettings.Value.UsernamesEnabled }; @@ -979,7 +1030,7 @@ public ActionResult DownloadLogFile(int id) if (profile == null) return RedirectToAction("List"); - var path = profile.GetExportLogFilePath(); + var path = profile.GetExportLogPath(); var stream = new FileStream(path, FileMode.Open); var result = new FileStreamResult(stream, "text/plain; charset=utf-8"); @@ -988,6 +1039,28 @@ public ActionResult DownloadLogFile(int id) return result; } + public ActionResult DownloadExportFile(int id, string name) + { + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) + return AccessDeniedView(); + + var profile = _exportService.GetExportProfileById(id); + if (profile == null) + return RedirectToAction("List"); + + var path = Path.Combine(profile.GetExportFolder(true), name); + + if (!System.IO.File.Exists(path)) + path = Path.Combine(profile.GetExportFolder(false), name); + + var stream = new FileStream(path, FileMode.Open); + + var result = new FileStreamResult(stream, MimeTypes.MapNameToMimeType(path)); + result.FileDownloadName = Path.GetFileName(path); + + return result; + } + public ActionResult ResolveFileNamePatternExample(int id, string pattern) { var profile = _exportService.GetExportProfileById(id); diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index 7ea59f0921..b41f8fc1f5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -95,6 +95,8 @@ public partial class ExportProfileModel : EntityModelBase public ScheduleTaskModel TaskModel { get; set; } + public ExportProfileDetailsModel Details { get; set; } + public class ProviderModel { public string ConfigPartialViewName { get; set; } @@ -152,4 +154,22 @@ public class ProviderSelectItem public string Description { get; set; } } } + + + public partial class ExportProfileDetailsModel : EntityModelBase + { + [SmartResourceDisplayName("Admin.DataExchange.Export.ExportFiles")] + public List ExportFiles { get; set; } + + public string ZipPath { get; set; } + + [SmartResourceDisplayName("Admin.DataExchange.Export.ExportFiles")] + public int ExportFileCount + { + get + { + return (ExportFiles.Count + (ZipPath.HasValue() ? 1 : 0)); + } + } + } } \ 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 5bc8e616ad..189b2f717d 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -535,6 +535,8 @@ + + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index ecf22fd744..17a71d5ebc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -25,7 +25,7 @@ } @if (Model.LogFileExists) { - +  @T("Admin.Configuration.ActivityLog") } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml index 6448df05a3..c1e1a0beb3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml @@ -27,11 +27,12 @@
@T("Common.Image") @T("Admin.DataExchange.Export.Name")@T("Admin.DataExchange.Export.IsSystemProfile") @T("Common.Provider") @T("Common.Enabled") @T("Admin.DataExchange.Export.EntityType") @T("Admin.DataExchange.Export.FileExtension")@T("Admin.DataExchange.Export.IsSystemProfile")@T("Admin.DataExchange.Export.ExportFiles") @T("Admin.System.ScheduleTasks.LastStart") @T("Admin.System.ScheduleTasks.NextRun") @T("Admin.Common.Actions")
@profile.Provider.FriendlyName + @Html.SymbolForBool(profile.IsSystemProfile) + @if (profile.Provider.ConfigurationUrl.HasValue()) { @@ -71,8 +75,8 @@ @Html.IconForFileExtension(profile.Provider.FileExtension, true) - @Html.SymbolForBool(profile.IsSystemProfile) + + @Html.Partial("ProfileFileCount", profile.Details)
@@ -127,6 +131,17 @@ else
+ + -} - - - - +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml index 129406b9a5..9d9bacaa8a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml @@ -37,13 +37,13 @@ @@ -217,7 +217,7 @@
\ No newline at end of file From 5a05d01dea5c2e8cc779617a573874168825b41d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Fri, 27 Nov 2015 14:17:19 +0100 Subject: [PATCH 127/732] Added manufacturer CSV export --- .../Controllers/ManufacturerController.cs | 19 +++--- .../Models/Catalog/ManufacturerListModel.cs | 5 +- .../Views/Manufacturer/List.cshtml | 61 +++++++++++++++++-- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs index 6d40857243..1382fe86c0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs @@ -241,14 +241,12 @@ public ActionResult List() if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - var model = new ManufacturerListModel(); - var manufacturers = _manufacturerService.GetAllManufacturers(null, 0, _adminAreaSettings.GridPageSize, true); - model.Manufacturers = new GridModel - { - Data = manufacturers.Select(x => x.ToModel()), - Total = manufacturers.TotalCount - }; - return View(model); + var model = new ManufacturerListModel + { + GridPageSize = _adminAreaSettings.GridPageSize + }; + + return View(model); } [HttpPost, GridAction(EnableCustomBinding = true)] @@ -257,13 +255,14 @@ public ActionResult List(GridCommand command, ManufacturerListModel model) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - var manufacturers = _manufacturerService.GetAllManufacturers(model.SearchManufacturerName, - command.Page - 1, command.PageSize, true); + var manufacturers = _manufacturerService.GetAllManufacturers(model.SearchManufacturerName, command.Page - 1, command.PageSize, true); + var gridModel = new GridModel { Data = manufacturers.Select(x => x.ToModel()), Total = manufacturers.TotalCount }; + return new JsonResult { Data = gridModel diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Catalog/ManufacturerListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Catalog/ManufacturerListModel.cs index 9fedd4f0bd..f683ac54c0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Catalog/ManufacturerListModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Catalog/ManufacturerListModel.cs @@ -1,16 +1,15 @@ using System.Web.Mvc; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Mvc; -using Telerik.Web.Mvc; namespace SmartStore.Admin.Models.Catalog { - public class ManufacturerListModel : ModelBase + public class ManufacturerListModel : ModelBase { [SmartResourceDisplayName("Admin.Catalog.Manufacturers.List.SearchManufacturerName")] [AllowHtml] public string SearchManufacturerName { get; set; } - public GridModel Manufacturers { get; set; } + public int GridPageSize { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml index 4d867bfcad..c5f8fccc99 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml @@ -1,8 +1,6 @@ @model ManufacturerListModel @using Telerik.Web.Mvc.UI -@{ - var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - +@{ ViewBag.Title = T("Admin.Catalog.Manufacturers").Text; }
@@ -45,12 +43,19 @@
- @(Html.Telerik().Grid(Model.Manufacturers.Data) + @(Html.Telerik().Grid() .Name("manufacturers-grid") .ClientEvents(events => events - .OnDataBinding("onDataBinding")) + .OnDataBinding("onDataBinding") + .OnDataBound("onDataBound")) .Columns(columns => { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); columns.Bound(x => x.Name) .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) .ClientTemplate("\"><#= Name #>"); @@ -68,7 +73,7 @@ .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) .Centered(); }) - .Pageable(settings => settings.Total(Model.Manufacturers.Total).PageSize(gridPageSize).Position(GridPagerPosition.Both)) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Manufacturer")) .PreserveGridState() .EnableCustomBinding(true)) @@ -77,6 +82,9 @@
\ No newline at end of file From 7233247becc5ecd01ea68fa7b64054832aa6af8d Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sat, 28 Nov 2015 11:12:22 +0100 Subject: [PATCH 128/732] Resolves #820 ConfigureTheme page contains German --- .../Migrations/201509150931528_ExportFramework2.cs | 4 ++++ .../SmartStore.Web/Views/Shared/ConfigureTheme.cshtml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs index c73e7769f1..d3fe755327 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs @@ -249,6 +249,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.System.SystemInfo.GarbageCollectSuccessful", "The memory has been successfully cleaned up.", "Der Arbeitsspeicher wurde erfolgreich aufgerumt."); + + builder.AddOrUpdate("Admin.Configuration.Themes.NoConfigurationRequired", + "Theme requires no configuration", + "Theme bentigt keine Konfiguration"); } } } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/ConfigureTheme.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/ConfigureTheme.cshtml index ca0152ce09..a6e34081b9 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/ConfigureTheme.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/ConfigureTheme.cshtml @@ -2,4 +2,4 @@ Layout = null; } -
Theme benötigt keine Konfiguration
+
@T("Admin.Configuration.Themes.NoConfigurationRequired")
From fcc2608a7f946272da638d8c2331eb114d2f63e0 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 30 Nov 2015 10:57:39 +0100 Subject: [PATCH 129/732] Further improvements in presenting download links of export files --- .../Logging/LoggingExtensions.cs | 12 ++- .../201511271019577_ExportFramework3.cs | 6 ++ .../DataExchange/DataExportResult.cs | 2 +- .../DataExchange/DataExporter.cs | 23 +++-- .../Controllers/ExportController.cs | 94 ++++++++++--------- .../DataExchange/ExportDeploymentModel.cs | 9 +- .../Models/DataExchange/ExportProfileModel.cs | 12 ++- .../Views/Export/InfoDeployment.cshtml | 59 +++++++----- .../Administration/Views/Export/List.cshtml | 2 +- .../Views/Export/ProfileFileCount.cshtml | 9 +- .../Views/Export/ProfileFileDetails.cshtml | 34 +++++-- .../Views/Export/_Tab.Deployment.cshtml | 7 ++ 12 files changed, 165 insertions(+), 104 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs b/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs index de1266efd4..f7c2633e40 100644 --- a/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs +++ b/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs @@ -37,7 +37,17 @@ public static void Error(this ILogger logger, Exception exception, Customer cust FilteredLog(logger, LogLevel.Error, exception.ToAllMessages(), exception, customer); } - private static void FilteredLog(ILogger logger, LogLevel level, string message, Exception exception = null, Customer customer = null) + public static void ErrorsAll(this ILogger logger, Exception exception, Customer customer = null) + { + while (exception != null) + { + FilteredLog(logger, LogLevel.Error, exception.Message, exception, customer); + + exception = exception.InnerException; + } + } + + private static void FilteredLog(ILogger logger, LogLevel level, string message, Exception exception = null, Customer customer = null) { // don't log thread abort exception if ((exception != null) && (exception is System.Threading.ThreadAbortException)) diff --git a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs index 42fe9dd04d..00ea19fba6 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs @@ -59,6 +59,12 @@ public void Seed(SmartObjectContext context) public void MigrateLocaleResources(LocaleResourcesBuilder builder) { builder.AddOrUpdate("Admin.Common.Export.PDF", "PDF Export", "PDF Export"); + builder.AddOrUpdate("Admin.Common.TemporaryFiles", "Temporary files", "Temporre Dateien"); + builder.AddOrUpdate("Admin.Common.PublicFiles", "Public files", "ffentliche Dateien"); + + builder.AddOrUpdate("Admin.Common.NoTempFilesFound", + "No temporary files were found.", + "Es wurden keine temporren Dateien gefunden."); builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.NewsLetterSubscription", "Newsletter Subscribers", diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs index cfbe0db7bf..d7e0d9fa6a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExportResult.cs @@ -29,7 +29,7 @@ public bool Succeeded /// /// Files created by last export /// - public IList Files { get; set; } + public List Files { get; set; } /// /// The path of the folder with the export files diff --git a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs index 528bc1f1b4..9193f38739 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/DataExporter.cs @@ -1135,7 +1135,7 @@ private void ExportCoreOuter(DataExporterContext ctx) } catch (Exception exception) { - logger.Error(exception); + logger.ErrorsAll(exception); ctx.Result.LastError = exception.ToString(); } finally @@ -1149,7 +1149,10 @@ private void ExportCoreOuter(DataExporterContext ctx) _exportProfileService.Value.UpdateExportProfile(ctx.Request.Profile); } } - catch { } + catch (Exception exception) + { + logger.ErrorsAll(exception); + } try { @@ -1158,7 +1161,10 @@ private void ExportCoreOuter(DataExporterContext ctx) FileSystemHelper.ClearDirectory(ctx.FolderContent, false); } } - catch { } + catch (Exception exception) + { + logger.ErrorsAll(exception); + } DetachAllEntitiesAndClear(ctx); @@ -1180,7 +1186,10 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.ExecuteContext.Log = null; ctx.Log = null; } - catch { } + catch (Exception exception) + { + logger.ErrorsAll(exception); + } } } @@ -1215,10 +1224,10 @@ private void ExportCoreOuter(DataExporterContext ctx) logger.Information("Updated order status for {0} order(s).".FormatInvariant(ctx.EntityIdsLoaded.Count())); } - catch (Exception exc) + catch (Exception exception) { - logger.Error(exc); - ctx.Result.LastError = exc.ToString(); + logger.ErrorsAll(exception); + ctx.Result.LastError = exception.ToString(); } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 11d5681e86..b54951bcc0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -106,21 +106,56 @@ private string GetThumbnailUrl(Provider provider) return url; } - private ExportProfileDetailsModel PrepareProfileDetailsModel(ExportProfile profile) + private ExportProfileDetailsModel PrepareProfileDetailsModel(ExportProfile profile, bool forEdit) { - var zipPath = profile.GetExportZipPath(); - var model = new ExportProfileDetailsModel { Id = profile.Id, - ZipPath = (System.IO.File.Exists(zipPath) ? zipPath : null), - ExportFiles = profile.GetExportFiles() + PublicFiles = new List() }; + try + { + var zipPath = profile.GetExportZipPath(); + + model.ZipPath = (System.IO.File.Exists(zipPath) ? zipPath : null); + model.ExportFiles = profile.GetExportFiles(); + + if (forEdit && profile.Deployments.Any(x => x.IsPublic && x.DeploymentType == ExportDeploymentType.FileSystem)) + { + var allStores = _services.StoreService.GetAllStores(); + var publicFolder = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder); + var resultInfo = XmlHelper.Deserialize(profile.ResultInfo); + + if (resultInfo != null && resultInfo.Files != null) + { + foreach (var fileInfo in resultInfo.Files) + { + if (System.IO.File.Exists(Path.Combine(publicFolder, fileInfo.FileName)) && !model.PublicFiles.Any(x => x.FileName == fileInfo.FileName)) + { + var store = allStores.FirstOrDefault(y => y.Id == fileInfo.StoreId) ?? _services.StoreContext.CurrentStore; + + model.PublicFiles.Add(new ExportProfileDetailsModel.PublicFile + { + StoreId = store.Id, + StoreName = store.Name, + FileName = fileInfo.FileName, + FileUrl = string.Concat(store.Url.EnsureEndsWith("/"), DataExporter.PublicFolder.EnsureEndsWith("/"), fileInfo.FileName) + }); + } + } + } + } + } + catch (Exception exception) + { + NotifyError(exception); + } + return model; } - private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile, Provider provider) + private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile, Provider provider, bool forEdit) { model.Id = profile.Id; model.Name = profile.Name; @@ -141,7 +176,7 @@ private void PrepareProfileModel(ExportProfileModel model, ExportProfile profile model.Provider = new ExportProfileModel.ProviderModel(); model.Provider.ThumbnailUrl = GetThumbnailUrl(provider); - model.Details = PrepareProfileDetailsModel(profile); + model.Details = PrepareProfileDetailsModel(profile, forEdit); if (provider != null) { @@ -266,37 +301,7 @@ private void PrepareProfileModelForEdit(ExportProfileModel model, ExportProfile { var deploymentModel = PrepareDeploymentModel(x, null, false); - if (x.IsPublic) - { - try - { - var publicFolder = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder); - var resultInfo = XmlHelper.Deserialize(profile.ResultInfo); - - if (resultInfo != null && resultInfo.Files != null) - { - foreach (var fileInfo in resultInfo.Files) - { - if (System.IO.File.Exists(Path.Combine(publicFolder, fileInfo.FileName)) && !deploymentModel.PublicFiles.Any(y => y.FileName == fileInfo.FileName)) - { - var store = allStores.FirstOrDefault(y => y.Id == fileInfo.StoreId) ?? _services.StoreContext.CurrentStore; - - deploymentModel.PublicFiles.Add(new ExportDeploymentModel.PublicFile - { - StoreId = store.Id, - StoreName = store.Name, - FileName = fileInfo.FileName, - FileUrl = string.Concat(store.Url.EnsureEndsWith("/"), DataExporter.PublicFolder.EnsureEndsWith("/"), fileInfo.FileName) - }); - } - } - } - } - catch (Exception exc) - { - exc.Dump(); - } - } + deploymentModel.ProfileDetails = PrepareProfileDetailsModel(profile, true); return deploymentModel; }) @@ -402,8 +407,7 @@ private ExportDeploymentModel PrepareDeploymentModel(ExportDeployment deployment EmailSubject = deployment.EmailSubject, EmailAccountId = deployment.EmailAccountId, PassiveMode = deployment.PassiveMode, - UseSsl = deployment.UseSsl, - PublicFiles = new List() + UseSsl = deployment.UseSsl }; if (forEdit) @@ -484,7 +488,7 @@ public ActionResult List() { var profileModel = new ExportProfileModel(); - PrepareProfileModel(profileModel, profile, providers.FirstOrDefault(x => x.Metadata.SystemName == profile.ProviderSystemName)); + PrepareProfileModel(profileModel, profile, providers.FirstOrDefault(x => x.Metadata.SystemName == profile.ProviderSystemName), false); profileModel.TaskModel = profile.ScheduleTask.ToScheduleTaskModel(_services.Localization, _dateTimeHelper, Url); @@ -501,7 +505,7 @@ public ActionResult ProfileListDetails(int profileId) var profile = _exportService.GetExportProfileById(profileId); if (profile != null) { - var model = PrepareProfileDetailsModel(profile); + var model = PrepareProfileDetailsModel(profile, false); return Json(new { @@ -520,7 +524,7 @@ public ActionResult ProfileFileDetails(int profileId) var profile = _exportService.GetExportProfileById(profileId); if (profile != null) { - var model = PrepareProfileDetailsModel(profile); + var model = PrepareProfileDetailsModel(profile, true); return PartialView(model); } @@ -610,7 +614,7 @@ public ActionResult Edit(int id) var model = new ExportProfileModel(); - PrepareProfileModel(model, profile, provider); + PrepareProfileModel(model, profile, provider, true); PrepareProfileModelForEdit(model, profile, provider); return View(model); @@ -631,7 +635,7 @@ public ActionResult Edit(ExportProfileModel model, bool continueEditing) if (!ModelState.IsValid) { - PrepareProfileModel(model, profile, provider); + PrepareProfileModel(model, profile, provider, true); PrepareProfileModelForEdit(model, profile, provider); return View(model); } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportDeploymentModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportDeploymentModel.cs index ffecae4f0a..6a0c317ab1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportDeploymentModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportDeploymentModel.cs @@ -55,7 +55,6 @@ public string DeploymentTypeIconClass [SmartResourceDisplayName("Admin.DataExchange.Export.Deployment.IsPublic")] public bool IsPublic { get; set; } - public List PublicFiles { get; set; } [SmartResourceDisplayName("Admin.DataExchange.Export.Deployment.Username")] public string Username { get; set; } @@ -91,12 +90,6 @@ public string DeploymentTypeIconClass [SmartResourceDisplayName("Admin.DataExchange.Export.Deployment.UseSsl")] public bool UseSsl { get; set; } - public class PublicFile - { - public int StoreId { get; set; } - public string StoreName { get; set; } - public string FileName { get; set; } - public string FileUrl { get; set; } - } + public ExportProfileDetailsModel ProfileDetails { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs index b41f8fc1f5..a1a535a5f6 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportProfileModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Web.Mvc; using FluentValidation.Attributes; using SmartStore.Admin.Models.Tasks; @@ -82,7 +81,6 @@ public partial class ExportProfileModel : EntityModelBase [SmartResourceDisplayName("Admin.DataExchange.Export.CloneProfile")] public int? CloneProfileId { get; set; } - //public List AvailableProfiles { get; set; } public List AvailableProfiles { get; set; } public ProviderModel Provider { get; set; } @@ -171,5 +169,15 @@ public int ExportFileCount return (ExportFiles.Count + (ZipPath.HasValue() ? 1 : 0)); } } + + public List PublicFiles { get; set; } + + public class PublicFile + { + public int StoreId { get; set; } + public string StoreName { get; set; } + public string FileName { get; set; } + public string FileUrl { get; set; } + } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/InfoDeployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/InfoDeployment.cshtml index 67ce817d18..fce2ac18c7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/InfoDeployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/InfoDeployment.cshtml @@ -4,42 +4,51 @@ @{ Layout = null; } -

+@helper FileDownloadLink(string path, string fileName) +{ +

+ @Html.IconForFileExtension(Path.GetExtension(fileName), false) + @fileName +
+} +
 @Model.DeploymentTypeName @if (Model.DeploymentType == ExportDeploymentType.FileSystem && Model.IsPublic) { (@T("Common.Public")) } -

-@if (Model.DeploymentType == ExportDeploymentType.FileSystem) -{ - if (Model.IsPublic) +
+
+ @if (Model.DeploymentType == ExportDeploymentType.FileSystem) { - foreach (var grp in Model.PublicFiles.OrderBy(x => x.StoreId).GroupBy(x => x.StoreId)) + foreach (var path in Model.ProfileDetails.ExportFiles) + { + @FileDownloadLink(path, Path.GetFileName(path)) + } + + if (Model.ProfileDetails.PublicFiles.Count > 0) { -

+ foreach (var grp in Model.ProfileDetails.PublicFiles.OrderBy(x => x.StoreId).GroupBy(x => x.StoreId)) + { +

@(grp.First().StoreName):
- @foreach (var file in grp) + foreach (var file in grp) { - + } -

+ } } } - else + else if (Model.DeploymentType == ExportDeploymentType.Email) { -
@Model.FileSystemPath
+
@Model.EmailAddresses
} -} -else if (Model.DeploymentType == ExportDeploymentType.Email) -{ -
@Model.EmailAddresses
-} -else if (Model.DeploymentType == ExportDeploymentType.Http) -{ -
@Model.Url
-} -else if (Model.DeploymentType == ExportDeploymentType.Ftp) -{ -
@Model.Url
-} + else if (Model.DeploymentType == ExportDeploymentType.Http) + { +
@Model.Url
+ } + else if (Model.DeploymentType == ExportDeploymentType.Ftp) + { +
@Model.Url
+ } +
\ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml index c1e1a0beb3..9ddcf27347 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/List.cshtml @@ -131,7 +131,7 @@ else
-
@Html.EditorFor(x => x.CreateZipArchive) @Html.ValidationMessageFor(x => x.CreateZipArchive) + @if (Model.Details.ZipPath.HasValue()) + { +
+ @Html.IconForFileExtension(Path.GetExtension(Model.Details.ZipPath), false) + @Path.GetFileName(Model.Details.ZipPath) +
+ }
+ +
@Html.SmartLabelFor(model => model.SearchStoreId) @@ -36,48 +36,45 @@
-

-

+ +

+
@(Html.Telerik().Grid() - .Name("topics-grid") - .Columns(columns => - { - columns.Bound(x => x.SystemName) - .Width(200) - .Template(x => Html.ActionLink(x.SystemName, "Edit", new { id = x.Id })) + .Name("topics-grid") + .Columns(columns => + { + columns.Bound(x => x.SystemName) + .Width(280) + .Template(x => Html.ActionLink(x.SystemName, "Edit", new { id = x.Id })) .ClientTemplate("<#= SystemName #>"); columns.Bound(x => x.Title); - columns.Bound(x => x.IsPasswordProtected) - .Width(100) - .Template(item => @Html.SymbolForBool(item.IsPasswordProtected)) - .ClientTemplate(@Html.SymbolForBool("IsPasswordProtected")) - .Centered(); - columns.Bound(x => x.IncludeInSitemap) - .Width(100) - .Template(item => @Html.SymbolForBool(item.IncludeInSitemap)) - .ClientTemplate(@Html.SymbolForBool("IncludeInSitemap")) - .Centered(); - columns.Bound(x => x.RenderAsWidget) - .Width(100) - .Template(item => @Html.SymbolForBool(item.RenderAsWidget)) - .ClientTemplate(@Html.SymbolForBool("RenderAsWidget")) - .Centered(); - columns.Bound(x => x.WidgetZone); - columns.Bound(x => x.Priority) + columns.Bound(x => x.IsPasswordProtected) + .Template(item => @Html.SymbolForBool(item.IsPasswordProtected)) + .ClientTemplate(@Html.SymbolForBool("IsPasswordProtected")) + .Centered(); + columns.Bound(x => x.IncludeInSitemap) + .Template(item => @Html.SymbolForBool(item.IncludeInSitemap)) + .ClientTemplate(@Html.SymbolForBool("IncludeInSitemap")) + .Centered(); + columns.Bound(x => x.RenderAsWidget) + .Template(item => @Html.SymbolForBool(item.RenderAsWidget)) + .ClientTemplate(@Html.SymbolForBool("RenderAsWidget")) + .Centered(); + columns.Bound(x => x.WidgetZone); + columns.Bound(x => x.LimitedToStores) + .Template(item => @Html.SymbolForBool(item.LimitedToStores)) + .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Hidden(Model.AvailableStores.Count <= 1) + .Centered(); + columns.Bound(x => x.Priority) .Centered(); - columns.Bound(x => x.Id) - .Width(50) - .Centered() - .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id })) - .ClientTemplate("\">" + T("Admin.Common.Edit").Text + "") - .Title(T("Admin.Common.Edit").Text); - }) - .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Topic")) + }) + .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Topic")) .ClientEvents(events => events.OnDataBinding("onDataBinding")) - .EnableCustomBinding(true)) + .EnableCustomBinding(true))
diff --git a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs index 839ca31595..600b06d52d 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CommonController.cs @@ -4,8 +4,8 @@ using System.Globalization; using System.Linq; using System.Text; -using System.Web; using System.Web.Mvc; +using SmartStore.Core.Domain; using SmartStore.Core.Domain.Blogs; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; @@ -41,15 +41,16 @@ using SmartStore.Web.Framework.UI; using SmartStore.Web.Infrastructure.Cache; using SmartStore.Web.Models.Common; -using SmartStore.Core.Domain; namespace SmartStore.Web.Controllers { - public partial class CommonController : PublicControllerBase + public partial class CommonController : PublicControllerBase { - #region Fields + private readonly static string[] s_hints = new string[] { "Shopsystem", "Onlineshop Software", "Shopsoftware", "E-Commerce Solution" }; - private readonly ITopicService _topicService; + #region Fields + + private readonly ITopicService _topicService; private readonly Lazy _languageService; private readonly Lazy _currencyService; private readonly IThemeContext _themeContext; @@ -59,8 +60,6 @@ public partial class CommonController : PublicControllerBase private readonly Lazy _mobileDeviceHelper; private readonly Lazy _compareProductsService; - private readonly static string[] s_hints = new string[] { "Shopsystem", "Onlineshop Software", "Shopsoftware", "E-Commerce Solution" }; - private readonly StoreInformationSettings _storeInfoSettings; private readonly CustomerSettings _customerSettings; private readonly TaxSettings _taxSettings; @@ -72,6 +71,7 @@ public partial class CommonController : PublicControllerBase private readonly ForumSettings _forumSettings; private readonly LocalizationSettings _localizationSettings; private readonly Lazy _securitySettings; + private readonly Lazy _socialSettings; private readonly IOrderTotalCalculationService _orderTotalCalculationService; private readonly IPriceFormatter _priceFormatter; @@ -104,7 +104,8 @@ public CommonController( ForumSettings forumSettings, LocalizationSettings localizationSettings, Lazy securitySettings, - IOrderTotalCalculationService orderTotalCalculationService, + Lazy socialSettings, + IOrderTotalCalculationService orderTotalCalculationService, IPriceFormatter priceFormatter, ThemeSettings themeSettings, IPageAssetsBuilder pageAssetsBuilder, @@ -131,6 +132,7 @@ public CommonController( this._forumSettings = forumSettings; this._localizationSettings = localizationSettings; this._securitySettings = securitySettings; + this._socialSettings = socialSettings; this._orderTotalCalculationService = orderTotalCalculationService; this._priceFormatter = priceFormatter; @@ -592,12 +594,11 @@ public ActionResult ShopBar() [ChildActionOnly] public ActionResult Footer() { - string taxInfo = (_services.WorkContext.GetTaxDisplayTypeFor(_services.WorkContext.CurrentCustomer, _services.StoreContext.CurrentStore.Id) == TaxDisplayType.IncludingTax) - ? T("Tax.InclVAT") - : T("Tax.ExclVAT"); - - string shippingInfoLink = Url.RouteUrl("Topic", new { SystemName = "shippinginfo" }); var store = _services.StoreContext.CurrentStore; + var allTopics = _topicService.GetAllTopics(store.Id); + var taxDisplayType = _services.WorkContext.GetTaxDisplayTypeFor(_services.WorkContext.CurrentCustomer, store.Id); + + var taxInfo = T(taxDisplayType == TaxDisplayType.IncludingTax ? "Tax.InclVAT" : "Tax.ExclVAT"); var availableStoreThemes = !_themeSettings.AllowCustomerToSelectTheme ? new List() : _themeRegistry.Value.GetThemeManifests() .Where(x => !x.MobileTheme) @@ -614,7 +615,6 @@ public ActionResult Footer() var model = new FooterModel { StoreName = store.Name, - LegalInfo = T("Tax.LegalInfoFooter", taxInfo, shippingInfoLink), ShowLegalInfo = _taxSettings.ShowLegalHintsInFooter, ShowThemeSelector = availableStoreThemes.Count > 1, BlogEnabled = _blogSettings.Enabled, @@ -622,6 +622,26 @@ public ActionResult Footer() HideNewsletterBlock = _customerSettings.HideNewsletterBlock, }; + model.TopicPageUrls = allTopics + .Where(x => !x.RenderAsWidget) + .GroupBy(x => x.SystemName) + .ToDictionary(x => x.Key.EmptyNull().ToLower(), x => + { + if (x.Key.IsCaseInsensitiveEqual("contactus")) + return Url.RouteUrl("ContactUs"); + + return Url.RouteUrl("Topic", new { SystemName = x.Key }); + }); + + if (model.TopicPageUrls.ContainsKey("shippinginfo")) + { + model.LegalInfo = T("Tax.LegalInfoFooter", taxInfo, model.TopicPageUrls["shippinginfo"]); + } + else + { + model.LegalInfo = T("Tax.LegalInfoFooter2", taxInfo); + } + var hint = _services.Settings.GetSettingByKey("Rnd_SmCopyrightHint", string.Empty, store.Id); if (hint.IsEmpty()) { @@ -629,30 +649,15 @@ public ActionResult Footer() _services.Settings.SetSetting("Rnd_SmCopyrightHint", hint, store.Id); } - var topics = new string[] { "paymentinfo", "imprint", "disclaimer" }; - foreach (var t in topics) - { - //load by store - var topic = _topicService.GetTopicBySystemName(t, store.Id); - if (topic == null) - //not found. let's find topic assigned to all stores - topic = _topicService.GetTopicBySystemName(t, 0); - - if (topic != null) - { - model.Topics.Add(t, topic.Title); - } - } + model.ShowSocialLinks = _socialSettings.Value.ShowSocialLinksInFooter; + model.FacebookLink = _socialSettings.Value.FacebookLink; + model.GooglePlusLink = _socialSettings.Value.GooglePlusLink; + model.TwitterLink = _socialSettings.Value.TwitterLink; + model.PinterestLink = _socialSettings.Value.PinterestLink; + model.YoutubeLink = _socialSettings.Value.YoutubeLink; - var socialSettings = EngineContext.Current.Resolve(); - - model.ShowSocialLinks = socialSettings.ShowSocialLinksInFooter; - model.FacebookLink = socialSettings.FacebookLink; - model.GooglePlusLink = socialSettings.GooglePlusLink; - model.TwitterLink = socialSettings.TwitterLink; - model.PinterestLink = socialSettings.PinterestLink; - model.YoutubeLink = socialSettings.YoutubeLink; - model.SmartStoreHint = "{0} by SmartStore AG © {1}".FormatCurrent(hint, DateTime.Now.Year); + model.SmartStoreHint = "{0} by SmartStore AG © {1}" + .FormatCurrent(hint, DateTime.Now.Year); return PartialView(model); } @@ -660,6 +665,7 @@ public ActionResult Footer() [ChildActionOnly] public ActionResult Menu() { + var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var model = new MenuModel @@ -674,7 +680,8 @@ public ActionResult Menu() IsCustomerImpersonated = _services.WorkContext.OriginalCustomerIfImpersonated != null, IsAuthenticated = customer.IsRegistered(), DisplayAdminLink = _services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel), - }; + HasContactUsPage = (_topicService.GetTopicBySystemName("ContactUs", store.Id) != null) + }; return PartialView(model); } diff --git a/src/Presentation/SmartStore.Web/Models/Common/FooterModel.cs b/src/Presentation/SmartStore.Web/Models/Common/FooterModel.cs index 67aea71c20..7d9f2980a4 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/FooterModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/FooterModel.cs @@ -5,11 +5,6 @@ namespace SmartStore.Web.Models.Common { public partial class FooterModel : ModelBase { - public FooterModel() - { - Topics = new Dictionary(); - } - public string StoreName { get; set; } public string LegalInfo { get; set; } @@ -20,7 +15,6 @@ public FooterModel() public bool HideNewsletterBlock { get; set; } public bool BlogEnabled { get; set; } public bool ForumEnabled { get; set; } - public Dictionary Topics { get; set; } public bool ShowSocialLinks { get; set; } public string FacebookLink { get; set; } @@ -28,5 +22,7 @@ public FooterModel() public string TwitterLink { get; set; } public string PinterestLink { get; set; } public string YoutubeLink { get; set; } - } + + public Dictionary TopicPageUrls { get; set; } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs b/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs index d461318081..29bba0c11e 100644 --- a/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Common/MenuModel.cs @@ -16,5 +16,7 @@ public partial class MenuModel : ModelBase public bool DisplayAdminLink { get; set; } public bool IsCustomerImpersonated { get; set; } public string CustomerEmailUsername { get; set; } - } + + public bool HasContactUsPage { get; set; } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Common/Footer.cshtml b/src/Presentation/SmartStore.Web/Views/Common/Footer.cshtml index 77b6c316da..cb9d4ab796 100644 --- a/src/Presentation/SmartStore.Web/Views/Common/Footer.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Common/Footer.cshtml @@ -52,28 +52,45 @@
- @Html.LabelFor(model => model.ShippingOriginAddress, new { style="font-weight: bold" }) -
+ @Html.SmartLabelFor(model => model.ShippingOriginAddress) + @Html.SettingOverrideCheckbox(model => Model.ShippingOriginAddress, "#pnlShippingOriginAddress")
@Html.EditorFor(model => model.ShippingOriginAddress, "Address") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Setting/ShoppingCart.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Setting/ShoppingCart.cshtml index 2a4d51b0b4..34f62d4c96 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Setting/ShoppingCart.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Setting/ShoppingCart.cshtml @@ -1,6 +1,5 @@ @model ShoppingCartSettingsModel @{ - //page title ViewBag.Title = T("Admin.Configuration.Settings.ShoppingCart").Text; } @using (Html.BeginForm()) @@ -142,8 +141,8 @@
+

-
-
+
+
-
-
@Html.SmartLabelFor(model => model.ShowLegalHintsInProductList) @@ -182,8 +176,8 @@ @Html.ValidationMessageFor(model => model.ShowLegalHintsInFooter)
+

- @Html.LabelFor(model => model.DefaultTaxAddress, new { style="font-weight: bold" }) + @Html.SmartLabelFor(model => model.DefaultTaxAddress) @Html.SettingOverrideCheckbox(model => Model.DefaultTaxAddress, "#pnlDefaultTaxAddress")
+ @Html.EditorFor(x => x.DefaultTaxAddress, "Address")
+

+

+

} \ No newline at end of file From dd74f1328433631b91676934691606822164a58a Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 4 Jan 2016 17:09:19 +0100 Subject: [PATCH 233/732] Permission list: Added label with permission category --- .../201506181858349_AclRecordCustomerRole.cs | 8 +-- .../201512151526290_ImportFramework.cs | 17 +++++ .../Security/PermissionService.cs | 16 +++-- .../Controllers/SecurityController.cs | 71 ++++++++++--------- .../Models/Security/PermissionRecordModel.cs | 22 +++++- .../Views/Security/Permissions.cshtml | 26 ++++--- 6 files changed, 105 insertions(+), 55 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs b/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs index 655737641f..d8c20d54d3 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs @@ -123,13 +123,13 @@ before you can assign them to all subcategories and products.
builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturersOnHomepage", "Show manufacturers on homepage", "Zeige Hersteller auf der Homepage", - "Specifies whether manufacturers will be displayed on the homepage.", + "Specifies whether manufacturers are displayed on the homepage.", "Legt fest, ob Hersteller auf der Homepage angezeigt werden."); builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerPictures", "Show manufacturer pictures on homepage", "Zeige Herstellerbilder auf der Homepage", - "Specifies whether manufacturers will be displayed as images or textual links on the homepage.", + "Specifies whether manufacturers are displayed as images or textual links on the homepage.", "Legt fest, ob Hersteller auf der Homepage als Bilder oder textuelle Links angezeigt werden."); builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNumberEnabled", @@ -145,8 +145,8 @@ before you can assign them to all subcategories and products.
builder.AddOrUpdate("Admin.Configuration.Settings.Tax.VatRequired", "Customers must enter a VATIN", "Kunden mssen eine Steuernummer angeben", - "Specifies whether Customers must enter a VAT identification number.", - "Legt fest, ob Kunden bei der Registrierung eine Steuernummer angeben mssen."); + "Specifies whether customers must enter a VAT identification number.", + "Legt fest, ob Kunden bei der Registrierung eine Steuernummer angeben mssen."); builder.AddOrUpdate("Account.Fields.Vat.Required", "Please enter your VATIN", diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 2073f9aecc..1c6e87e649 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -278,6 +278,23 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Zeichenkette prfen", "Enter any string to check the SEO name creation. Changed settings must be saved before.", "Geben Sie eine beliebige Zeichenkette ein, um daraus den SEO Namen zu erstellen. Genderte Einstellungen mssen zuvor gespeichert werden."); + + + builder.AddOrUpdate("Admin.System.Warnings.NoPermissionsDefined", + "There are no permissions defined.", + "Es sind keine Zugriffsrechte festgelegt."); + + builder.AddOrUpdate("Admin.System.Warnings.NoCustomerRolesDefined", + "There are no customer roles defined.", + "Es sind keine Kundengruppen festgelegt."); + + builder.AddOrUpdate("Admin.System.Warnings.AccessDeniedToAnonymousRequest", + "Access denied to anonymous request on {0}.", + "Zugriffsverweigerung durch anonyme Anfrage bei {0}."); + + builder.AddOrUpdate("Admin.System.Warnings.AccessDeniedToUser", + "Access denied to user #{0} '{1}' on {2}.", + "Zugriffsverweigerung durch Kunde #{0} '{1}' bei {2}."); } } } diff --git a/src/Libraries/SmartStore.Services/Security/PermissionService.cs b/src/Libraries/SmartStore.Services/Security/PermissionService.cs index 3ea2815466..a3ada9721a 100644 --- a/src/Libraries/SmartStore.Services/Security/PermissionService.cs +++ b/src/Libraries/SmartStore.Services/Security/PermissionService.cs @@ -10,10 +10,10 @@ namespace SmartStore.Services.Security { - /// - /// Permission service - /// - public partial class PermissionService : IPermissionService + /// + /// Permission service + /// + public partial class PermissionService : IPermissionService { #region Constants /// @@ -141,9 +141,11 @@ orderby pr.Id /// Permissions public virtual IList GetAllPermissionRecords() { - var query = from pr in _permissionRecordRepository.Table - orderby pr.Name - select pr; + var query = + from pr in _permissionRecordRepository.Table + orderby pr.Category, pr.Name + select pr; + var permissions = query.ToList(); return permissions; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs index 794c33e0f8..45b5aa901a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SecurityController.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using SmartStore.Admin.Models.Customers; using SmartStore.Admin.Models.Security; using SmartStore.Core; -using SmartStore.Services.Customers; -using SmartStore.Services.Localization; using SmartStore.Core.Logging; +using SmartStore.Services.Customers; using SmartStore.Services.Security; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Security; @@ -22,7 +20,6 @@ public class SecurityController : AdminControllerBase private readonly IWorkContext _workContext; private readonly IPermissionService _permissionService; private readonly ICustomerService _customerService; - private readonly ILocalizationService _localizationService; #endregion @@ -30,12 +27,11 @@ public class SecurityController : AdminControllerBase public SecurityController(IWorkContext workContext, IPermissionService permissionService, - ICustomerService customerService, ILocalizationService localizationService) + ICustomerService customerService) { this._workContext = workContext; this._permissionService = permissionService; this._customerService = customerService; - this._localizationService = localizationService; } #endregion  @@ -45,14 +41,15 @@ public SecurityController(IWorkContext workContext, public ActionResult AccessDenied(string pageUrl) { var currentCustomer = _workContext.CurrentCustomer; + if (currentCustomer == null || currentCustomer.IsGuest()) { - Logger.Information(string.Format("Access denied to anonymous request on {0}", pageUrl)); + Logger.Information(T("Admin.System.Warnings.AccessDeniedToAnonymousRequest", pageUrl.NaIfEmpty())); return View(); } - Logger.Information(string.Format("Access denied to user #{0} '{1}' on {2}", currentCustomer.Email, currentCustomer.Email, pageUrl)); - + Logger.Information(T("Admin.System.Warnings.AccessDeniedToUser", + currentCustomer.Email.NaIfEmpty(), currentCustomer.Email.NaIfEmpty(), pageUrl.NaIfEmpty())); return View(); } @@ -66,30 +63,38 @@ public ActionResult Permissions() var permissionRecords = _permissionService.GetAllPermissionRecords(); var customerRoles = _customerService.GetAllCustomerRoles(true); + foreach (var pr in permissionRecords) { - model.AvailablePermissions.Add(new PermissionRecordModel() + model.AvailablePermissions.Add(new PermissionRecordModel { Name = pr.Name, - SystemName = pr.SystemName + SystemName = pr.SystemName, + Category = pr.Category }); } + foreach (var cr in customerRoles) { - model.AvailableCustomerRoles.Add(new CustomerRoleModel() + model.AvailableCustomerRoles.Add(new CustomerRoleModel { Id = cr.Id, Name = cr.Name }); } - foreach (var pr in permissionRecords) - foreach (var cr in customerRoles) - { - bool allowed = pr.CustomerRoles.Where(x => x.Id == cr.Id).ToList().Count() > 0; - if (!model.Allowed.ContainsKey(pr.SystemName)) - model.Allowed[pr.SystemName] = new Dictionary(); - model.Allowed[pr.SystemName][cr.Id] = allowed; - } + + foreach (var pr in permissionRecords) + { + foreach (var cr in customerRoles) + { + var allowed = pr.CustomerRoles.Any(x => x.Id == cr.Id); + + if (!model.Allowed.ContainsKey(pr.SystemName)) + model.Allowed[pr.SystemName] = new Dictionary(); + + model.Allowed[pr.SystemName][cr.Id] = allowed; + } + } return View(model); } @@ -103,38 +108,38 @@ public ActionResult PermissionsSave(FormCollection form) var permissionRecords = _permissionService.GetAllPermissionRecords(); var customerRoles = _customerService.GetAllCustomerRoles(true); - foreach (var cr in customerRoles) { - string formKey = "allow_" + cr.Id; - var permissionRecordSystemNamesToRestrict = form[formKey] != null ? form[formKey].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() : new List(); + var restrictedSystemNames = form["allow_" + cr.Id.ToString()].SplitSafe(",").ToList(); - foreach (var pr in permissionRecords) + foreach (var permission in permissionRecords) { + bool allow = restrictedSystemNames.Contains(permission.SystemName); - bool allow = permissionRecordSystemNamesToRestrict.Contains(pr.SystemName); if (allow) { - if (pr.CustomerRoles.Where(x => x.Id == cr.Id).FirstOrDefault() == null) + if (!permission.CustomerRoles.Any(x => x.Id == cr.Id)) { - pr.CustomerRoles.Add(cr); - _permissionService.UpdatePermissionRecord(pr); + permission.CustomerRoles.Add(cr); + _permissionService.UpdatePermissionRecord(permission); } } else { - if (pr.CustomerRoles.Where(x => x.Id == cr.Id).FirstOrDefault() != null) + if (permission.CustomerRoles.Any(x => x.Id == cr.Id)) { - pr.CustomerRoles.Remove(cr); - _permissionService.UpdatePermissionRecord(pr); + permission.CustomerRoles.Remove(cr); + _permissionService.UpdatePermissionRecord(permission); } } } } - NotifySuccess(_localizationService.GetResource("Admin.Configuration.ACL.Updated")); + NotifySuccess(T("Admin.Configuration.ACL.Updated")); + return RedirectToAction("Permissions"); } + #endregion } } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Security/PermissionRecordModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Security/PermissionRecordModel.cs index 10840b978c..b444eaa04b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Security/PermissionRecordModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Security/PermissionRecordModel.cs @@ -6,5 +6,25 @@ public class PermissionRecordModel : ModelBase { public string Name { get; set; } public string SystemName { get; set; } - } + public string Category { get; set; } + + public string CategoryLabel + { + get + { + switch (Category) + { + case "PublicStore": + case "Standard": + return "label-success"; + + case "Plugin": + return ""; + + default: + return "label-info"; + } + } + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Security/Permissions.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Security/Permissions.cshtml index 0d02bfd4b2..743c8f9d8f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Security/Permissions.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Security/Permissions.cshtml @@ -1,6 +1,6 @@ -@model PermissionMappingModel +@using SmartStore.Utilities; +@model PermissionMappingModel @{ - //page title ViewBag.Title = T("Admin.Configuration.ACL").Text; } @using (Html.BeginForm()) @@ -11,7 +11,9 @@ @T("Admin.Configuration.ACL")
- +
@@ -19,12 +21,16 @@
@if (Model.AvailablePermissions.Count == 0) - { - No permissions defined - } - else if (Model.AvailableCustomerRoles.Count == 0) - { - No customer roles available + { +
+ @T("Admin.System.Warnings.NoPermissionsDefined") +
+ } + else if (Model.AvailableCustomerRoles.Count == 0) + { +
+ @T("Admin.System.Warnings.NoCustomerRolesDefined") +
} else { @@ -48,6 +54,7 @@ {
+ @Inflector.Titleize(pr.Category).NaIfEmpty() @pr.Name
- } From 8f8a8afb04b0a4d778f83bb8d6fb16871e4cdbca Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 4 Jan 2016 21:13:03 +0100 Subject: [PATCH 234/732] Resolves #26 Company or name in order list --- .../Controllers/OrderController.cs | 27 +- .../Models/Orders/OrderListModel.cs | 5 +- .../Models/Orders/OrderModel.cs | 3 + .../Administration/Views/Order/List.cshtml | 252 +++++++++--------- 4 files changed, 150 insertions(+), 137 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs index fd82849786..f7adf8147d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs @@ -89,8 +89,9 @@ public partial class OrderController : AdminControllerBase private readonly MeasureSettings _measureSettings; private readonly PdfSettings _pdfSettings; private readonly AddressSettings _addressSettings; + private readonly AdminAreaSettings _adminAreaSettings; - private readonly ICheckoutAttributeFormatter _checkoutAttributeFormatter; + private readonly ICheckoutAttributeFormatter _checkoutAttributeFormatter; private readonly IPdfConverter _pdfConverter; private readonly ICommonServices _services; private readonly Lazy _pictureService; @@ -124,7 +125,8 @@ public OrderController(IOrderService orderService, ICustomerActivityService customerActivityService, CatalogSettings catalogSettings, CurrencySettings currencySettings, TaxSettings taxSettings, MeasureSettings measureSettings, PdfSettings pdfSettings, AddressSettings addressSettings, - IPdfConverter pdfConverter, ICommonServices services, Lazy pictureService) + AdminAreaSettings adminAreaSettings, + IPdfConverter pdfConverter, ICommonServices services, Lazy pictureService) { this._orderService = orderService; this._orderReportService = orderReportService; @@ -167,6 +169,7 @@ public OrderController(IOrderService orderService, this._measureSettings = measureSettings; this._pdfSettings = pdfSettings; this._addressSettings = addressSettings; + this._adminAreaSettings = adminAreaSettings; this._checkoutAttributeFormatter = checkoutAttributeFormatter; _pdfConverter = pdfConverter; @@ -787,15 +790,20 @@ public ActionResult List(OrderListModel model) if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) return AccessDeniedView(); - model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList(); + var allStores = _storeService.GetAllStores(); + + model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList(); model.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList(); model.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList(); - //stores - model.AvailableStores.AddRange(_storeService.GetAllStores().ToList().ToSelectListItems()); - model.AvailableStores.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" }); + model.AvailableStores = allStores + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); - return View(model); + model.GridPageSize = _adminAreaSettings.GridPageSize; + model.StoreCount = allStores.Count; + + return View(model); } [GridAction(EnableCustomBinding = true)] @@ -823,12 +831,13 @@ public ActionResult OrderList(GridCommand command, OrderListModel model) { Id = x.Id, OrderNumber = x.GetOrderNumber(), - StoreName = store != null ? store.Name : "Unknown", + StoreName = (store != null ? store.Name : "".NaIfEmpty()), OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false), OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext), PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext), ShippingStatus = x.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext), - CustomerEmail = x.BillingAddress.Email, + CustomerName = x.BillingAddress.GetFullName(), + CustomerEmail = x.BillingAddress.Email, CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), HasNewPaymentNotification = x.HasNewPaymentNotification }; diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderListModel.cs index 2d64f5216c..8fc20246ad 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderListModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderListModel.cs @@ -54,9 +54,10 @@ public OrderListModel() [AllowHtml] public string GoDirectlyToNumber { get; set; } - + public int GridPageSize { get; set; } + public int StoreCount { get; set; } - public IList AvailableOrderStatuses { get; set; } + public IList AvailableOrderStatuses { get; set; } public IList AvailablePaymentStatuses { get; set; } public IList AvailableShippingStatuses { get; set; } public IList AvailableStores { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs index 17c2b7be51..c622929029 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs @@ -37,10 +37,13 @@ public OrderModel() //customer info [SmartResourceDisplayName("Admin.Orders.Fields.Customer")] public int CustomerId { get; set; } + + [SmartResourceDisplayName("Admin.Orders.List.CustomerName")] public string CustomerName { get; set; } [SmartResourceDisplayName("Admin.Orders.Fields.CustomerEmail")] public string CustomerEmail { get; set; } + [SmartResourceDisplayName("Admin.Orders.Fields.CustomerIP")] public string CustomerIp { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml index 3e50f7232c..47e12fdb99 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml @@ -1,10 +1,10 @@ @model OrderListModel @using Telerik.Web.Mvc.UI @{ - var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - var hideProfitReport = false; - - ViewBag.Title = T("Admin.Orders").Text; + var hideProfitReport = false; + var allString = T("Admin.Common.All").Text; + + ViewBag.Title = T("Admin.Orders").Text; } @using (Html.BeginForm()) @@ -39,39 +39,40 @@ @Html.Widget("admin_button_toolbar_after") - - - - - - - - - - - - - - - - - + +
- @Html.SmartLabelFor(model => model.StartDate) - - @Html.EditorFor(model => model.StartDate) -
- @Html.SmartLabelFor(model => model.EndDate) - - @Html.EditorFor(model => Model.EndDate) -
- @Html.SmartLabelFor(model => model.CustomerName) - - @Html.EditorFor(model => Model.CustomerName) -
- @Html.SmartLabelFor(model => model.CustomerEmail) - - @Html.EditorFor(model => Model.CustomerEmail) -
+ + + + + + + + + + + + + + + + - - - + + - - - - - - - - - + + - - - + + + + @if (Model.StoreCount > 1) + { + + + + + } - - - - - - + + + + + + + + + - - - - - -
+ @Html.SmartLabelFor(model => model.StartDate) + + @Html.EditorFor(model => model.StartDate) +
+ @Html.SmartLabelFor(model => model.EndDate) + + @Html.EditorFor(model => Model.EndDate) +
+ @Html.SmartLabelFor(model => model.CustomerName) + + @Html.EditorFor(model => Model.CustomerName) +
+ @Html.SmartLabelFor(model => model.CustomerEmail) + + @Html.EditorFor(model => Model.CustomerEmail) +
@Html.SmartLabelFor(model => model.OrderStatusIds) @@ -80,69 +81,72 @@ @Html.DropDownList("OrderStatusIds", Model.AvailableOrderStatuses, null, new { multiple = "multiple" })
- @Html.SmartLabelFor(model => model.PaymentStatusIds) - +
+ @Html.SmartLabelFor(model => model.PaymentStatusIds) + @Html.DropDownList("PaymentStatusIds", Model.AvailablePaymentStatuses, null, new { multiple = "multiple" }) -
- @Html.SmartLabelFor(model => model.ShippingStatusIds) - - @Html.DropDownList("ShippingStatusIds", Model.AvailableShippingStatuses, null, new { multiple = "multiple" }) -
- @Html.SmartLabelFor(model => model.StoreId) - - @Html.DropDownList("StoreId", Model.AvailableStores) -
- @Html.SmartLabelFor(model => model.OrderGuid) - - @Html.EditorFor(model => Model.OrderGuid) -
+ @Html.SmartLabelFor(model => model.ShippingStatusIds) + + @Html.DropDownList("ShippingStatusIds", Model.AvailableShippingStatuses, null, new { multiple = "multiple" }) +
+ @Html.SmartLabelFor(model => model.StoreId) + + @Html.DropDownListFor(model => model.StoreId, Model.AvailableStores, allString) +
- @Html.SmartLabelFor(model => model.OrderNumber) - - @Html.EditorFor(model => Model.OrderNumber) -
- @Html.SmartLabelFor(model => model.GoDirectlyToNumber) - - @Html.EditorFor(model => Model.GoDirectlyToNumber) - + @Html.SmartLabelFor(model => model.OrderGuid) + + @Html.EditorFor(model => Model.OrderGuid) +
+ @Html.SmartLabelFor(model => model.OrderNumber) + + @Html.EditorFor(model => Model.OrderNumber) +
+ @Html.SmartLabelFor(model => model.GoDirectlyToNumber) + + @Html.EditorFor(model => Model.GoDirectlyToNumber) + -
-   - - -
+ + + + +   + + + + + +

@@ -150,29 +154,31 @@ @(Html.Telerik().Grid() - .Name("orders-grid") - .ClientEvents(events => events - .OnDataBinding("onDataBinding") - .OnDataBound("onDataBound") - .OnComplete("onComplete")) - .Columns(columns => - { - columns.Bound(x => x.Id) - .ClientTemplate("") - .Title("") - .Width(50) - .HtmlAttributes(new { style = "text-align:center" }) - .HeaderHtmlAttributes(new { style = "text-align:center" }); + .Name("orders-grid") + .ClientEvents(events => events + .OnDataBinding("onDataBinding") + .OnDataBound("onDataBound") + .OnComplete("onComplete")) + .Columns(columns => + { + columns.Bound(x => x.Id) + .ClientTemplate("") + .Title("") + .Width(50) + .HtmlAttributes(new { style = "text-align:center" }) + .HeaderHtmlAttributes(new { style = "text-align:center" }); - columns.Bound(x => x.OrderNumber) + columns.Bound(x => x.OrderNumber) .ClientTemplate(@Html.LabeledOrderNumber()); - columns.Bound(x => x.OrderStatus); - columns.Bound(x => x.PaymentStatus); - columns.Bound(x => x.ShippingStatus); - columns.Bound(x => x.CustomerEmail); - columns.Bound(x => x.StoreName); - columns.Bound(x => x.CreatedOn); - columns.Bound(x => x.OrderTotal) + columns.Bound(x => x.OrderStatus); + columns.Bound(x => x.PaymentStatus); + columns.Bound(x => x.ShippingStatus); + columns.Bound(x => x.CustomerName); + columns.Bound(x => x.CustomerEmail); + columns.Bound(x => x.StoreName) + .Hidden(Model.StoreCount <= 1); + columns.Bound(x => x.CreatedOn); + columns.Bound(x => x.OrderTotal) .Width(180) .RightAlign() .FooterTemplate( @@ -184,17 +190,11 @@ (hideProfitReport ? "" : @T("Admin.Orders.Report.Profit").Text), @T("Admin.Orders.Report.Tax").Text, @T("Admin.Orders.Report.Total").Text)); - columns.Bound(x => x.Id) - .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "Edit", new { id = x.Id })) - .ClientTemplate("\">" + T("Admin.Common.View").Text + "") - .Width(50) - .Centered() - .Title(T("Admin.Common.View").Text); - }) - .Pageable(settings => settings.PageSize(gridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("OrderList", "Order")) + }) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) + .DataBinding(dataBinding => dataBinding.Ajax().Select("OrderList", "Order")) .PreserveGridState() - .EnableCustomBinding(true)) + .EnableCustomBinding(true)) From 203765ac463601a49c5d41624186d4cbd1d744d4 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 4 Jan 2016 21:18:41 +0100 Subject: [PATCH 235/732] Updated changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index ac7dd3701f..2decbbfa2f 100644 --- a/changelog.md +++ b/changelog.md @@ -65,6 +65,7 @@ * Simplified handling of SEO names * URLs are not converted to lower case anymore * Product grid sortable by name, price and created on +* #26 Display company or name in order list ### Bugfixes * #523 Redirecting to payment provider performed by core instead of plugin From 2d07edba69861ffb3ddaa25b73181f686b10e457 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 5 Jan 2016 12:33:42 +0100 Subject: [PATCH 236/732] Added inline editing of country grid. Also resolves #96 --- changelog.md | 1 + .../201512151526290_ImportFramework.cs | 4 + .../Stores/StoreMappingService.cs | 2 +- .../Controllers/SmartController.cs | 17 ++ .../Controllers/CountryController.cs | 189 ++++++++++-------- .../Models/Directory/CountryListModel.cs | 9 + .../Administration/SmartStore.Admin.csproj | 1 + .../Administration/Views/Country/List.cshtml | 112 ++++++----- 8 files changed, 203 insertions(+), 132 deletions(-) create mode 100644 src/Presentation/SmartStore.Web/Administration/Models/Directory/CountryListModel.cs diff --git a/changelog.md b/changelog.md index 2decbbfa2f..72610122db 100644 --- a/changelog.md +++ b/changelog.md @@ -66,6 +66,7 @@ * URLs are not converted to lower case anymore * Product grid sortable by name, price and created on * #26 Display company or name in order list +* Added inline editing of country grid ### Bugfixes * #523 Redirecting to payment provider performed by core instead of plugin diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 1c6e87e649..24f368ef69 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -295,6 +295,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.System.Warnings.AccessDeniedToUser", "Access denied to user #{0} '{1}' on {2}.", "Zugriffsverweigerung durch Kunde #{0} '{1}' bei {2}."); + + builder.AddOrUpdate("Admin.Configuration.Countries.CannotDeleteDueToAssociatedAddresses", + "The country cannaot be deleted because it has associated addresses.", + "Das Land kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); } } } diff --git a/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs b/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs index da282801ef..04ff4206ff 100644 --- a/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs +++ b/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs @@ -219,7 +219,7 @@ public virtual void UpdateStoreMapping(StoreMapping storeMapping) public virtual int[] GetStoresIdsWithAccess(T entity) where T : BaseEntity, IStoreMappingSupported { if (entity == null) - throw new ArgumentNullException("entity"); + return new int[0]; int entityId = entity.Id; string entityName = typeof(T).Name; diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs index 3b6cf51813..a5169e45c4 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs @@ -95,6 +95,23 @@ protected virtual void NotifyError(Exception exception, bool durable = true, boo Services.Notifier.Error(exception.ToAllMessages(), durable); } + /// + /// Pushes an error message to the notification queue that the access to a resource has been denied + /// + /// A value indicating whether a message should be persisted for the next request + /// A value indicating whether the message should be logged + protected virtual void NotifyAccessDenied(bool durable = true, bool log = false) + { + var message = T("Admin.AccessDenied.Description"); + + if (log) + { + Logger.Error(message, null, Services.WorkContext.CurrentCustomer); + } + + Services.Notifier.Error(message, durable); + } + protected virtual ActionResult RedirectToReferrer() { if (Request.UrlReferrer != null && Request.UrlReferrer.ToString().HasValue()) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs index 17300117c2..bf67df0dae 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Web.Mvc; using SmartStore.Admin.Models.Directory; -using SmartStore.Core; using SmartStore.Core.Domain.Directory; +using SmartStore.Services; using SmartStore.Services.Common; using SmartStore.Services.Directory; using SmartStore.Services.Localization; @@ -24,37 +24,31 @@ public class CountryController : AdminControllerBase private readonly ICountryService _countryService; private readonly IStateProvinceService _stateProvinceService; - private readonly ILocalizationService _localizationService; private readonly IAddressService _addressService; - private readonly IPermissionService _permissionService; private readonly ILocalizedEntityService _localizedEntityService; private readonly ILanguageService _languageService; - private readonly IStoreService _storeService; private readonly IStoreMappingService _storeMappingService; + private readonly ICommonServices _services; - #endregion + #endregion #region Constructors - public CountryController(ICountryService countryService, + public CountryController(ICountryService countryService, IStateProvinceService stateProvinceService, - ILocalizationService localizationService, IAddressService addressService, - IPermissionService permissionService, ILocalizedEntityService localizedEntityService, ILanguageService languageService, - IStoreService storeService, - IStoreMappingService storeMappingService) + IStoreMappingService storeMappingService, + ICommonServices services) { _countryService = countryService; _stateProvinceService = stateProvinceService; - _localizationService = localizationService; _addressService = addressService; - _permissionService = permissionService; _localizedEntityService = localizedEntityService; _languageService = languageService; - _storeService = storeService; _storeMappingService = storeMappingService; + _services = services; } #endregion  @@ -66,10 +60,7 @@ private void UpdateLocales(Country country, CountryModel model) { foreach (var localized in model.Locales) { - _localizedEntityService.SaveLocalizedValue(country, - x => x.Name, - localized.Name, - localized.LanguageId); + _localizedEntityService.SaveLocalizedValue(country, x => x.Name, localized.Name, localized.LanguageId); } } @@ -78,10 +69,7 @@ private void UpdateLocales(StateProvince stateProvince, StateProvinceModel model { foreach (var localized in model.Locales) { - _localizedEntityService.SaveLocalizedValue(stateProvince, - x => x.Name, - localized.Name, - localized.LanguageId); + _localizedEntityService.SaveLocalizedValue(stateProvince, x => x.Name, localized.Name, localized.LanguageId); } } @@ -91,19 +79,14 @@ private void PrepareCountryModel(CountryModel model, Country country, bool exclu if (model == null) throw new ArgumentNullException("model"); - var allStores = _storeService.GetAllStores(); + var allStores = _services.StoreService.GetAllStores(); model.AvailableStores = allStores.Select(s => s.ToModel()).ToList(); if (!excludeProperties) { - if (country != null) - model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(country); - else - model.SelectedStoreIds = new int[0]; + model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(country); } - - ViewBag.StoreCount = allStores.Count; } #endregion @@ -117,40 +100,85 @@ public ActionResult Index() public ActionResult List() { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); - var countries = _countryService.GetAllCountries(true); - var model = new GridModel - { - Data = countries.Select(x => x.ToModel()), - Total = countries.Count - }; - return View(model); + var allStores = _services.StoreService.GetAllStores(); + + var model = new CountryListModel + { + StoreCount = allStores.Count + }; + + return View(model); } [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CountryList(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) - return AccessDeniedView(); + var model = new GridModel(); - var countries = _countryService.GetAllCountries(true); - var model = new GridModel - { - Data = countries.Select(x => x.ToModel()), - Total = countries.Count - }; + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) + { + var countries = _countryService.GetAllCountries(true); + + model.Data = countries.Select(x => x.ToModel()); + model.Total = countries.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { Data = model }; } - - public ActionResult Create() + + [GridAction(EnableCustomBinding = true)] + public ActionResult CountryUpdate(CountryModel model, GridCommand command) + { + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } + + var country = _countryService.GetCountryById(model.Id); + + country = model.ToEntity(country); + _countryService.UpdateCountry(country); + } + + return CountryList(command); + } + + [GridAction(EnableCustomBinding = true)] + public ActionResult CountryDelete(int id, GridCommand command) + { + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) + { + if (_addressService.GetAddressTotalByCountryId(id) > 0) + { + return Content(T("Admin.Configuration.Countries.CannotDeleteDueToAssociatedAddresses")); + } + + var country = _countryService.GetCountryById(id); + + _countryService.DeleteCountry(country); + } + + return CountryList(command); + } + + public ActionResult Create() { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var model = new CountryModel(); @@ -169,7 +197,7 @@ public ActionResult Create() [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Create(CountryModel model, bool continueEditing) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); if (ModelState.IsValid) @@ -181,7 +209,7 @@ public ActionResult Create(CountryModel model, bool continueEditing) _storeMappingService.SaveStoreMappings(country, model.SelectedStoreIds); - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Countries.Added")); + NotifySuccess(T("Admin.Configuration.Countries.Added")); return continueEditing ? RedirectToAction("Edit", new { id = country.Id }) : RedirectToAction("List"); } @@ -193,7 +221,7 @@ public ActionResult Create(CountryModel model, bool continueEditing) public ActionResult Edit(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var country = _countryService.GetCountryById(id); @@ -215,7 +243,7 @@ public ActionResult Edit(int id) [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Edit(CountryModel model, bool continueEditing) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var country = _countryService.GetCountryById(model.Id); @@ -231,7 +259,7 @@ public ActionResult Edit(CountryModel model, bool continueEditing) _storeMappingService.SaveStoreMappings(country, model.SelectedStoreIds); - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Countries.Updated")); + NotifySuccess(T("Admin.Configuration.Countries.Updated")); return continueEditing ? RedirectToAction("Edit", new { id = country.Id }) : RedirectToAction("List"); } @@ -244,7 +272,7 @@ public ActionResult Edit(CountryModel model, bool continueEditing) [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var country = _countryService.GetCountryById(id); @@ -254,11 +282,12 @@ public ActionResult DeleteConfirmed(int id) try { if (_addressService.GetAddressTotalByCountryId(country.Id) > 0) - throw new SmartException("The country can't be deleted. It has associated addresses"); + throw new SmartException(T("Admin.Configuration.Countries.CannotDeleteDueToAssociatedAddresses")); _countryService.DeleteCountry(country); - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Countries.Deleted")); + NotifySuccess(T("Admin.Configuration.Countries.Deleted")); + return RedirectToAction("List"); } catch (Exception exc) @@ -268,7 +297,6 @@ public ActionResult DeleteConfirmed(int id) } } - #endregion #region States / provinces @@ -276,7 +304,7 @@ public ActionResult DeleteConfirmed(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult States(int countryId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var states = _stateProvinceService.GetStateProvincesByCountryId(countryId, true) @@ -287,30 +315,33 @@ public ActionResult States(int countryId, GridCommand command) Data = states, Total = states.Count() }; + return new JsonResult { Data = model }; } - //create public ActionResult StateCreatePopup(int countryId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); - var model = new StateProvinceModel(); - model.CountryId = countryId; - //locales + var model = new StateProvinceModel + { + CountryId = countryId + }; + AddLocales(_languageService, model.Locales); + return View(model); } [HttpPost] public ActionResult StateCreatePopup(string btnId, string formId, StateProvinceModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var country = _countryService.GetCountryById(model.CountryId); @@ -327,6 +358,7 @@ public ActionResult StateCreatePopup(string btnId, string formId, StateProvinceM ViewBag.RefreshPage = true; ViewBag.btnId = btnId; ViewBag.formId = formId; + return View(model); } @@ -337,16 +369,15 @@ public ActionResult StateCreatePopup(string btnId, string formId, StateProvinceM //edit public ActionResult StateEditPopup(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var sp = _stateProvinceService.GetStateProvinceById(id); if (sp == null) - //No state found with the specified id return RedirectToAction("List"); var model = sp.ToModel(); - //locales + AddLocales(_languageService, model.Locales, (locale, languageId) => { locale.Name = sp.GetLocalized(x => x.Name, languageId, false, false); @@ -358,12 +389,11 @@ public ActionResult StateEditPopup(int id) [HttpPost] public ActionResult StateEditPopup(string btnId, string formId, StateProvinceModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var sp = _stateProvinceService.GetStateProvinceById(model.Id); if (sp == null) - //No state found with the specified id return RedirectToAction("List"); if (ModelState.IsValid) @@ -376,6 +406,7 @@ public ActionResult StateEditPopup(string btnId, string formId, StateProvinceMod ViewBag.RefreshPage = true; ViewBag.btnId = btnId; ViewBag.formId = formId; + return View(model); } @@ -386,7 +417,7 @@ public ActionResult StateEditPopup(string btnId, string formId, StateProvinceMod [GridAction(EnableCustomBinding = true)] public ActionResult StateDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCountries)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) return AccessDeniedView(); var state = _stateProvinceService.GetStateProvinceById(id); @@ -394,35 +425,31 @@ public ActionResult StateDelete(int id, GridCommand command) throw new ArgumentException("No state found with the specified id"); if (_addressService.GetAddressTotalByStateProvinceId(state.Id) > 0) - return Content(_localizationService.GetResource("Admin.Configuration.Countries.States.CantDeleteWithAddresses")); + return Content(T("Admin.Configuration.Countries.States.CantDeleteWithAddresses")); int countryId = state.CountryId; _stateProvinceService.DeleteStateProvince(state); - return States(countryId, command); } [AcceptVerbs(HttpVerbs.Get)] public ActionResult GetStatesByCountryId(string countryId, bool? addEmptyStateIfRequired, bool? addAsterisk) { - //permission validation is not required here - + // permission validation is not required here // This action method gets called via an ajax request - int cid = 0; - int.TryParse(countryId, out cid); - /*if (String.IsNullOrEmpty(countryId)) - throw new ArgumentNullException("countryId");*/ + var country = _countryService.GetCountryById(countryId.ToInt()); - var country = _countryService.GetCountryById(cid /* Convert.ToInt32(countryId) */); var states = country != null ? _stateProvinceService.GetStateProvincesByCountryId(country.Id, true).ToList() : new List(); - var result = (from s in states - select new { id = s.Id, name = s.Name }).ToList(); + var result = (from s in states select new { id = s.Id, name = s.Name }).ToList(); + if (addEmptyStateIfRequired.HasValue && addEmptyStateIfRequired.Value && result.Count == 0) - result.Insert(0, new { id = 0, name = _localizationService.GetResource("Admin.Address.OtherNonUS") }); + result.Insert(0, new { id = 0, name = T("Admin.Address.OtherNonUS").Text }); + if (addAsterisk.HasValue && addAsterisk.Value) result.Insert(0, new { id = 0, name = "*" }); + return Json(result, JsonRequestBehavior.AllowGet); } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Directory/CountryListModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Directory/CountryListModel.cs new file mode 100644 index 0000000000..ece27d8805 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Administration/Models/Directory/CountryListModel.cs @@ -0,0 +1,9 @@ +using SmartStore.Web.Framework.Modelling; + +namespace SmartStore.Admin.Models.Directory +{ + public class CountryListModel : ModelBase + { + public int StoreCount { get; set; } + } +} \ 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 8aa8fd5f3f..c7ca89c0f8 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -258,6 +258,7 @@ + diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Country/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Country/List.cshtml index 5285bcd4d5..833e3badee 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Country/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Country/List.cshtml @@ -1,4 +1,4 @@ -@model Telerik.Web.Mvc.GridModel +@model CountryListModel @using Telerik.Web.Mvc.UI @{ ViewBag.Title = T("Admin.Configuration.Countries").Text; @@ -12,62 +12,74 @@  @T("Admin.Common.AddNew") +
@(Html.Telerik().Grid() - .Name("countries-grid") - .BindTo(Model.Data) - .Columns(columns => - { - columns.Bound(x => x.Name) - .Width(250) - .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) - .ClientTemplate("<#= Name #>"); - columns.Bound(x => x.AllowsBilling) - .Width(50) - .Template(item => @Html.SymbolForBool(item.AllowsBilling)) - .ClientTemplate(@Html.SymbolForBool("AllowsBilling")) - .Centered(); - columns.Bound(x => x.AllowsShipping) - .Width(50) - .Template(item => @Html.SymbolForBool(item.AllowsShipping)) - .ClientTemplate(@Html.SymbolForBool("AllowsShipping")) - .Centered(); - columns.Bound(x => x.TwoLetterIsoCode) - .Width(50) - .Centered(); - columns.Bound(x => x.ThreeLetterIsoCode) - .Width(50) - .Centered(); - columns.Bound(x => x.NumericIsoCode) - .Width(50) - .Centered(); - columns.Bound(x => x.SubjectToVat) - .Width(50) - .Template(item => @Html.SymbolForBool(item.SubjectToVat)) - .ClientTemplate(@Html.SymbolForBool("SubjectToVat")) - .Centered(); - columns.Bound(x => x.NumberOfStates) - .Width(50) - .Centered(); - columns.Bound(x => x.DisplayOrder) - .Width(50) - .Centered(); + .Name("countries-grid") + .DataKeys(x => + { + x.Add(y => y.Id).RouteKey("Id"); + }) + .Editable(x => + { + x.Mode(GridEditMode.InLine); + }) + .Columns(columns => + { + columns.Bound(x => x.Name) + .ClientTemplate("<#= Name #>"); + columns.Bound(x => x.AllowsBilling) + .Width(50) + .ClientTemplate(@Html.SymbolForBool("AllowsBilling")) + .Centered(); + columns.Bound(x => x.AllowsShipping) + .ClientTemplate(@Html.SymbolForBool("AllowsShipping")) + .Centered(); + columns.Bound(x => x.TwoLetterIsoCode) + .Centered(); + columns.Bound(x => x.ThreeLetterIsoCode) + .Centered(); + columns.Bound(x => x.NumericIsoCode) + .Centered(); + columns.Bound(x => x.SubjectToVat) + .ClientTemplate(@Html.SymbolForBool("SubjectToVat")) + .Centered(); + columns.Bound(x => x.NumberOfStates) + .ReadOnly() + .Centered(); + columns.Bound(x => x.DisplayOrder) + .Centered(); columns.Bound(x => x.LimitedToStores) - .Template(item => @Html.SymbolForBool(item.LimitedToStores)) .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) .Centered() - .Width(100) - .Hidden(ViewBag.StoreCount <= 1); - columns.Bound(x => x.Published) - .Width(50) - .Template(item => @Html.SymbolForBool(item.Published)) - .ClientTemplate(@Html.SymbolForBool("Published")) - .Centered(); - }) - .DataBinding(dataBinding => dataBinding.Ajax().Select("CountryList", "Country")) - .EnableCustomBinding(true)) + .ReadOnly() + .Hidden(Model.StoreCount <= 1); + columns.Bound(x => x.Published) + .ClientTemplate(@Html.SymbolForBool("Published")) + .Centered(); + columns.Command(commands => + { + commands.Edit().Localize(T); + commands.Delete().Localize(T); + }); + }) + .DataBinding(dataBinding => dataBinding.Ajax() + .Select("CountryList", "Country") + .Update("CountryUpdate", "Country") + .Delete("CountryDelete", "Country") + ) + .ClientEvents(x => x.OnError("CountryGrid_onError")) + .PreserveGridState() + .EnableCustomBinding(true))
+ + From 718323ce9f06773c37a7bbd5e486c0da43fd805f Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 5 Jan 2016 14:03:26 +0100 Subject: [PATCH 237/732] Product import: Store mappings were not applied when inserting new records --- changelog.md | 1 + .../Catalog/Importer/ProductImporter.cs | 38 ++++++++++++++++--- .../Controllers/SmartController.cs | 3 +- .../Controllers/ImportController.cs | 2 +- .../Views/Import/_ColumnMappings.cshtml | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 72610122db..0ca5d818fc 100644 --- a/changelog.md +++ b/changelog.md @@ -84,6 +84,7 @@ * #805 Product filter is reset if 'product sorting' or 'view mode' or 'amount of displayed products per page' is changed * Hide link to a topic page if it is limited to stores * #829 Activity log: Searching by customer email out of function +* Product import: Store mappings were not applied when inserting new records ## SmartStore.NET 2.2.2 diff --git a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs index b115a270eb..c4321349f6 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs @@ -367,6 +367,21 @@ private int ProcessSlugs(IImportExecuteContext context, ImportRow[] bat return _urlRecordRepository.Context.SaveChanges(); } + private void ProcessStoreMappings(IImportExecuteContext context, ImportRow[] batch) + { + foreach (var row in batch) + { + var limitedToStore = row.GetDataValue("LimitedToStores"); + + if (limitedToStore) + { + var storeIds = row.GetDataValue>("StoreIds"); + + _storeMappingService.SaveStoreMappings(row.Entity, storeIds == null ? new int[0] : storeIds.ToArray()); + } + } + } + private int ProcessProducts(IImportExecuteContext context, ImportRow[] batch, DateTime utcNow) { _productRepository.AutoCommitEnabled = true; @@ -497,12 +512,6 @@ private int ProcessProducts(IImportExecuteContext context, ImportRow[] row.SetProperty(context.Result, product, (x) => x.AvailableEndDateTimeUtc); row.SetProperty(context.Result, product, (x) => x.LimitedToStores); - var storeIds = row.GetDataValue>("StoreIds"); - if (storeIds != null && storeIds.Any()) - { - _storeMappingService.SaveStoreMappings(product, storeIds.ToArray()); - } - row.SetProperty(context.Result, product, (x) => x.CreatedOnUtc, utcNow); product.UpdatedOnUtc = utcNow; @@ -590,6 +599,23 @@ public void Execute(IImportExecuteContext context) } } + if (segmenter.HasColumn("LimitedToStores") && segmenter.HasColumn("StoreIds")) + { + try + { + _productRepository.Context.AutoDetectChangesEnabled = true; + ProcessStoreMappings(context, batch); + } + catch (Exception exception) + { + context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessStoreMappings"); + } + finally + { + _productRepository.Context.AutoDetectChangesEnabled = false; + } + } + // =========================================================================== // 3.) Import Localizations // =========================================================================== diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs index a5169e45c4..2debc94ede 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using System.Web; using System.Web.Mvc; using SmartStore.Core.Localization; using SmartStore.Core.Logging; @@ -92,7 +93,7 @@ protected virtual void NotifyError(Exception exception, bool durable = true, boo LogException(exception); } - Services.Notifier.Error(exception.ToAllMessages(), durable); + Services.Notifier.Error(HttpUtility.HtmlEncode(exception.ToAllMessages()), durable); } /// diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs index 9c2d6b565f..c56638c2a1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs @@ -125,7 +125,7 @@ private void PrepareColumnMappingModels(ImportProfile profile, ImportProfileMode } catch (Exception exception) { - NotifyError(exception); + NotifyError(exception, true, false); } } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml index 4dc8045232..9e15de2aef 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml @@ -1,6 +1,6 @@ @using SmartStore.Admin.Models.DataExchange; @model ImportProfileModel -@if (Model.ColumnMappings.Any()) +@if (Model.ColumnMappings != null && Model.ColumnMappings.Any()) {

 

From 9966961092d437f46883a308a2feea4f3dd75f21 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 5 Jan 2016 14:08:19 +0100 Subject: [PATCH 238/732] Dito new category import --- .../Catalog/Importer/CategoryImporter.cs | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs b/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs index e0e5a2bdf2..08e43b0448 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs @@ -178,6 +178,21 @@ private int ProcessPictures(IImportExecuteContext context, return num; } + private void ProcessStoreMappings(IImportExecuteContext context, ImportRow[] batch) + { + foreach (var row in batch) + { + var limitedToStore = row.GetDataValue("LimitedToStores"); + + if (limitedToStore) + { + var storeIds = row.GetDataValue>("StoreIds"); + + _storeMappingService.SaveStoreMappings(row.Entity, storeIds == null ? new int[0] : storeIds.ToArray()); + } + } + } + private int ProcessCategories(IImportExecuteContext context, ImportRow[] batch, DateTime utcNow, @@ -249,12 +264,6 @@ private int ProcessCategories(IImportExecuteContext context, category.CategoryTemplateId = templateId; } - var storeIds = row.GetDataValue>("StoreIds"); - if (storeIds != null && storeIds.Any()) - { - _storeMappingService.SaveStoreMappings(category, storeIds.ToArray()); - } - row.SetProperty(context.Result, category, (x) => x.CreatedOnUtc, utcNow); category.UpdatedOnUtc = utcNow; @@ -355,6 +364,24 @@ public void Execute(IImportExecuteContext context) } } + // process store mappings + if (segmenter.HasColumn("LimitedToStores") && segmenter.HasColumn("StoreIds")) + { + try + { + _categoryRepository.Context.AutoDetectChangesEnabled = true; + ProcessStoreMappings(context, batch); + } + catch (Exception exception) + { + context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessStoreMappings"); + } + finally + { + _categoryRepository.Context.AutoDetectChangesEnabled = false; + } + } + // process pictures if (srcToDestId.Any() && segmenter.HasColumn("PictureThumbPath")) { From ef6da8abe065e2b65b1740525ec20310503b8eb5 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Tue, 5 Jan 2016 18:11:26 +0100 Subject: [PATCH 239/732] Faulty permission handling in ajax grid actions (no message, infinite loading icon)(part 1) --- changelog.md | 1 + .../201512151526290_ImportFramework.cs | 6 +- .../Controllers/SmartController.cs | 2 +- .../Controllers/AffiliateController.cs | 121 ++++++------ .../Controllers/BlogController.cs | 135 +++++++------ .../Controllers/CampaignController.cs | 35 ++-- .../Controllers/CategoryController.cs | 168 +++++++++-------- .../CheckoutAttributeController.cs | 90 ++++----- .../Controllers/CommonController.cs | 177 +++++++++--------- .../Controllers/CountryController.cs | 43 +++-- .../Controllers/CurrencyController.cs | 25 ++- .../Controllers/CustomerController.cs | 130 +++++++------ .../Controllers/CustomerRoleController.cs | 25 ++- .../Controllers/DiscountController.cs | 87 ++++----- .../Controllers/EmailAccountController.cs | 34 ++-- .../Controllers/ExportController.cs | 12 +- .../Controllers/GiftCardController.cs | 106 ++++++----- 17 files changed, 664 insertions(+), 533 deletions(-) diff --git a/changelog.md b/changelog.md index 0ca5d818fc..2308726b85 100644 --- a/changelog.md +++ b/changelog.md @@ -85,6 +85,7 @@ * Hide link to a topic page if it is limited to stores * #829 Activity log: Searching by customer email out of function * Product import: Store mappings were not applied when inserting new records +* Faulty permission handling in ajax grid actions (no message, infinite loading icon) ## SmartStore.NET 2.2.2 diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 24f368ef69..7de4cc953b 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -297,8 +297,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Zugriffsverweigerung durch Kunde #{0} '{1}' bei {2}."); builder.AddOrUpdate("Admin.Configuration.Countries.CannotDeleteDueToAssociatedAddresses", - "The country cannaot be deleted because it has associated addresses.", + "The country cannot be deleted because it has associated addresses.", "Das Land kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); + + builder.AddOrUpdate("Admin.Configuration.Countries.States.CantDeleteWithAddresses", + "The state\\province cannot be deleted because it has associated addresses.", + "Das Bundesland\\Region kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs index 2debc94ede..b5db948b54 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs @@ -101,7 +101,7 @@ protected virtual void NotifyError(Exception exception, bool durable = true, boo ///
/// A value indicating whether a message should be persisted for the next request /// A value indicating whether the message should be logged - protected virtual void NotifyAccessDenied(bool durable = true, bool log = false) + protected virtual void NotifyAccessDenied(bool durable = true, bool log = true) { var message = T("Admin.AccessDenied.Description"); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/AffiliateController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/AffiliateController.cs index e98628b496..cf1e2b3c87 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/AffiliateController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/AffiliateController.cs @@ -24,7 +24,7 @@ namespace SmartStore.Admin.Controllers { - [AdminAuthorize] + [AdminAuthorize] public partial class AffiliateController : AdminControllerBase { #region Fields @@ -156,23 +156,31 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) - return AccessDeniedView(); + var model = new GridModel(); - var affiliates = _affiliateService.GetAllAffiliates(true); - var gridModel = new GridModel - { - Data = affiliates.PagedForCommand(command).Select(x => - { - var m = new AffiliateModel(); - PrepareAffiliateModel(m, x, false); - return m; - }), - Total = affiliates.Count, - }; - return new JsonResult + if (_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) + { + var affiliates = _affiliateService.GetAllAffiliates(true); + + model.Data = affiliates.PagedForCommand(command).Select(x => + { + var m = new AffiliateModel(); + PrepareAffiliateModel(m, x, false); + return m; + }); + + model.Total = affiliates.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + + return new JsonResult { - Data = gridModel + Data = model }; } @@ -287,31 +295,34 @@ public ActionResult Delete(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AffiliatedOrderList(int affiliateId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) - return AccessDeniedView(); + var model = new GridModel(); - var affiliate = _affiliateService.GetAffiliateById(affiliateId); - if (affiliate == null) - throw new ArgumentException("No affiliate found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) + { + var affiliate = _affiliateService.GetAffiliateById(affiliateId); + var orders = _orderService.GetAllOrders(affiliate.Id, command.Page - 1, command.PageSize); + + model.Data = orders.Select(order => + { + var orderModel = new AffiliateModel.AffiliatedOrderModel(); + orderModel.Id = order.Id; + orderModel.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext); + orderModel.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext); + orderModel.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext); + orderModel.OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false); + orderModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc); + return orderModel; + }); + model.Total = orders.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); - var orders = _orderService.GetAllOrders(affiliate.Id, command.Page - 1, command.PageSize); - var model = new GridModel - { - Data = orders.Select(order => - { - var orderModel = new AffiliateModel.AffiliatedOrderModel(); - orderModel.Id = order.Id; - orderModel.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext); - orderModel.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext); - orderModel.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext); - orderModel.OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false); - orderModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc); - return orderModel; - }), - Total = orders.TotalCount - }; + NotifyAccessDenied(); + } - return new JsonResult + return new JsonResult { Data = model }; @@ -320,19 +331,16 @@ public ActionResult AffiliatedOrderList(int affiliateId, GridCommand command) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AffiliatedCustomerList(int affiliateId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) - return AccessDeniedView(); + var model = new GridModel(); - var affiliate = _affiliateService.GetAffiliateById(affiliateId); - if (affiliate == null) - throw new ArgumentException("No affiliate found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageAffiliates)) + { + var affiliate = _affiliateService.GetAffiliateById(affiliateId); + var customers = _customerService.GetAllCustomers(affiliate.Id, command.Page - 1, command.PageSize); - var customers = _customerService.GetAllCustomers(affiliate.Id, command.Page - 1, command.PageSize); - var model = new GridModel - { - Data = customers.Select(customer => - { - var customerModel = new AffiliateModel.AffiliatedCustomerModel + model.Data = customers.Select(customer => + { + var customerModel = new AffiliateModel.AffiliatedCustomerModel { Id = customer.Id, Email = customer.Email, @@ -340,16 +348,23 @@ public ActionResult AffiliatedCustomerList(int affiliateId, GridCommand command) FullName = customer.GetFullName() }; - return customerModel; - }), - Total = customers.TotalCount - }; + return customerModel; + }); + model.Total = customers.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { Data = model }; } + #endregion } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs index 98d4a868c4..f0883628b1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/BlogController.cs @@ -105,29 +105,37 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) + { + var blogPosts = _blogService.GetAllBlogPosts(0, 0, null, null, command.Page - 1, command.PageSize, true); + + model.Data = blogPosts.Select(x => + { + var m = x.ToModel(); + if (x.StartDateUtc.HasValue) + m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); + if (x.EndDateUtc.HasValue) + m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); + m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + m.LanguageName = x.Language.Name; + m.Comments = x.ApprovedCommentCount + x.NotApprovedCommentCount; + return m; + }); + + model.Total = blogPosts.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var blogPosts = _blogService.GetAllBlogPosts(0, 0, null, null, command.Page - 1, command.PageSize, true); - var gridModel = new GridModel - { - Data = blogPosts.Select(x => - { - var m = x.ToModel(); - if (x.StartDateUtc.HasValue) - m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); - if (x.EndDateUtc.HasValue) - m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); - m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - m.LanguageName = x.Language.Name; - m.Comments = x.ApprovedCommentCount + x.NotApprovedCommentCount; - return m; - }), - Total = blogPosts.TotalCount - }; return new JsonResult { - Data = gridModel + Data = model }; } @@ -269,71 +277,76 @@ public ActionResult Comments(int? filterByBlogPostId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Comments(int? filterByBlogPostId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) - return AccessDeniedView(); + var model = new GridModel(); - IList comments; - if (filterByBlogPostId.HasValue) - { - //filter comments by blog - var blogPost = _blogService.GetBlogPostById(filterByBlogPostId.Value); - comments = blogPost.BlogComments.OrderBy(bc => bc.CreatedOnUtc).ToList(); - } - else - { - //load all blog comments - comments = _customerContentService.GetAllCustomerContent(0, null); - } + if (_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) + { + IList comments; + if (filterByBlogPostId.HasValue) + { + //filter comments by blog + var blogPost = _blogService.GetBlogPostById(filterByBlogPostId.Value); + comments = blogPost.BlogComments.OrderBy(bc => bc.CreatedOnUtc).ToList(); + } + else + { + //load all blog comments + comments = _customerContentService.GetAllCustomerContent(0, null); + } - var gridModel = new GridModel - { - Data = comments.PagedForCommand(command).Select(blogComment => - { - var commentModel = new BlogCommentModel(); + model.Data = comments.PagedForCommand(command).Select(blogComment => + { + var commentModel = new BlogCommentModel(); var customer = _customerService.GetCustomerById(blogComment.CustomerId); - commentModel.Id = blogComment.Id; - commentModel.BlogPostId = blogComment.BlogPostId; - commentModel.BlogPostTitle = blogComment.BlogPost.Title; - commentModel.CustomerId = blogComment.CustomerId; - commentModel.IpAddress = blogComment.IpAddress; - commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(blogComment.CreatedOnUtc, DateTimeKind.Utc); - commentModel.Comment = Core.Html.HtmlUtils.FormatText(blogComment.CommentText, false, true, false, false, false, false); + commentModel.Id = blogComment.Id; + commentModel.BlogPostId = blogComment.BlogPostId; + commentModel.BlogPostTitle = blogComment.BlogPost.Title; + commentModel.CustomerId = blogComment.CustomerId; + commentModel.IpAddress = blogComment.IpAddress; + commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(blogComment.CreatedOnUtc, DateTimeKind.Utc); + commentModel.Comment = Core.Html.HtmlUtils.FormatText(blogComment.CommentText, false, true, false, false, false, false); if (customer == null) commentModel.CustomerName = "".NaIfEmpty(); else commentModel.CustomerName = customer.GetFullName(); - return commentModel; - }), - Total = comments.Count, - }; + return commentModel; + }); + + model.Total = comments.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { - Data = gridModel + Data = model }; } [GridAction(EnableCustomBinding = true)] public ActionResult CommentDelete(int? filterByBlogPostId, int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageBlog)) + { + var comment = _customerContentService.GetCustomerContentById(id) as BlogComment; - var comment = _customerContentService.GetCustomerContentById(id) as BlogComment; - if (comment == null) - throw new ArgumentException("No comment found with the specified id"); + var blogPost = comment.BlogPost; + _customerContentService.DeleteCustomerContent(comment); - var blogPost = comment.BlogPost; - _customerContentService.DeleteCustomerContent(comment); - //update totals - _blogService.UpdateCommentTotals(blogPost); + //update totals + _blogService.UpdateCommentTotals(blogPost); + } return Comments(filterByBlogPostId, command); } - #endregion } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CampaignController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CampaignController.cs index 7b3b290fd9..51f11cd40c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CampaignController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CampaignController.cs @@ -88,24 +88,31 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCampaigns)) - return AccessDeniedView(); + var model = new GridModel(); - var campaigns = _campaignService.GetAllCampaigns(); - var gridModel = new GridModel - { - Data = campaigns.Select(x => - { - var model = x.ToModel(); - model.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - return model; - }), - Total = campaigns.Count - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageCampaigns)) + { + var campaigns = _campaignService.GetAllCampaigns(); + + model.Data = campaigns.Select(x => + { + var m = x.ToModel(); + m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + return m; + }); + + model.Total = campaigns.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { - Data = gridModel + Data = model }; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs index c8b714dfab..05822a9c17 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs @@ -285,24 +285,30 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, CategoryListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var categories = _categoryService.GetAllCategories(model.SearchCategoryName, command.Page - 1, command.PageSize, true, model.SearchAlias, true, false, model.SearchStoreId); - var mappedCategories = categories.ToDictionary(x => x.Id); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var categories = _categoryService.GetAllCategories(model.SearchCategoryName, command.Page - 1, command.PageSize, true, model.SearchAlias, true, false, model.SearchStoreId); + var mappedCategories = categories.ToDictionary(x => x.Id); - var gridModel = new GridModel - { - Data = categories.Select(x => - { - var categoryModel = x.ToModel(); - categoryModel.Breadcrumb = x.GetCategoryBreadCrumb(_categoryService, mappedCategories); - return categoryModel; - }), - Total = categories.TotalCount - }; + gridModel.Data = categories.Select(x => + { + var categoryModel = x.ToModel(); + categoryModel.Breadcrumb = x.GetCategoryBreadCrumb(_categoryService, mappedCategories); + return categoryModel; + }); + + gridModel.Total = categories.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); - return new JsonResult + NotifyAccessDenied(); + } + + return new JsonResult { Data = gridModel }; @@ -758,17 +764,16 @@ public ActionResult Delete(int id, string deleteType) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductList(GridCommand command, int categoryId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var productCategories = _categoryService.GetProductCategoriesByCategoryId(categoryId, command.Page - 1, command.PageSize, true); + var model = new GridModel(); - var products = _productService.GetProductsByIds(productCategories.Select(x => x.ProductId).ToArray()); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productCategories = _categoryService.GetProductCategoriesByCategoryId(categoryId, command.Page - 1, command.PageSize, true); - var model = new GridModel - { - Data = productCategories.Select(x => - { + var products = _productService.GetProductsByIds(productCategories.Select(x => x.ProductId).ToArray()); + + model.Data = productCategories.Select(x => + { var productModel = new CategoryModel.CategoryProductModel { Id = x.Id, @@ -788,13 +793,20 @@ public ActionResult ProductList(GridCommand command, int categoryId) productModel.ProductTypeLabelHint = product.ProductTypeLabelHint; productModel.Published = product.Published; } - + return productModel; - }), - Total = productCategories.TotalCount - }; + }); + + model.Total = productCategories.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); - return new JsonResult + NotifyAccessDenied(); + } + + return new JsonResult { Data = model }; @@ -803,34 +815,32 @@ public ActionResult ProductList(GridCommand command, int categoryId) [GridAction(EnableCustomBinding = true)] public ActionResult ProductUpdate(GridCommand command, CategoryModel.CategoryProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productCategory = _categoryService.GetProductCategoryById(model.Id); - var productCategory = _categoryService.GetProductCategoryById(model.Id); - if (productCategory == null) - throw new ArgumentException("No product category mapping found with the specified id"); + productCategory.IsFeaturedProduct = model.IsFeaturedProduct; + productCategory.DisplayOrder = model.DisplayOrder1; - productCategory.IsFeaturedProduct = model.IsFeaturedProduct; - productCategory.DisplayOrder = model.DisplayOrder1; - _categoryService.UpdateProductCategory(productCategory); + _categoryService.UpdateProductCategory(productCategory); + } - return ProductList(command, productCategory.CategoryId); + return ProductList(command, model.Id); } [GridAction(EnableCustomBinding = true)] public ActionResult ProductDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productCategory = _categoryService.GetProductCategoryById(id); - var productCategory = _categoryService.GetProductCategoryById(id); - if (productCategory == null) - throw new ArgumentException("No product category mapping found with the specified id"); + var categoryId = productCategory.CategoryId; - var categoryId = productCategory.CategoryId; - _categoryService.DeleteProductCategory(productCategory); + _categoryService.DeleteProductCategory(productCategory); + } - return ProductList(command, categoryId); + return ProductList(command, id); } public ActionResult ProductAddPopup(int categoryId) @@ -838,7 +848,6 @@ public ActionResult ProductAddPopup(int categoryId) if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); - var ctx = new ProductSearchContext(); ctx.LanguageId = _workContext.WorkingLanguage.Id; ctx.OrderBy = ProductSortingEnum.Position; @@ -864,16 +873,18 @@ public ActionResult ProductAddPopup(int categoryId) var mappedCategories = allCategories.ToDictionary(x => x.Id); foreach (var c in allCategories) { - model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService, mappedCategories), Value = c.Id.ToString() }); + model.AvailableCategories.Add(new SelectListItem { Text = c.GetCategoryNameWithPrefix(_categoryService, mappedCategories), Value = c.Id.ToString() }); } - //manufacturers - foreach (var m in _manufacturerService.GetAllManufacturers(true)) - model.AvailableManufacturers.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() }); + //manufacturers + foreach (var m in _manufacturerService.GetAllManufacturers(true)) + { + model.AvailableManufacturers.Add(new SelectListItem { Text = m.Name, Value = m.Id.ToString() }); + } //product types model.AvailableProductTypes = ProductType.SimpleProduct.ToSelectList(false).ToList(); - model.AvailableProductTypes.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" }); + model.AvailableProductTypes.Insert(0, new SelectListItem { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" }); return View(model); } @@ -881,35 +892,44 @@ public ActionResult ProductAddPopup(int categoryId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductAddPopupList(GridCommand command, CategoryModel.AddCategoryProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var gridModel = new GridModel(); - var ctx = new ProductSearchContext(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var ctx = new ProductSearchContext(); - if (model.SearchCategoryId > 0) - ctx.CategoryIds.Add(model.SearchCategoryId); + if (model.SearchCategoryId > 0) + ctx.CategoryIds.Add(model.SearchCategoryId); - ctx.ManufacturerId = model.SearchManufacturerId; - ctx.Keywords = model.SearchProductName; - ctx.LanguageId = _workContext.WorkingLanguage.Id; - ctx.OrderBy = ProductSortingEnum.Position; - ctx.PageIndex = command.Page - 1; - ctx.PageSize = command.PageSize; - ctx.ShowHidden = true; - ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; + ctx.ManufacturerId = model.SearchManufacturerId; + ctx.Keywords = model.SearchProductName; + ctx.LanguageId = _workContext.WorkingLanguage.Id; + ctx.OrderBy = ProductSortingEnum.Position; + ctx.PageIndex = command.Page - 1; + ctx.PageSize = command.PageSize; + ctx.ShowHidden = true; + ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; - var products = _productService.SearchProducts(ctx); - gridModel.Data = products.Select(x => + var products = _productService.SearchProducts(ctx); + + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); - return productModel; - }); + NotifyAccessDenied(); + } - gridModel.Total = products.TotalCount; - return new JsonResult + return new JsonResult { Data = gridModel }; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CheckoutAttributeController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CheckoutAttributeController.cs index 78170d9674..4ac32d8493 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CheckoutAttributeController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CheckoutAttributeController.cs @@ -104,7 +104,6 @@ private void PrepareCheckoutAttributeModel(CheckoutAttributeModel model, Checkou #region Checkout attributes - //list public ActionResult Index() { return RedirectToAction("List"); @@ -121,23 +120,31 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var checkoutAttributes = _checkoutAttributeService.GetAllCheckoutAttributes(true); - var gridModel = new GridModel - { - Data = checkoutAttributes.Select(x => - { - var caModel = x.ToModel(); - caModel.AttributeControlTypeName = x.AttributeControlType.GetLocalizedEnum(_services.Localization, _services.WorkContext); - return caModel; - }), - Total = checkoutAttributes.Count() - }; - return new JsonResult + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var checkoutAttributes = _checkoutAttributeService.GetAllCheckoutAttributes(true); + + model.Data = checkoutAttributes.Select(x => + { + var caModel = x.ToModel(); + caModel.AttributeControlTypeName = x.AttributeControlType.GetLocalizedEnum(_services.Localization, _services.WorkContext); + return caModel; + }); + + model.Total = checkoutAttributes.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + + return new JsonResult { - Data = gridModel + Data = model }; } @@ -254,31 +261,29 @@ public ActionResult DeleteConfirmed(int id) #region Checkout attribute values - //list [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ValueList(int checkoutAttributeId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var values = _checkoutAttributeService.GetCheckoutAttributeValues(checkoutAttributeId); - var gridModel = new GridModel - { - Data = values.Select(x => - { - var model = x.ToModel(); - //locales - //AddLocales(_languageService, model.Locales, (locale, languageId) => - //{ - // locale.Name = x.GetLocalized(y => y.Name, languageId, false, false); - //}); - return model; - }), - Total = values.Count() - }; - return new JsonResult + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var values = _checkoutAttributeService.GetCheckoutAttributeValues(checkoutAttributeId); + + model.Data = values.Select(x => x.ToModel()); + + model.Total = values.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + + return new JsonResult { - Data = gridModel + Data = model }; } @@ -384,22 +389,19 @@ public ActionResult ValueEditPopup(string btnId, string formId, CheckoutAttribut return View(model); } - //delete [GridAction(EnableCustomBinding = true)] public ActionResult ValueDelete(int valueId, int checkoutAttributeId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var cav = _checkoutAttributeService.GetCheckoutAttributeValueById(valueId); - var cav = _checkoutAttributeService.GetCheckoutAttributeValueById(valueId); - if (cav == null) - throw new ArgumentException("No checkout attribute value found with the specified id"); - _checkoutAttributeService.DeleteCheckoutAttributeValue(cav); + _checkoutAttributeService.DeleteCheckoutAttributeValue(cav); + } return ValueList(checkoutAttributeId, command); } - #endregion } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index ce08f8803f..5127d27256 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -1034,35 +1034,43 @@ public ActionResult GenericAttributes(string entityName, int entityId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult GenericAttributesSelect(string entityName, int entityId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) - return AccessDeniedView(); + var model = new GridModel(); var storeId = _services.StoreContext.CurrentStore.Id; - ViewBag.StoreId = storeId; + ViewBag.StoreId = storeId; - var model = new List(); - if (entityName.HasValue() && entityId > 0) - { - var attributes = _genericAttributeService.GetAttributesForEntity(entityId, entityName); - var query = from attr in attributes - where attr.StoreId == storeId || attr.StoreId == 0 - select new GenericAttributeModel - { - Id = attr.Id, - EntityId = attr.EntityId, - EntityName = attr.KeyGroup, - Key = attr.Key, - Value = attr.Value - }; - model.AddRange(query); - } + if (_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) + { + if (entityName.HasValue() && entityId > 0) + { + var attributes = _genericAttributeService.GetAttributesForEntity(entityId, entityName); + model.Data = attributes + .Where(x => x.StoreId == storeId || x.StoreId == 0) + .Select(x => new GenericAttributeModel + { + Id = x.Id, + EntityId = x.EntityId, + EntityName = x.KeyGroup, + Key = x.Key, + Value = x.Value + }) + .ToList(); + + model.Total = model.Data.Count(); + } + else + { + model.Data = Enumerable.Empty(); + } + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var result = new GridModel - { - Data = model, - Total = model.Count - }; return new JsonResult { Data = model @@ -1072,38 +1080,36 @@ public ActionResult GenericAttributesSelect(string entityName, int entityId, Gri [GridAction(EnableCustomBinding = true)] public ActionResult GenericAttributeAdd(GenericAttributeModel model, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) - return AccessDeniedView(); + if (_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) + { + model.Key = model.Key.TrimSafe(); + model.Value = model.Value.TrimSafe(); - model.Key = model.Key.TrimSafe(); - model.Value = model.Value.TrimSafe(); + if (!ModelState.IsValid) + { + var modelStateErrorMessages = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrorMessages.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - // display the first model error - var modelStateErrorMessages = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrorMessages.FirstOrDefault()); - } + var storeId = _services.StoreContext.CurrentStore.Id; - var storeId = _services.StoreContext.CurrentStore.Id; - - var attr = _genericAttributeService.GetAttribute(model.EntityName, model.EntityId, model.Key, storeId); - if (attr == null) - { - var ga = new GenericAttribute - { - StoreId = storeId, - KeyGroup = model.EntityName, - EntityId = model.EntityId, - Key = model.Key, - Value = model.Value - }; - _genericAttributeService.InsertAttribute(ga); - } - else - { - return Content(string.Format(_localizationService.GetResource("Admin.Common.GenericAttributes.NameAlreadyExists"), model.Key)); - } + var attr = _genericAttributeService.GetAttribute(model.EntityName, model.EntityId, model.Key, storeId); + if (attr == null) + { + _genericAttributeService.InsertAttribute(new GenericAttribute + { + StoreId = storeId, + KeyGroup = model.EntityName, + EntityId = model.EntityId, + Key = model.Key, + Value = model.Value + }); + } + else + { + return Content(T("Admin.Common.GenericAttributes.NameAlreadyExists", model.Key)); + } + } return GenericAttributesSelect(model.EntityName, model.EntityId, command); } @@ -1111,38 +1117,38 @@ public ActionResult GenericAttributeAdd(GenericAttributeModel model, GridCommand [GridAction(EnableCustomBinding = true)] public ActionResult GenericAttributeUpdate(GenericAttributeModel model, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) - return AccessDeniedView(); + if (_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) + { + model.Key = model.Key.TrimSafe(); + model.Value = model.Value.TrimSafe(); - model.Key = model.Key.TrimSafe(); - model.Value = model.Value.TrimSafe(); + if (!ModelState.IsValid) + { + var modelStateErrorMessages = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrorMessages.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - // display the first model error - var modelStateErrorMessages = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrorMessages.FirstOrDefault()); - } + var storeId = _services.StoreContext.CurrentStore.Id; - var storeId = _services.StoreContext.CurrentStore.Id; + var attr = _genericAttributeService.GetAttributeById(model.Id); + // if the key changed, ensure it isn't being used by another attribute + if (!attr.Key.IsCaseInsensitiveEqual(model.Key)) + { + var attr2 = _genericAttributeService.GetAttributesForEntity(model.EntityId, model.EntityName) + .Where(x => x.StoreId == storeId && x.Key.Equals(model.Key, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(); - var attr = _genericAttributeService.GetAttributeById(model.Id); - // if the key changed, ensure it isn't being used by another attribute - if (!attr.Key.IsCaseInsensitiveEqual(model.Key)) - { - var attr2 = _genericAttributeService.GetAttributesForEntity(model.EntityId, model.EntityName) - .Where(x => x.StoreId == storeId && x.Key.Equals(model.Key, StringComparison.InvariantCultureIgnoreCase)) - .FirstOrDefault(); - if (attr2 != null && attr2.Id != attr.Id) - { - return Content(string.Format(_localizationService.GetResource("Admin.Common.GenericAttributes.NameAlreadyExists"), model.Key)); - } - } + if (attr2 != null && attr2.Id != attr.Id) + { + return Content(T("Admin.Common.GenericAttributes.NameAlreadyExists", model.Key)); + } + } - attr.Key = model.Key; - attr.Value = model.Value; + attr.Key = model.Key; + attr.Value = model.Value; - _genericAttributeService.UpdateAttribute(attr); + _genericAttributeService.UpdateAttribute(attr); + } return GenericAttributesSelect(model.EntityName, model.EntityId, command); } @@ -1150,17 +1156,12 @@ public ActionResult GenericAttributeUpdate(GenericAttributeModel model, GridComm [GridAction(EnableCustomBinding = true)] public ActionResult GenericAttributeDelete(int id, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) - return AccessDeniedView(); - var attr = _genericAttributeService.GetAttributeById(id); - if (attr == null) - { - throw new System.Web.HttpException(404, "No resource found with the specified id"); - } - - _genericAttributeService.DeleteAttribute(attr); + if (_services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel)) + { + _genericAttributeService.DeleteAttribute(attr); + } return GenericAttributesSelect(attr.KeyGroup, attr.EntityId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs index bf67df0dae..3a4d573b43 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CountryController.cs @@ -304,17 +304,22 @@ public ActionResult DeleteConfirmed(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult States(int countryId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) - return AccessDeniedView(); + var model = new GridModel(); - var states = _stateProvinceService.GetStateProvincesByCountryId(countryId, true) - .Select(x => x.ToModel()); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) + { + var states = _stateProvinceService.GetStateProvincesByCountryId(countryId, true) + .Select(x => x.ToModel()); - var model = new GridModel - { - Data = states, - Total = states.Count() - }; + model.Data = states; + model.Total = states.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -417,18 +422,18 @@ public ActionResult StateEditPopup(string btnId, string formId, StateProvinceMod [GridAction(EnableCustomBinding = true)] public ActionResult StateDelete(int id, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) - return AccessDeniedView(); - - var state = _stateProvinceService.GetStateProvinceById(id); - if (state == null) - throw new ArgumentException("No state found with the specified id"); + var state = _stateProvinceService.GetStateProvinceById(id); + var countryId = state.CountryId; - if (_addressService.GetAddressTotalByStateProvinceId(state.Id) > 0) - return Content(T("Admin.Configuration.Countries.States.CantDeleteWithAddresses")); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCountries)) + { + if (_addressService.GetAddressTotalByStateProvinceId(state.Id) > 0) + { + return Content(T("Admin.Configuration.Countries.States.CantDeleteWithAddresses")); + } - int countryId = state.CountryId; - _stateProvinceService.DeleteStateProvince(state); + _stateProvinceService.DeleteStateProvince(state); + } return States(countryId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CurrencyController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CurrencyController.cs index 181071a2d2..e20b59013c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CurrencyController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CurrencyController.cs @@ -239,18 +239,25 @@ public ActionResult Save(FormCollection formValues) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageCurrencies)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageCurrencies)) + { + var currencies = _currencyService.GetAllCurrencies(true); + + model.Data = currencies.Select(x => x.ToModel()); + model.Total = currencies.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var currencies = _currencyService.GetAllCurrencies(true); - var gridModel = new GridModel - { - Data = currencies.Select(x => x.ToModel()), - Total = currencies.Count() - }; return new JsonResult { - Data = gridModel + Data = model }; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs index 892bbaf5c6..0a91c9860f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerController.cs @@ -396,28 +396,37 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CustomerList(GridCommand command, CustomerListModel model, [ModelBinderAttribute(typeof(CommaSeparatedModelBinder))] int[] searchCustomerRoleIds) { - //we use own own binder for searchCustomerRoleIds property - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) - return AccessDeniedView(); + // we use own own binder for searchCustomerRoleIds property + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) + { + var searchDayOfBirth = 0; + var searchMonthOfBirth = 0; + + if (!String.IsNullOrWhiteSpace(model.SearchDayOfBirth)) + searchDayOfBirth = Convert.ToInt32(model.SearchDayOfBirth); + + if (!String.IsNullOrWhiteSpace(model.SearchMonthOfBirth)) + searchMonthOfBirth = Convert.ToInt32(model.SearchMonthOfBirth); + + var customers = _customerService.GetAllCustomers(null, null, + searchCustomerRoleIds, model.SearchEmail, model.SearchUsername, + model.SearchFirstName, model.SearchLastName, + searchDayOfBirth, searchMonthOfBirth, + model.SearchCompany, model.SearchPhone, model.SearchZipPostalCode, + false, null, command.Page - 1, command.PageSize); + + gridModel.Data = customers.Select(PrepareCustomerModelForList); + gridModel.Total = customers.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var searchDayOfBirth = 0; - int searchMonthOfBirth = 0; - if (!String.IsNullOrWhiteSpace(model.SearchDayOfBirth)) - searchDayOfBirth = Convert.ToInt32(model.SearchDayOfBirth); - if (!String.IsNullOrWhiteSpace(model.SearchMonthOfBirth)) - searchMonthOfBirth = Convert.ToInt32(model.SearchMonthOfBirth); - - var customers = _customerService.GetAllCustomers(null, null, - searchCustomerRoleIds, model.SearchEmail, model.SearchUsername, - model.SearchFirstName, model.SearchLastName, - searchDayOfBirth, searchMonthOfBirth, - model.SearchCompany, model.SearchPhone, model.SearchZipPostalCode, - false, null, command.Page - 1, command.PageSize); - var gridModel = new GridModel - { - Data = customers.Select(PrepareCustomerModelForList), - Total = customers.TotalCount - }; return new JsonResult { Data = gridModel @@ -1519,17 +1528,15 @@ public ActionResult AddressEdit(CustomerAddressModel model) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult OrderList(int customerId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) - return AccessDeniedView(); + var model = new GridModel(); - var orders = _orderService.SearchOrders(0, customerId, - null, null, null, null, null, null, null, null, 0, int.MaxValue); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) + { + var orders = _orderService.SearchOrders(0, customerId, null, null, null, null, null, null, null, null, 0, int.MaxValue); - var model = new GridModel - { - Data = orders.PagedForCommand(command) - .Select(order => - { + model.Data = orders.PagedForCommand(command) + .Select(order => + { var store = _storeService.GetStoreById(order.StoreId); var orderModel = new CustomerModel.OrderModel() { @@ -1538,13 +1545,20 @@ public ActionResult OrderList(int customerId, GridCommand command) PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext), ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext), OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false), - StoreName = store != null ? store.Name : "Unknown", + StoreName = store != null ? store.Name : "".NaIfEmpty(), CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc), }; - return orderModel; - }), - Total = orders.Count - }; + return orderModel; + }); + + model.Total = orders.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1596,9 +1610,8 @@ public ActionResult ReportBestCustomersByOrderTotalList(GridCommand command, Bes PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null; ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null; + var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue, orderStatus, paymentStatus, shippingStatus, 1); - var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue, - orderStatus, paymentStatus, shippingStatus, 1); var gridModel = new GridModel { Data = items.Select(x => @@ -1609,23 +1622,25 @@ public ActionResult ReportBestCustomersByOrderTotalList(GridCommand command, Bes OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false), OrderCount = x.OrderCount, }; + var customer = _customerService.GetCustomerById(x.CustomerId); if (customer != null) { - m.CustomerName = customer.IsGuest() - ? _localizationService.GetResource("Admin.Customers.Guest") - : customer.Email; + m.CustomerName = customer.IsGuest() ? T("Admin.Customers.Guest").Text : customer.Email; } + return m; }), Total = items.Count }; + return new JsonResult { Data = gridModel }; } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult ReportBestCustomersByNumberOfOrdersList(GridCommand command, BestCustomersReportModel model) { DateTime? startDateValue = (model.StartDate == null) ? null @@ -1638,26 +1653,25 @@ public ActionResult ReportBestCustomersByNumberOfOrdersList(GridCommand command, PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null; ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null; + var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue, orderStatus, paymentStatus, shippingStatus, 2); - var items = _customerReportService.GetBestCustomersReport(startDateValue, endDateValue, - orderStatus, paymentStatus, shippingStatus, 2); var gridModel = new GridModel { Data = items.Select(x => { - var m = new BestCustomerReportLineModel() + var m = new BestCustomerReportLineModel { CustomerId = x.CustomerId, OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false), OrderCount = x.OrderCount, }; + var customer = _customerService.GetCustomerById(x.CustomerId); if (customer != null) { - m.CustomerName = customer.IsGuest() - ? _localizationService.GetResource("Admin.Customers.Guest") - : customer.Email; + m.CustomerName = customer.IsGuest() ? T("Admin.Customers.Guest").Text : customer.Email; } + return m; }), Total = items.Count @@ -1674,15 +1688,18 @@ public ActionResult ReportRegisteredCustomers() var model = GetReportRegisteredCustomersModel(); return PartialView(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult ReportRegisteredCustomersList(GridCommand command) { var model = GetReportRegisteredCustomersModel(); - var gridModel = new GridModel + + var gridModel = new GridModel { Data = model, Total = model.Count }; + return new JsonResult { Data = gridModel @@ -1699,16 +1716,17 @@ public ActionResult GetCartList(int customerId, int cartTypeId) var customer = _customerService.GetCustomerById(customerId); var cart = customer.GetCartItems((ShoppingCartType)cartTypeId); - var gridModel = new GridModel() + var gridModel = new GridModel { Data = cart.Select(sci => { decimal taxRate; var store = _storeService.GetStoreById(sci.Item.StoreId); - var sciModel = new ShoppingCartItemModel() + + var sciModel = new ShoppingCartItemModel { Id = sci.Item.Id, - Store = store != null ? store.Name : "Unknown", + Store = store != null ? store.Name : "".NaIfEmpty(), ProductId = sci.Item.ProductId, Quantity = sci.Item.Quantity, ProductName = sci.Item.Product.Name, @@ -1722,6 +1740,7 @@ public ActionResult GetCartList(int customerId, int cartTypeId) }), Total = cart.Count }; + return new JsonResult { Data = gridModel @@ -1736,11 +1755,12 @@ public ActionResult GetCartList(int customerId, int cartTypeId) public JsonResult ListActivityLog(GridCommand command, int customerId) { var activityLog = _customerActivityService.GetAllActivities(null, null, customerId, 0, command.Page - 1, command.PageSize); + var gridModel = new GridModel { Data = activityLog.Select(x => { - var m = new CustomerModel.ActivityLogModel() + var m = new CustomerModel.ActivityLogModel { Id = x.Id, ActivityLogTypeName = x.ActivityLogType.Name, @@ -1748,11 +1768,11 @@ public JsonResult ListActivityLog(GridCommand command, int customerId) CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc) }; return m; - }), Total = activityLog.TotalCount }; - return new JsonResult { Data = gridModel }; ; + + return new JsonResult { Data = gridModel }; } #endregion diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerRoleController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerRoleController.cs index 7cc7fa5575..6bb2d10b47 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerRoleController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CustomerRoleController.cs @@ -102,18 +102,25 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles)) - return AccessDeniedView(); - - var customerRoles = _customerService.GetAllCustomerRoles(true); - var gridModel = new GridModel + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCustomerRoles)) { - Data = customerRoles.Select(x => x.ToModel()), - Total = customerRoles.Count() - }; + var customerRoles = _customerService.GetAllCustomerRoles(true); + + model.Data = customerRoles.Select(x => x.ToModel()); + model.Total = customerRoles.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { - Data = gridModel + Data = model }; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/DiscountController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/DiscountController.cs index 6e18b8ec06..c9ab6e4496 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/DiscountController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/DiscountController.cs @@ -138,7 +138,6 @@ private void PrepareDiscountModel(DiscountModel model, Discount discount) #region Discounts - //list public ActionResult Index() { return RedirectToAction("List"); @@ -161,22 +160,28 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) + { + var discounts = _discountService.GetAllDiscounts(null, null, true); + + model.Data = discounts.Select(x => x.ToModel()); + model.Total = discounts.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var discounts = _discountService.GetAllDiscounts(null, null, true); - var gridModel = new GridModel - { - Data = discounts.Select(x => x.ToModel()), - Total = discounts.Count() - }; return new JsonResult { - Data = gridModel + Data = model }; } - //create public ActionResult Create() { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) @@ -212,7 +217,6 @@ public ActionResult Create(DiscountModel model, bool continueEditing) return View(model); } - //edit public ActionResult Edit(int id) { if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) @@ -281,7 +285,6 @@ public ActionResult Edit(DiscountModel model, bool continueEditing) return View(model); } - //delete [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { @@ -386,28 +389,31 @@ public ActionResult DeleteDiscountRequirement(int discountRequirementId, int dis [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult UsageHistoryList(int discountId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) - return AccessDeniedView(); + var model = new GridModel(); - var discount = _discountService.GetDiscountById(discountId); - if (discount == null) - throw new ArgumentException("No discount found with the specified id"); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) + { + var discount = _discountService.GetDiscountById(discountId); + + var discountHistories = _discountService.GetAllDiscountUsageHistory(discount.Id, null, command.Page - 1, command.PageSize); + + model.Data = discountHistories.Select(x => new DiscountModel.DiscountUsageHistoryModel + { + Id = x.Id, + DiscountId = x.DiscountId, + OrderId = x.OrderId, + CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc) + }); + + model.Total = discountHistories.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var duh = _discountService.GetAllDiscountUsageHistory(discount.Id, null, command.Page - 1, command.PageSize); - var model = new GridModel - { - Data = duh.Select(x => - { - return new DiscountModel.DiscountUsageHistoryModel - { - Id = x.Id, - DiscountId = x.DiscountId, - OrderId = x.OrderId, - CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc) - }; - }), - Total = duh.TotalCount - }; return new JsonResult { Data = model @@ -417,16 +423,13 @@ public ActionResult UsageHistoryList(int discountId, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult UsageHistoryDelete(int discountId, int id, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) - return AccessDeniedView(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageDiscounts)) + { + var discountHistory = _discountService.GetDiscountUsageHistoryById(id); + + _discountService.DeleteDiscountUsageHistory(discountHistory); + } - var discount = _discountService.GetDiscountById(discountId); - if (discount == null) - throw new ArgumentException("No discount found with the specified id"); - - var duh = _discountService.GetDiscountUsageHistoryById(id); - if (duh != null) - _discountService.DeleteDiscountUsageHistory(duh); return UsageHistoryList(discountId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/EmailAccountController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/EmailAccountController.cs index c883d5f266..2586a826cb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/EmailAccountController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/EmailAccountController.cs @@ -78,24 +78,32 @@ public ActionResult List(string id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageEmailAccounts)) - return AccessDeniedView(); + var model = new GridModel(); - var emailAccountModels = _emailAccountService.GetAllEmailAccounts() - .Select(x => x.ToModel()) - .ToList(); - foreach (var eam in emailAccountModels) - eam.IsDefaultEmailAccount = eam.Id == _emailAccountSettings.DefaultEmailAccountId; + if (_permissionService.Authorize(StandardPermissionProvider.ManageEmailAccounts)) + { + var emailAccountModels = _emailAccountService.GetAllEmailAccounts() + .Select(x => x.ToModel()) + .ToList(); - var gridModel = new GridModel - { - Data = emailAccountModels, - Total = emailAccountModels.Count() - }; + foreach (var eam in emailAccountModels) + { + eam.IsDefaultEmailAccount = eam.Id == _emailAccountSettings.DefaultEmailAccountId; + } + + model.Data = emailAccountModels; + model.Total = emailAccountModels.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { - Data = gridModel + Data = model }; } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index 2e719959ec..39579caa57 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -823,11 +823,17 @@ public ActionResult Preview(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult PreviewList(GridCommand command, int id, int totalRecords) { + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) + { + NotifyAccessDenied(); + + return new JsonResult { Data = Enumerable.Empty() }; + } + ExportProfile profile = null; Provider provider = null; - if (_services.Permissions.Authorize(StandardPermissionProvider.ManageExports) && - (profile = _exportService.GetExportProfileById(id)) != null && + if ((profile = _exportService.GetExportProfileById(id)) != null && (provider = _exportService.LoadProvider(profile.ProviderSystemName)) != null && !provider.Metadata.IsHidden) { @@ -984,7 +990,7 @@ public ActionResult PreviewList(GridCommand command, int id, int totalRecords) return new JsonResult { Data = gridData }; } - return new EmptyResult(); + return new JsonResult { Data = Enumerable.Empty() }; } [HttpPost] diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/GiftCardController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/GiftCardController.cs index ea3eff5cd1..f52756dd84 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/GiftCardController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/GiftCardController.cs @@ -94,27 +94,37 @@ public ActionResult List() [GridAction(EnableCustomBinding = true)] public ActionResult GiftCardList(GridCommand command, GiftCardListModel model) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageGiftCards)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageGiftCards)) + { + bool? isGiftCardActivated = null; + + if (model.ActivatedId == 1) + isGiftCardActivated = true; + else if (model.ActivatedId == 2) + isGiftCardActivated = false; + + var giftCards = _giftCardService.GetAllGiftCards(null, null, null, isGiftCardActivated, model.CouponCode); + + gridModel.Data = giftCards.PagedForCommand(command).Select(x => + { + var m = x.ToModel(); + m.RemainingAmountStr = _priceFormatter.FormatPrice(x.GetGiftCardRemainingAmount(), true, false); + m.AmountStr = _priceFormatter.FormatPrice(x.Amount, true, false); + m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + return m; + }); + + gridModel.Total = giftCards.Count(); + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - bool? isGiftCardActivated = null; - if (model.ActivatedId == 1) - isGiftCardActivated = true; - else if (model.ActivatedId == 2) - isGiftCardActivated = false; - var giftCards = _giftCardService.GetAllGiftCards(null, null, null, isGiftCardActivated, model.CouponCode); - var gridModel = new GridModel - { - Data = giftCards.PagedForCommand(command).Select(x => - { - var m = x.ToModel(); - m.RemainingAmountStr = _priceFormatter.FormatPrice(x.GetGiftCardRemainingAmount(), true, false); - m.AmountStr = _priceFormatter.FormatPrice(x.Amount, true, false); - m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - return m; - }), - Total = giftCards.Count() - }; return new JsonResult { Data = gridModel @@ -274,46 +284,48 @@ public ActionResult DeleteConfirmed(int id) var giftCard = _giftCardService.GetGiftCardById(id); if (giftCard == null) - //No gift card found with the specified id return RedirectToAction("List"); _giftCardService.DeleteGiftCard(giftCard); - //activity log _customerActivityService.InsertActivity("DeleteGiftCard", _services.Localization.GetResource("ActivityLog.DeleteGiftCard"), giftCard.GiftCardCouponCode); NotifySuccess(_services.Localization.GetResource("Admin.GiftCards.Deleted")); return RedirectToAction("List"); } - //Gif card usage history [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult UsageHistoryList(int giftCardId, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageGiftCards)) - return AccessDeniedView(); - - var giftCard = _giftCardService.GetGiftCardById(giftCardId); - if (giftCard == null) - throw new ArgumentException("No gift card found with the specified id"); - - var usageHistoryModel = giftCard.GiftCardUsageHistory.OrderByDescending(gcuh => gcuh.CreatedOnUtc) - .Select(x => - { - return new GiftCardModel.GiftCardUsageHistoryModel() - { - Id = x.Id, - OrderId = x.UsedWithOrderId, - UsedValue = _priceFormatter.FormatPrice(x.UsedValue, true, false), - CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc) - }; - }) - .ToList(); - var model = new GridModel - { - Data = usageHistoryModel.PagedForCommand(command), - Total = usageHistoryModel.Count - }; + var model = new GridModel(); + + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageGiftCards)) + { + var giftCard = _giftCardService.GetGiftCardById(giftCardId); + + var usageHistoryModel = giftCard.GiftCardUsageHistory + .OrderByDescending(gcuh => gcuh.CreatedOnUtc) + .Select(x => + { + return new GiftCardModel.GiftCardUsageHistoryModel() + { + Id = x.Id, + OrderId = x.UsedWithOrderId, + UsedValue = _priceFormatter.FormatPrice(x.UsedValue, true, false), + CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc) + }; + }) + .ToList(); + + model.Data = usageHistoryModel.PagedForCommand(command); + model.Total = usageHistoryModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { From 619e8dcb4426c7503ce455b9d6be10b0e7bf08b8 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 6 Jan 2016 12:19:09 +0100 Subject: [PATCH 240/732] Faulty permission handling in ajax grid actions (no message, infinite loading icon)(part 2) --- .../Controllers/WebApiController.cs | 2 +- .../Controllers/LanguageController.cs | 192 +- .../Controllers/LogController.cs | 78 +- .../Controllers/ManufacturerController.cs | 160 +- .../Controllers/MeasureController.cs | 198 +- .../Controllers/MessageTemplateController.cs | 20 +- .../Controllers/NewsController.cs | 138 +- .../NewsLetterSubscriptionController.cs | 70 +- .../Controllers/OnlineCustomerController.cs | 48 +- .../Controllers/OrderController.cs | 331 +-- .../Controllers/PollController.cs | 172 +- .../Controllers/ProductAttributeController.cs | 26 +- .../Controllers/ProductController.cs | 1766 +++++++++-------- .../Controllers/ProductReviewController.cs | 45 +- .../Controllers/QueuedEmailController.cs | 64 +- .../Controllers/RecurringPaymentController.cs | 81 +- .../Controllers/ReturnRequestController.cs | 38 +- .../Controllers/SettingController.cs | 173 +- .../Controllers/ShippingController.cs | 27 +- .../Controllers/ShoppingCartController.cs | 200 +- .../SpecificationAttributeController.cs | 96 +- .../Controllers/StoreController.cs | 35 +- .../Controllers/TaxController.cs | 83 +- .../Controllers/TopicController.cs | 27 +- .../Controllers/UrlRecordController.cs | 32 +- 25 files changed, 2200 insertions(+), 1902 deletions(-) diff --git a/src/Plugins/SmartStore.WebApi/Controllers/WebApiController.cs b/src/Plugins/SmartStore.WebApi/Controllers/WebApiController.cs index 808387b270..4fb254bbff 100644 --- a/src/Plugins/SmartStore.WebApi/Controllers/WebApiController.cs +++ b/src/Plugins/SmartStore.WebApi/Controllers/WebApiController.cs @@ -97,7 +97,7 @@ public ActionResult SaveGeneralSettings(WebApiConfigModel model) public ActionResult GridUserData(GridCommand command) { if (!HasPermission()) - return new JsonResult { Data = new GridModel { Data = new List() }}; + return new JsonResult { Data = new GridModel { Data = new List() } }; var model = _webApiPluginService.GetGridModel(command.Page - 1, command.PageSize); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs index e546998d03..02587df34f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs @@ -60,7 +60,8 @@ public LanguageController(ILanguageService languageService, } #endregion - #region Utilities + + #region Utilities [NonAction] private void PrepareFlagsModel(LanguageModel model) @@ -123,18 +124,25 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + { + var languages = _languageService.GetAllLanguages(true); + + model.Data = languages.Select(x => x.ToModel()); + model.Total = languages.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var languages = _languageService.GetAllLanguages(true); - var gridModel = new GridModel - { - Data = languages.Select(x => x.ToModel()), - Total = languages.Count() - }; return new JsonResult { - Data = gridModel + Data = model }; } @@ -329,31 +337,37 @@ public ActionResult Resources(int languageId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Resources(int languageId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) - return AccessDeniedView(); + var model = new GridModel(); - var language = _languageService.GetLanguageById(languageId); + if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + { + var language = _languageService.GetLanguageById(languageId); + + var resources = _localizationService + .GetResourceValues(languageId, true) + .OrderBy(x => x.Key) + .Where(x => x.Key != "!!___EOF___!!" && x.Value != null) + .Select(x => new LanguageResourceModel + { + LanguageId = languageId, + LanguageName = language.Name, + Id = x.Value.Item1, + Name = x.Key, + Value = x.Value.Item2.EmptyNull(), + }) + .ForCommand(command) + .ToList(); + + model.Data = resources.PagedForCommand(command); + model.Total = resources.Count; + } + else + { + model.Data = Enumerable.Empty(); - var resources = _localizationService - .GetResourceValues(languageId, true) - .OrderBy(x => x.Key) - .Where(x => x.Key != "!!___EOF___!!" && x.Value != null) - .Select(x => new LanguageResourceModel - { - LanguageId = languageId, - LanguageName = language.Name, - Id = x.Value.Item1, - Name = x.Key, - Value = x.Value.Item2.EmptyNull(), - }) - .ForCommand(command) - .ToList(); + NotifyAccessDenied(); + } - var model = new GridModel - { - Data = resources.PagedForCommand(command), - Total = resources.Count - }; return new JsonResult { Data = model @@ -363,36 +377,36 @@ public ActionResult Resources(int languageId, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + { + if (model.Name != null) + model.Name = model.Name.Trim(); + if (model.Value != null) + model.Value = model.Value.Trim(); - if (model.Name != null) - model.Name = model.Name.Trim(); - if (model.Value != null) - model.Value = model.Value.Trim(); + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var resource = _localizationService.GetLocaleStringResourceById(model.Id); + // if the resourceName changed, ensure it isn't being used by another resource + if (!resource.ResourceName.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase)) + { + var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); + if (res != null && res.Id != resource.Id) + { + return Content(T("Admin.Configuration.Languages.Resources.NameAlreadyExists", res.ResourceName)); + } + } - var resource = _localizationService.GetLocaleStringResourceById(model.Id); - // if the resourceName changed, ensure it isn't being used by another resource - if (!resource.ResourceName.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase)) - { - var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); - if (res != null && res.Id != resource.Id) - { - return Content(string.Format(_localizationService.GetResource("Admin.Configuration.Languages.Resources.NameAlreadyExists"), res.ResourceName)); - } - } + resource.ResourceName = model.Name; + resource.ResourceValue = model.Value; + resource.IsTouched = true; - resource.ResourceName = model.Name; - resource.ResourceValue = model.Value; - resource.IsTouched = true; - _localizationService.UpdateLocaleStringResource(resource); + _localizationService.UpdateLocaleStringResource(resource); + } return Resources(model.LanguageId, command); } @@ -400,47 +414,47 @@ public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand comm [GridAction(EnableCustomBinding = true)] public ActionResult ResourceAdd(int id, LanguageResourceModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + { + if (model.Name != null) + model.Name = model.Name.Trim(); + if (model.Value != null) + model.Value = model.Value.Trim(); - if (model.Name != null) - model.Name = model.Name.Trim(); - if (model.Value != null) - model.Value = model.Value.Trim(); + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); + if (res == null) + { + var resource = new LocaleStringResource { LanguageId = id }; + resource.ResourceName = model.Name; + resource.ResourceValue = model.Value; + resource.IsTouched = true; + + _localizationService.InsertLocaleStringResource(resource); + } + else + { + return Content(T("Admin.Configuration.Languages.Resources.NameAlreadyExists", model.Name)); + } + } - var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); - if (res == null) - { - var resource = new LocaleStringResource { LanguageId = id }; - resource.ResourceName = model.Name; - resource.ResourceValue = model.Value; - resource.IsTouched = true; - _localizationService.InsertLocaleStringResource(resource); - } - else - { - return Content(string.Format(_localizationService.GetResource("Admin.Configuration.Languages.Resources.NameAlreadyExists"), model.Name)); - } return Resources(id, command); } [GridAction(EnableCustomBinding = true)] public ActionResult ResourceDelete(int id, int languageId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + { + var resource = _localizationService.GetLocaleStringResourceById(id); - var resource = _localizationService.GetLocaleStringResourceById(id); - if (resource == null) - throw new ArgumentException("No resource found with the specified id"); - _localizationService.DeleteLocaleStringResource(resource); + _localizationService.DeleteLocaleStringResource(resource); + } return Resources(languageId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LogController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LogController.cs index 8b82d5d894..9fb8db0105 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LogController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LogController.cs @@ -64,49 +64,55 @@ public ActionResult List() [GridAction(EnableCustomBinding = true)] public ActionResult LogList(GridCommand command, LogListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageSystemLog)) - return AccessDeniedView(); - - DateTime? createdOnFromValue = (model.CreatedOnFrom == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnFrom.Value, _dateTimeHelper.CurrentTimeZone); - - DateTime? createdToFromValue = (model.CreatedOnTo == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); - - LogLevel? logLevel = model.LogLevelId > 0 ? (LogLevel?)(model.LogLevelId) : null; - - - var logItems = Logger.GetAllLogs(createdOnFromValue, createdToFromValue, model.Message, - logLevel, command.Page - 1, command.PageSize, model.MinFrequency); - - var gridModel = new GridModel - { - Data = logItems.Select(x => - { - var logModel = new LogModel() - { - Id = x.Id, - LogLevelHint = s_logLevelHintMap[x.LogLevel], - LogLevel = x.LogLevel.GetLocalizedEnum(_localizationService, _workContext), - ShortMessage = x.ShortMessage, - FullMessage = x.FullMessage, - IpAddress = x.IpAddress, - CustomerId = x.CustomerId, - CustomerEmail = x.Customer != null ? x.Customer.Email : null, - PageUrl = x.PageUrl, - ReferrerUrl = x.ReferrerUrl, - CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageSystemLog)) + { + DateTime? createdOnFromValue = (model.CreatedOnFrom == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnFrom.Value, _dateTimeHelper.CurrentTimeZone); + + DateTime? createdToFromValue = (model.CreatedOnTo == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + + LogLevel? logLevel = model.LogLevelId > 0 ? (LogLevel?)(model.LogLevelId) : null; + + var logItems = Logger.GetAllLogs(createdOnFromValue, createdToFromValue, model.Message, + logLevel, command.Page - 1, command.PageSize, model.MinFrequency); + + gridModel.Data = logItems.Select(x => + { + var logModel = new LogModel + { + Id = x.Id, + LogLevelHint = s_logLevelHintMap[x.LogLevel], + LogLevel = x.LogLevel.GetLocalizedEnum(_localizationService, _workContext), + ShortMessage = x.ShortMessage, + FullMessage = x.FullMessage, + IpAddress = x.IpAddress, + CustomerId = x.CustomerId, + CustomerEmail = x.Customer != null ? x.Customer.Email : null, + PageUrl = x.PageUrl, + ReferrerUrl = x.ReferrerUrl, + CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), Frequency = x.Frequency, ContentHash = x.ContentHash - }; + }; if (x.UpdatedOnUtc.HasValue) logModel.UpdatedOn = _dateTimeHelper.ConvertToUserTime(x.UpdatedOnUtc.Value, DateTimeKind.Utc); return logModel; - }), - Total = logItems.TotalCount - }; + }); + + gridModel.Total = logItems.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs index e107a81fac..5f345fcb42 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs @@ -254,16 +254,21 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, ManufacturerListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var manufacturers = _manufacturerService.GetAllManufacturers(model.SearchManufacturerName, command.Page - 1, command.PageSize, true); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var manufacturers = _manufacturerService.GetAllManufacturers(model.SearchManufacturerName, command.Page - 1, command.PageSize, true); - var gridModel = new GridModel - { - Data = manufacturers.Select(x => x.ToModel()), - Total = manufacturers.TotalCount - }; + gridModel.Data = manufacturers.Select(x => x.ToModel()); + gridModel.Total = manufacturers.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -441,35 +446,43 @@ public ActionResult Delete(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductList(GridCommand command, int manufacturerId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productManufacturers = _manufacturerService.GetProductManufacturersByManufacturerId(manufacturerId, command.Page - 1, command.PageSize, true); - var productManufacturers = _manufacturerService.GetProductManufacturersByManufacturerId(manufacturerId, - command.Page - 1, command.PageSize, true); + var productIds = productManufacturers.Select(x => x.ProductId).ToArray(); + var products = _productService.GetProductsByIds(productIds); - var model = new GridModel - { - Data = productManufacturers - .Select(x => - { - var product = _productService.GetProductById(x.ProductId); + model.Data = productManufacturers + .Select(x => + { + var product = products.FirstOrDefault(y => y.Id == x.ProductId); + + return new ManufacturerModel.ManufacturerProductModel + { + Id = x.Id, + ManufacturerId = x.ManufacturerId, + ProductId = x.ProductId, + ProductName = product.Name, + Sku = product.Sku, + ProductTypeName = product.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = product.ProductTypeLabelHint, + Published = product.Published, + IsFeaturedProduct = x.IsFeaturedProduct, + DisplayOrder1 = x.DisplayOrder + }; + }); - return new ManufacturerModel.ManufacturerProductModel() - { - Id = x.Id, - ManufacturerId = x.ManufacturerId, - ProductId = x.ProductId, - ProductName = product.Name, - Sku = product.Sku, - ProductTypeName = product.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = product.ProductTypeLabelHint, - Published = product.Published, - IsFeaturedProduct = x.IsFeaturedProduct, - DisplayOrder1 = x.DisplayOrder - }; - }), - Total = productManufacturers.TotalCount - }; + model.Total = productManufacturers.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -480,16 +493,15 @@ public ActionResult ProductList(GridCommand command, int manufacturerId) [GridAction(EnableCustomBinding = true)] public ActionResult ProductUpdate(GridCommand command, ManufacturerModel.ManufacturerProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productManufacturer = _manufacturerService.GetProductManufacturerById(model.Id); - var productManufacturer = _manufacturerService.GetProductManufacturerById(model.Id); - if (productManufacturer == null) - throw new ArgumentException("No product manufacturer mapping found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + productManufacturer.IsFeaturedProduct = model.IsFeaturedProduct; + productManufacturer.DisplayOrder = model.DisplayOrder1; - productManufacturer.IsFeaturedProduct = model.IsFeaturedProduct; - productManufacturer.DisplayOrder = model.DisplayOrder1; - _manufacturerService.UpdateProductManufacturer(productManufacturer); + _manufacturerService.UpdateProductManufacturer(productManufacturer); + } return ProductList(command, productManufacturer.ManufacturerId); } @@ -497,15 +509,13 @@ public ActionResult ProductUpdate(GridCommand command, ManufacturerModel.Manufac [GridAction(EnableCustomBinding = true)] public ActionResult ProductDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var productManufacturer = _manufacturerService.GetProductManufacturerById(id); - if (productManufacturer == null) - throw new ArgumentException("No product manufacturer mapping found with the specified id"); + var productManufacturer = _manufacturerService.GetProductManufacturerById(id); + var manufacturerId = productManufacturer.ManufacturerId; - var manufacturerId = productManufacturer.ManufacturerId; - _manufacturerService.DeleteProductManufacturer(productManufacturer); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _manufacturerService.DeleteProductManufacturer(productManufacturer); + } return ProductList(command, manufacturerId); } @@ -560,35 +570,43 @@ public ActionResult ProductAddPopup(int manufacturerId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductAddPopupList(GridCommand command, ManufacturerModel.AddManufacturerProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var gridModel = new GridModel(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var ctx = new ProductSearchContext(); - var ctx = new ProductSearchContext(); + if (model.SearchCategoryId > 0) + ctx.CategoryIds.Add(model.SearchCategoryId); - if (model.SearchCategoryId > 0) - ctx.CategoryIds.Add(model.SearchCategoryId); + ctx.ManufacturerId = model.SearchManufacturerId; + ctx.Keywords = model.SearchProductName; + ctx.LanguageId = _workContext.WorkingLanguage.Id; + ctx.OrderBy = ProductSortingEnum.Position; + ctx.PageIndex = command.Page - 1; + ctx.PageSize = command.PageSize; + ctx.ShowHidden = true; + ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; - ctx.ManufacturerId = model.SearchManufacturerId; - ctx.Keywords = model.SearchProductName; - ctx.LanguageId = _workContext.WorkingLanguage.Id; - ctx.OrderBy = ProductSortingEnum.Position; - ctx.PageIndex = command.Page - 1; - ctx.PageSize = command.PageSize; - ctx.ShowHidden = true; - ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; + var products = _productService.SearchProducts(ctx); - var products = _productService.SearchProducts(ctx); + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + + return productModel; + }); - gridModel.Data = products.Select(x => + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - return productModel; - }); - gridModel.Total = products.TotalCount; return new JsonResult { Data = gridModel diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MeasureController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MeasureController.cs index ec1265db77..f8b33e6102 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MeasureController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MeasureController.cs @@ -79,20 +79,29 @@ public ActionResult Weights(string id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Weights(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); + var model = new GridModel(); - var weightsModel = _measureService.GetAllMeasureWeights() - .Select(x => x.ToModel()) - .ForCommand(command) - .ToList(); - foreach (var wm in weightsModel) - wm.IsPrimaryWeight = wm.Id == _measureSettings.BaseWeightId; - var model = new GridModel - { - Data = weightsModel, - Total = weightsModel.Count - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + var weightsModel = _measureService.GetAllMeasureWeights() + .Select(x => x.ToModel()) + .ForCommand(command) + .ToList(); + + foreach (var wm in weightsModel) + { + wm.IsPrimaryWeight = wm.Id == _measureSettings.BaseWeightId; + } + + model.Data = weightsModel; + model.Total = weightsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -100,22 +109,22 @@ public ActionResult Weights(GridCommand command) }; } - [GridAction(EnableCustomBinding=true)] + [GridAction(EnableCustomBinding = true)] public ActionResult WeightUpdate(MeasureWeightModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); - - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - var weight = _measureService.GetMeasureWeightById(model.Id); - weight = model.ToEntity(weight); - _measureService.UpdateMeasureWeight(weight); + var weight = _measureService.GetMeasureWeightById(model.Id); + weight = model.ToEntity(weight); + + _measureService.UpdateMeasureWeight(weight); + } return Weights(command); } @@ -123,19 +132,19 @@ public ActionResult WeightUpdate(MeasureWeightModel model, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult WeightAdd([Bind(Exclude="Id")] MeasureWeightModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var weight = new MeasureWeight(); + weight = model.ToEntity(weight); - var weight = new MeasureWeight(); - weight = model.ToEntity(weight); - _measureService.InsertMeasureWeight(weight); + _measureService.InsertMeasureWeight(weight); + } return Weights(command); } @@ -143,17 +152,17 @@ public ActionResult WeightAdd([Bind(Exclude="Id")] MeasureWeightModel model, Gri [GridAction(EnableCustomBinding = true)] public ActionResult WeightDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); - - var weight = _measureService.GetMeasureWeightById(id); - if (weight == null) - throw new ArgumentException("No weight found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + var weight = _measureService.GetMeasureWeightById(id); - if (weight.Id == _measureSettings.BaseWeightId) - return Content(_localizationService.GetResource("Admin.Configuration.Measures.Weights.CantDeletePrimary")); + if (weight.Id == _measureSettings.BaseWeightId) + { + return Content(T("Admin.Configuration.Measures.Weights.CantDeletePrimary")); + } - _measureService.DeleteMeasureWeight(weight); + _measureService.DeleteMeasureWeight(weight); + } return Weights(command); } @@ -195,20 +204,29 @@ public ActionResult Dimensions(string id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Dimensions(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); + var model = new GridModel(); - var dimensionsModel = _measureService.GetAllMeasureDimensions() - .Select(x => x.ToModel()) - .ForCommand(command) - .ToList(); - foreach (var wm in dimensionsModel) - wm.IsPrimaryDimension = wm.Id == _measureSettings.BaseDimensionId; - var model = new GridModel - { - Data = dimensionsModel, - Total = dimensionsModel.Count - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + var dimensionsModel = _measureService.GetAllMeasureDimensions() + .Select(x => x.ToModel()) + .ForCommand(command) + .ToList(); + + foreach (var wm in dimensionsModel) + { + wm.IsPrimaryDimension = wm.Id == _measureSettings.BaseDimensionId; + } + + model.Data = dimensionsModel; + model.Total = dimensionsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -219,19 +237,19 @@ public ActionResult Dimensions(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult DimensionUpdate(MeasureDimensionModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var dimension = _measureService.GetMeasureDimensionById(model.Id); + dimension = model.ToEntity(dimension); - var dimension = _measureService.GetMeasureDimensionById(model.Id); - dimension = model.ToEntity(dimension); - _measureService.UpdateMeasureDimension(dimension); + _measureService.UpdateMeasureDimension(dimension); + } return Dimensions(command); } @@ -239,19 +257,19 @@ public ActionResult DimensionUpdate(MeasureDimensionModel model, GridCommand com [GridAction(EnableCustomBinding = true)] public ActionResult DimensionAdd([Bind(Exclude="Id")] MeasureDimensionModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var dimension = new MeasureDimension(); + dimension = model.ToEntity(dimension); - var dimension = new MeasureDimension(); - dimension = model.ToEntity(dimension); - _measureService.InsertMeasureDimension(dimension); + _measureService.InsertMeasureDimension(dimension); + } return Dimensions(command); } @@ -259,17 +277,17 @@ public ActionResult DimensionAdd([Bind(Exclude="Id")] MeasureDimensionModel mode [GridAction(EnableCustomBinding = true)] public ActionResult DimensionDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) - return AccessDeniedView(); - - var dimension = _measureService.GetMeasureDimensionById(id); - if (dimension == null) - throw new ArgumentException("No dimension found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageMeasures)) + { + var dimension = _measureService.GetMeasureDimensionById(id); - if (dimension.Id == _measureSettings.BaseDimensionId) - return Content(_localizationService.GetResource("Admin.Configuration.Measures.Dimensions.CantDeletePrimary")); + if (dimension.Id == _measureSettings.BaseDimensionId) + { + return Content(T("Admin.Configuration.Measures.Dimensions.CantDeletePrimary")); + } - _measureService.DeleteMeasureDimension(dimension); + _measureService.DeleteMeasureDimension(dimension); + } return Dimensions(command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs index be6c73a92b..eb0b111b06 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/MessageTemplateController.cs @@ -162,15 +162,21 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, MessageTemplateListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMessageTemplates)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var messageTemplates = _messageTemplateService.GetAllMessageTemplates(model.SearchStoreId); - var gridModel = new GridModel + if (_permissionService.Authorize(StandardPermissionProvider.ManageMessageTemplates)) { - Data = messageTemplates.Select(x => x.ToModel()), - Total = messageTemplates.Count - }; + var messageTemplates = _messageTemplateService.GetAllMessageTemplates(model.SearchStoreId); + + gridModel.Data = messageTemplates.Select(x => x.ToModel()); + gridModel.Total = messageTemplates.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs index 2ac51b9184..2d12ecc9d6 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsController.cs @@ -115,26 +115,38 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, NewsItemListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageNews)) + { + var news = _newsService.GetAllNews(0, model.SearchStoreId, command.Page - 1, command.PageSize, true); + + gridModel.Data = news.Select(x => + { + var m = x.ToModel(); + + if (x.StartDateUtc.HasValue) + m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); + + if (x.EndDateUtc.HasValue) + m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); + + m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + m.LanguageName = x.Language.Name; + m.Comments = x.ApprovedCommentCount + x.NotApprovedCommentCount; + + return m; + }); + + gridModel.Total = news.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var news = _newsService.GetAllNews(0, model.SearchStoreId, command.Page - 1, command.PageSize, true); - var gridModel = new GridModel - { - Data = news.Select(x => - { - var m = x.ToModel(); - if (x.StartDateUtc.HasValue) - m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); - if (x.EndDateUtc.HasValue) - m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); - m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - m.LanguageName = x.Language.Name; - m.Comments = x.ApprovedCommentCount + x.NotApprovedCommentCount; - return m; - }), - Total = news.TotalCount - }; return new JsonResult { Data = gridModel @@ -297,47 +309,54 @@ public ActionResult Comments(int? filterByNewsItemId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Comments(int? filterByNewsItemId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) - return AccessDeniedView(); + var gridModel = new GridModel(); - IList comments; - if (filterByNewsItemId.HasValue) - { - //filter comments by news item - var newsItem = _newsService.GetNewsById(filterByNewsItemId.Value); - comments = newsItem.NewsComments.OrderBy(bc => bc.CreatedOnUtc).ToList(); - } - else - { - //load all news comments - comments = _customerContentService.GetAllCustomerContent(0, null); - } + if (_permissionService.Authorize(StandardPermissionProvider.ManageNews)) + { + IList comments; + if (filterByNewsItemId.HasValue) + { + //filter comments by news item + var newsItem = _newsService.GetNewsById(filterByNewsItemId.Value); + comments = newsItem.NewsComments.OrderBy(bc => bc.CreatedOnUtc).ToList(); + } + else + { + //load all news comments + comments = _customerContentService.GetAllCustomerContent(0, null); + } - var gridModel = new GridModel - { - Data = comments.PagedForCommand(command).Select(newsComment => - { - var commentModel = new NewsCommentModel(); + gridModel.Data = comments.PagedForCommand(command).Select(newsComment => + { + var commentModel = new NewsCommentModel(); var customer = _customerService.GetCustomerById(newsComment.CustomerId); - commentModel.Id = newsComment.Id; - commentModel.NewsItemId = newsComment.NewsItemId; - commentModel.NewsItemTitle = newsComment.NewsItem.Title; - commentModel.CustomerId = newsComment.CustomerId; - commentModel.IpAddress = newsComment.IpAddress; - commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(newsComment.CreatedOnUtc, DateTimeKind.Utc); - commentModel.CommentTitle = newsComment.CommentTitle; - commentModel.CommentText = Core.Html.HtmlUtils.FormatText(newsComment.CommentText, false, true, false, false, false, false); + commentModel.Id = newsComment.Id; + commentModel.NewsItemId = newsComment.NewsItemId; + commentModel.NewsItemTitle = newsComment.NewsItem.Title; + commentModel.CustomerId = newsComment.CustomerId; + commentModel.IpAddress = newsComment.IpAddress; + commentModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(newsComment.CreatedOnUtc, DateTimeKind.Utc); + commentModel.CommentTitle = newsComment.CommentTitle; + commentModel.CommentText = Core.Html.HtmlUtils.FormatText(newsComment.CommentText, false, true, false, false, false, false); if (customer == null) commentModel.CustomerName = "".NaIfEmpty(); else commentModel.CustomerName = customer.GetFullName(); - return commentModel; - }), - Total = comments.Count, - }; + return commentModel; + }); + + gridModel.Total = comments.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel @@ -347,17 +366,16 @@ public ActionResult Comments(int? filterByNewsItemId, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult CommentDelete(int? filterByNewsItemId, int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) - return AccessDeniedView(); - - var comment = _customerContentService.GetCustomerContentById(id) as NewsComment; - if (comment == null) - throw new ArgumentException("No comment found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageNews)) + { + var comment = _customerContentService.GetCustomerContentById(id) as NewsComment; - var newsItem = comment.NewsItem; - _customerContentService.DeleteCustomerContent(comment); - //update totals - _newsService.UpdateCommentTotals(newsItem); + var newsItem = comment.NewsItem; + _customerContentService.DeleteCustomerContent(comment); + + //update totals + _newsService.UpdateCommentTotals(newsItem); + } return Comments(filterByNewsItemId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs index e8e92184a2..9c688979ea 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/NewsLetterSubscriptionController.cs @@ -68,7 +68,7 @@ public ActionResult List() var store = _storeService.GetStoreById(x.StoreId); m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - m.StoreName = store != null ? store.Name : "Unknown"; + m.StoreName = store != null ? store.Name : "".NaIfEmpty(); return m; }), @@ -80,26 +80,33 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult SubscriptionList(GridCommand command, NewsLetterSubscriptionListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var newsletterSubscriptions = _newsLetterSubscriptionService.GetAllNewsLetterSubscriptions( - model.SearchEmail, command.Page - 1, command.PageSize, true, model.StoreId); + if (_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) + { + var newsletterSubscriptions = _newsLetterSubscriptionService.GetAllNewsLetterSubscriptions( + model.SearchEmail, command.Page - 1, command.PageSize, true, model.StoreId); - var gridModel = new GridModel - { - Data = newsletterSubscriptions.Select(x => + gridModel.Data = newsletterSubscriptions.Select(x => { var m = x.ToModel(); var store = _storeService.GetStoreById(x.StoreId); m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - m.StoreName = store != null ? store.Name : "Unknown"; + m.StoreName = store != null ? store.Name : "".NaIfEmpty(); return m; - }), - Total = newsletterSubscriptions.TotalCount - }; + }); + + gridModel.Total = newsletterSubscriptions.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel @@ -109,39 +116,36 @@ public ActionResult SubscriptionList(GridCommand command, NewsLetterSubscription [GridAction(EnableCustomBinding = true)] public ActionResult SubscriptionUpdate(NewsLetterSubscriptionModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) - return AccessDeniedView(); - - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + if (_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionById(model.Id); - subscription.Email = model.Email; - subscription.Active = model.Active; + var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionById(model.Id); + subscription.Email = model.Email; + subscription.Active = model.Active; - _newsLetterSubscriptionService.UpdateNewsLetterSubscription(subscription); + _newsLetterSubscriptionService.UpdateNewsLetterSubscription(subscription); + } var listModel = new NewsLetterSubscriptionListModel(); PrepareNewsLetterSubscriptionListModel(listModel); - return SubscriptionList(command, listModel); + return SubscriptionList(command, listModel); } [GridAction(EnableCustomBinding = true)] public ActionResult SubscriptionDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) - return AccessDeniedView(); - - var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionById(id); - if (subscription == null) - throw new ArgumentException("No subscription found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers)) + { + var subscription = _newsLetterSubscriptionService.GetNewsLetterSubscriptionById(id); - _newsLetterSubscriptionService.DeleteNewsLetterSubscription(subscription); + _newsLetterSubscriptionService.DeleteNewsLetterSubscription(subscription); + } var listModel = new NewsLetterSubscriptionListModel(); PrepareNewsLetterSubscriptionListModel(listModel); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/OnlineCustomerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/OnlineCustomerController.cs index 222bb0863e..3aca0ddd54 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/OnlineCustomerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/OnlineCustomerController.cs @@ -81,27 +81,35 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCustomers)) + { + var lastActivityFrom = DateTime.UtcNow.AddMinutes(-_customerSettings.OnlineCustomerMinutes); + var customers = _customerService.GetOnlineCustomers(lastActivityFrom, null, command.Page - 1, command.PageSize); + + model.Data = customers.Select(x => + { + return new OnlineCustomerModel + { + Id = x.Id, + CustomerInfo = x.IsRegistered() ? x.Email : T("Admin.Customers.Guest").Text, + LastIpAddress = x.LastIpAddress, + Location = _geoCountryLookup.LookupCountryName(x.LastIpAddress), + LastActivityDate = _dateTimeHelper.ConvertToUserTime(x.LastActivityDateUtc, DateTimeKind.Utc), + LastVisitedPage = x.GetAttribute(SystemCustomerAttributeNames.LastVisitedPage) + }; + }); + + model.Total = customers.TotalCount; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var customers = _customerService.GetOnlineCustomers(DateTime.UtcNow.AddMinutes(-_customerSettings.OnlineCustomerMinutes), - null, command.Page - 1, command.PageSize); - var model = new GridModel - { - Data = customers.Select(x => - { - return new OnlineCustomerModel() - { - Id = x.Id, - CustomerInfo = x.IsRegistered() ? x.Email : _localizationService.GetResource("Admin.Customers.Guest"), - LastIpAddress = x.LastIpAddress, - Location = _geoCountryLookup.LookupCountryName(x.LastIpAddress), - LastActivityDate = _dateTimeHelper.ConvertToUserTime(x.LastActivityDateUtc, DateTimeKind.Utc), - LastVisitedPage = x.GetAttribute(SystemCustomerAttributeNames.LastVisitedPage) - }; - }), - Total = customers.TotalCount - }; return new JsonResult { Data = model diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs index f7adf8147d..911d14b44b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/OrderController.cs @@ -809,57 +809,65 @@ public ActionResult List(OrderListModel model) [GridAction(EnableCustomBinding = true)] public ActionResult OrderList(GridCommand command, OrderListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); - DateTime? startDateValue = (model.StartDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); - DateTime? endDateValue = (model.EndDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + DateTime? startDateValue = (model.StartDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); + DateTime? endDateValue = (model.EndDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); - var orderStatusIds = model.OrderStatusIds.ToIntArray(); - var paymentStatusIds = model.PaymentStatusIds.ToIntArray(); - var shippingStatusIds = model.ShippingStatusIds.ToIntArray(); + var orderStatusIds = model.OrderStatusIds.ToIntArray(); + var paymentStatusIds = model.PaymentStatusIds.ToIntArray(); + var shippingStatusIds = model.ShippingStatusIds.ToIntArray(); - var orders = _orderService.SearchOrders(model.StoreId, 0, startDateValue, endDateValue, orderStatusIds, paymentStatusIds, shippingStatusIds, - model.CustomerEmail, model.OrderGuid, model.OrderNumber, command.Page - 1, command.PageSize, model.CustomerName); + var orders = _orderService.SearchOrders(model.StoreId, 0, startDateValue, endDateValue, orderStatusIds, paymentStatusIds, shippingStatusIds, + model.CustomerEmail, model.OrderGuid, model.OrderNumber, command.Page - 1, command.PageSize, model.CustomerName); - var gridModel = new GridModel - { - Data = orders.Select(x => - { + gridModel.Data = orders.Select(x => + { var store = _storeService.GetStoreById(x.StoreId); - return new OrderModel - { - Id = x.Id, - OrderNumber = x.GetOrderNumber(), + return new OrderModel + { + Id = x.Id, + OrderNumber = x.GetOrderNumber(), StoreName = (store != null ? store.Name : "".NaIfEmpty()), - OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false), - OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext), - PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext), - ShippingStatus = x.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext), + OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal, true, false), + OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext), + PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext), + ShippingStatus = x.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext), CustomerName = x.BillingAddress.GetFullName(), CustomerEmail = x.BillingAddress.Email, - CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), + CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc), HasNewPaymentNotification = x.HasNewPaymentNotification - }; - }), - Total = orders.TotalCount - }; + }; + }); + + gridModel.Total = orders.TotalCount; + + //summary report + //implemented as a workaround described here: http://www.telerik.com/community/forums/aspnet-mvc/grid/gridmodel-aggregates-how-to-use.aspx + var reportSummary = _orderReportService.GetOrderAverageReportLine(model.StoreId, orderStatusIds, + paymentStatusIds, shippingStatusIds, startDateValue, endDateValue, model.CustomerEmail); + + var profit = _orderReportService.ProfitReport(model.StoreId, orderStatusIds, + paymentStatusIds, shippingStatusIds, startDateValue, endDateValue, model.CustomerEmail); + + var aggregator = new OrderModel + { + aggregatorprofit = _priceFormatter.FormatPrice(profit, true, false), + aggregatortax = _priceFormatter.FormatPrice(reportSummary.SumTax, true, false), + aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false) + }; + + gridModel.Aggregates = aggregator; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - //summary report - //implemented as a workaround described here: http://www.telerik.com/community/forums/aspnet-mvc/grid/gridmodel-aggregates-how-to-use.aspx - var reportSummary = _orderReportService.GetOrderAverageReportLine(model.StoreId, orderStatusIds, - paymentStatusIds, shippingStatusIds, startDateValue, endDateValue, model.CustomerEmail); - - var profit = _orderReportService.ProfitReport(model.StoreId, orderStatusIds, - paymentStatusIds, shippingStatusIds, startDateValue, endDateValue, model.CustomerEmail); - - var aggregator = new OrderModel() - { - aggregatorprofit = _priceFormatter.FormatPrice(profit, true, false), - aggregatortax = _priceFormatter.FormatPrice(reportSummary.SumTax, true, false), - aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false) - }; - gridModel.Aggregates = aggregator; return new JsonResult { Data = gridModel @@ -1676,36 +1684,46 @@ public ActionResult AddProductToOrder(int orderId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AddProductToOrder(GridCommand command, OrderModel.AddOrderProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var gridModel = new GridModel(); - var searchContext = new ProductSearchContext() + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) { - CategoryIds = new List() { model.SearchCategoryId }, - ManufacturerId = model.SearchManufacturerId, - Keywords = model.SearchProductName, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null - }; + var searchContext = new ProductSearchContext + { + CategoryIds = new List { model.SearchCategoryId }, + ManufacturerId = model.SearchManufacturerId, + Keywords = model.SearchProductName, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null + }; - var products = _productService.SearchProducts(searchContext); - gridModel.Data = products.Select(x => - { - var productModel = new OrderModel.AddOrderProductModel.ProductModel - { - Id = x.Id, - Name = x.Name, - Sku = x.Sku, - ProductTypeName = x.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = x.ProductTypeLabelHint - }; + var products = _productService.SearchProducts(searchContext); + + gridModel.Data = products.Select(x => + { + var productModel = new OrderModel.AddOrderProductModel.ProductModel + { + Id = x.Id, + Name = x.Name, + Sku = x.Sku, + ProductTypeName = x.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = x.ProductTypeLabelHint + }; + + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - return productModel; - }); - gridModel.Total = products.TotalCount; return new JsonResult { Data = gridModel @@ -1983,23 +2001,29 @@ public ActionResult ShipmentList() [GridAction(EnableCustomBinding = true)] public ActionResult ShipmentListSelect(GridCommand command, ShipmentListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); - DateTime? startDateValue = (model.StartDate == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + DateTime? startDateValue = (model.StartDate == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); - DateTime? endDateValue = (model.EndDate == null) ? null - :(DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + DateTime? endDateValue = (model.EndDate == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + + //load shipments + var shipments = _shipmentService.GetAllShipments(model.TrackingNumber, startDateValue, endDateValue, command.Page - 1, command.PageSize); + + gridModel.Data = shipments.Select(shipment => PrepareShipmentModel(shipment, false, false)); + gridModel.Total = shipments.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - //load shipments - var shipments = _shipmentService.GetAllShipments(model.TrackingNumber, startDateValue, endDateValue, - command.Page - 1, command.PageSize); - var gridModel = new GridModel - { - Data = shipments.Select(shipment => PrepareShipmentModel(shipment, false, false)), - Total = shipments.TotalCount - }; return new JsonResult { Data = gridModel @@ -2009,24 +2033,29 @@ public ActionResult ShipmentListSelect(GridCommand command, ShipmentListModel mo [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ShipmentsSelect(int orderId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var model = new GridModel(); - var order = _orderService.GetOrderById(orderId); - if (order == null) - throw new ArgumentException("No order found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var order = _orderService.GetOrderById(orderId); - //shipments - var shipmentModels = new List(); - var shipments = order.Shipments.OrderBy(s => s.CreatedOnUtc).ToList(); - foreach (var shipment in shipments) - shipmentModels.Add(PrepareShipmentModel(shipment, false, false)); + var shipmentModels = new List(); + var shipments = order.Shipments.OrderBy(s => s.CreatedOnUtc).ToList(); - var model = new GridModel - { - Data = shipmentModels, - Total = shipmentModels.Count - }; + foreach (var shipment in shipments) + { + shipmentModels.Add(PrepareShipmentModel(shipment, false, false)); + } + + model.Data = shipmentModels; + model.Total = shipmentModels.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -2311,37 +2340,40 @@ public ActionResult PdfPackagingSlips(bool all, string selectedIds = null) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult OrderNotesSelect(int orderId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var model = new GridModel(); - var order = _orderService.GetOrderById(orderId); - if (order == null) - throw new ArgumentException("No order found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var order = _orderService.GetOrderById(orderId); - var orderNoteModels = new List(); + var orderNoteModels = new List(); - foreach (var orderNote in order.OrderNotes.OrderByDescending(on => on.CreatedOnUtc)) - { - orderNoteModels.Add(new OrderModel.OrderNote - { - Id = orderNote.Id, - OrderId = orderNote.OrderId, - DisplayToCustomer = orderNote.DisplayToCustomer, - Note = orderNote.FormatOrderNoteText(), - CreatedOn = _dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc) - }); - } + foreach (var orderNote in order.OrderNotes.OrderByDescending(on => on.CreatedOnUtc)) + { + orderNoteModels.Add(new OrderModel.OrderNote + { + Id = orderNote.Id, + OrderId = orderNote.OrderId, + DisplayToCustomer = orderNote.DisplayToCustomer, + Note = orderNote.FormatOrderNoteText(), + CreatedOn = _dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc) + }); + } - var model = new GridModel - { - Data = orderNoteModels, - Total = orderNoteModels.Count - }; + model.Data = orderNoteModels; + model.Total = orderNoteModels.Count; - if (order.HasNewPaymentNotification) + if (order.HasNewPaymentNotification) + { + order.HasNewPaymentNotification = false; + _orderService.UpdateOrder(order); + } + } + else { - order.HasNewPaymentNotification = false; - _orderService.UpdateOrder(order); + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); } return new JsonResult @@ -2385,17 +2417,13 @@ public ActionResult OrderNoteAdd(int orderId, bool displayToCustomer, string mes [GridAction(EnableCustomBinding = true)] public ActionResult OrderNoteDelete(int orderId, int orderNoteId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); - - var order = _orderService.GetOrderById(orderId); - if (order == null) - throw new ArgumentException("No order found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var order = _orderService.GetOrderById(orderId); + var orderNote = order.OrderNotes.Where(on => on.Id == orderNoteId).FirstOrDefault(); - var orderNote = order.OrderNotes.Where(on => on.Id == orderNoteId).FirstOrDefault(); - if (orderNote == null) - throw new ArgumentException("No order note found with the specified id"); - _orderService.DeleteOrderNote(orderNote); + _orderService.DeleteOrderNote(orderNote); + } return OrderNotesSelect(orderId, command); } @@ -2433,12 +2461,14 @@ protected IList GetBestsellersBriefReportModel(int r return model; } - public ActionResult BestsellersBriefReportByQuantity() + + public ActionResult BestsellersBriefReportByQuantity() { var model = GetBestsellersBriefReportModel(5, 1); return PartialView(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult BestsellersBriefReportByQuantityList(GridCommand command) { var model = GetBestsellersBriefReportModel(5, 1); @@ -2452,12 +2482,14 @@ public ActionResult BestsellersBriefReportByQuantityList(GridCommand command) Data = gridModel }; } - public ActionResult BestsellersBriefReportByAmount() + + public ActionResult BestsellersBriefReportByAmount() { var model = GetBestsellersBriefReportModel(5, 2); return PartialView(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult BestsellersBriefReportByAmountList(GridCommand command) { var model = GetBestsellersBriefReportModel(5, 2); @@ -2496,27 +2528,29 @@ public ActionResult BestsellersReport() return View(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult BestsellersReportList(GridCommand command, BestsellersReportModel model) { DateTime? startDateValue = (model.StartDate == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); DateTime? endDateValue = (model.EndDate == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null; PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null; var items = _orderReportService.BestSellersReport(0, startDateValue, endDateValue, orderStatus, paymentStatus, null, model.BillingCountryId, 100, 2, true); + var gridModel = new GridModel { Data = items.Select(x => { var product = _productService.GetProductById(x.ProductId); - var m = new BestsellersReportLineModel() + var m = new BestsellersReportLineModel { ProductId = x.ProductId, TotalAmount = _priceFormatter.FormatPrice(x.TotalAmount, true, false), @@ -2533,6 +2567,7 @@ public ActionResult BestsellersReportList(GridCommand command, BestsellersReport }), Total = items.Count }; + return new JsonResult { Data = gridModel @@ -2546,17 +2581,18 @@ public ActionResult NeverSoldReport() var model = new NeverSoldReportModel(); return View(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult NeverSoldReportList(GridCommand command, NeverSoldReportModel model) { DateTime? startDateValue = (model.StartDate == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone); DateTime? endDateValue = (model.EndDate == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); - var items = _orderReportService.ProductsNeverSold(startDateValue, endDateValue, - command.Page - 1, command.PageSize, true); + var items = _orderReportService.ProductsNeverSold(startDateValue, endDateValue, command.Page - 1, command.PageSize, true); + var gridModel = new GridModel { Data = items.Select(x => @@ -2572,6 +2608,7 @@ public ActionResult NeverSoldReportList(GridCommand command, NeverSoldReportMode }), Total = items.TotalCount }; + return new JsonResult { Data = gridModel @@ -2680,13 +2717,15 @@ protected virtual IList GetOrderIncompleteReport return model; } - [ChildActionOnly] + + [ChildActionOnly] public ActionResult OrderIncompleteReport() { var model = GetOrderIncompleteReportModel(); return PartialView(model); } - [GridAction(EnableCustomBinding = true)] + + [GridAction(EnableCustomBinding = true)] public ActionResult OrderIncompleteReportList(GridCommand command) { var model = GetOrderIncompleteReportModel(); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/PollController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/PollController.cs index c6c6abac89..efbf8ad4ad 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/PollController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/PollController.cs @@ -103,24 +103,36 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) + { + var polls = _pollService.GetPolls(0, false, command.Page - 1, command.PageSize, true); + + gridModel.Data = polls.Select(x => + { + var m = x.ToModel(); + + if (x.StartDateUtc.HasValue) + m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); + + if (x.EndDateUtc.HasValue) + m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); + + m.LanguageName = x.Language.Name; + + return m; + }); + + gridModel.Total = polls.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var polls = _pollService.GetPolls(0, false, command.Page - 1, command.PageSize, true); - var gridModel = new GridModel - { - Data = polls.Select(x => - { - var m = x.ToModel(); - if (x.StartDateUtc.HasValue) - m.StartDate = _dateTimeHelper.ConvertToUserTime(x.StartDateUtc.Value, DateTimeKind.Utc); - if (x.EndDateUtc.HasValue) - m.EndDate = _dateTimeHelper.ConvertToUserTime(x.EndDateUtc.Value, DateTimeKind.Utc); - m.LanguageName = x.Language.Name; - return m; - }), - Total = polls.TotalCount - }; return new JsonResult { Data = gridModel @@ -245,30 +257,35 @@ public ActionResult DeleteConfirmed(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult PollAnswers(int pollId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) - return AccessDeniedView(); + var model = new GridModel(); - var poll = _pollService.GetPollById(pollId); - if (poll == null) - throw new ArgumentException("No poll found with the specified id", "pollId"); + if (_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) + { + var poll = _pollService.GetPollById(pollId); + + var answers = poll.PollAnswers.OrderBy(x => x.DisplayOrder).ToList(); + + model.Data = answers.Select(x => + { + return new PollAnswerModel + { + Id = x.Id, + PollId = x.PollId, + Name = x.Name, + NumberOfVotes = x.NumberOfVotes, + DisplayOrder1 = x.DisplayOrder + }; + }); + + model.Total = answers.Count; + } + else + { + model.Data = Enumerable.Empty(); - var answers = poll.PollAnswers.OrderBy(x=>x.DisplayOrder).ToList(); + NotifyAccessDenied(); + } - var model = new GridModel - { - Data = answers.Select(x => - { - return new PollAnswerModel() - { - Id = x.Id, - PollId = x.PollId, - Name = x.Name, - NumberOfVotes = x.NumberOfVotes, - DisplayOrder1 = x.DisplayOrder - }; - }), - Total = answers.Count - }; return new JsonResult { Data = model @@ -279,23 +296,21 @@ public ActionResult PollAnswers(int pollId, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult PollAnswerUpdate(PollAnswerModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) - return AccessDeniedView(); - - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var pollAnswer = _pollService.GetPollAnswerById(model.Id); - var pollAnswer = _pollService.GetPollAnswerById(model.Id); - if (pollAnswer == null) - throw new ArgumentException("No poll answer found with the specified id", "id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } + + pollAnswer.Name = model.Name; + pollAnswer.DisplayOrder = model.DisplayOrder1; - pollAnswer.Name = model.Name; - pollAnswer.DisplayOrder = model.DisplayOrder1; - _pollService.UpdatePoll(pollAnswer.Poll); + _pollService.UpdatePoll(pollAnswer.Poll); + } return PollAnswers(pollAnswer.PollId, command); } @@ -303,44 +318,39 @@ public ActionResult PollAnswerUpdate(PollAnswerModel model, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult PollAnswerAdd(int pollId, PollAnswerModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) - return AccessDeniedView(); - - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + if (_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - var poll = _pollService.GetPollById(pollId); - if (poll == null) - throw new ArgumentException("No poll found with the specified id", "pollId"); + var poll = _pollService.GetPollById(pollId); - poll.PollAnswers.Add(new PollAnswer - { - Name = model.Name, - DisplayOrder = model.DisplayOrder1 - }); - _pollService.UpdatePoll(poll); + poll.PollAnswers.Add(new PollAnswer + { + Name = model.Name, + DisplayOrder = model.DisplayOrder1 + }); + + _pollService.UpdatePoll(poll); + } - return PollAnswers(poll.Id, command); + return PollAnswers(pollId, command); } [GridAction(EnableCustomBinding = true)] public ActionResult PollAnswerDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) - return AccessDeniedView(); - - var pollAnswer = _pollService.GetPollAnswerById(id); - if (pollAnswer == null) - throw new ArgumentException("No poll answer found with the specified id", "id"); - - int pollId = pollAnswer.PollId; - _pollService.DeletePollAnswer(pollAnswer); + var pollAnswer = _pollService.GetPollAnswerById(id); + var pollId = pollAnswer.PollId; + if (_permissionService.Authorize(StandardPermissionProvider.ManagePolls)) + { + _pollService.DeletePollAnswer(pollAnswer); + } return PollAnswers(pollId, command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductAttributeController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductAttributeController.cs index 9381bd50ee..ea2a6ae606 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductAttributeController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductAttributeController.cs @@ -91,29 +91,35 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productAttributes = _productAttributeService.GetAllProductAttributes(); + + gridModel.Data = productAttributes.Select(x => x.ToModel()); + gridModel.Total = productAttributes.Count(); + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var productAttributes = _productAttributeService.GetAllProductAttributes(); - var gridModel = new GridModel - { - Data = productAttributes.Select(x => x.ToModel()), - Total = productAttributes.Count() - }; return new JsonResult { Data = gridModel }; } - //create public ActionResult Create() { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) return AccessDeniedView(); var model = new ProductAttributeModel(); - //locales + AddLocales(_languageService, model.Locales); return View(model); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index e1f0748bfc..1371b0d5c8 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -895,67 +895,74 @@ public ActionResult List(ProductListModel model) return View(model); } - [HttpPost, GridAction(EnableCustomBinding = true)] - public ActionResult ProductList(GridCommand command, ProductListModel model) - { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + [HttpPost, GridAction(EnableCustomBinding = true)] + public ActionResult ProductList(GridCommand command, ProductListModel model) + { + var gridModel = new GridModel(); - var gridModel = new GridModel(); - var searchContext = new ProductSearchContext - { - ManufacturerId = model.SearchManufacturerId, - StoreId = model.SearchStoreId, - Keywords = model.SearchProductName, - SearchSku = !_catalogSettings.SuppressSkuSearch, - LanguageId = _workContext.WorkingLanguage.Id, - OrderBy = ProductSortingEnum.Position, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = (model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null), - WithoutCategories = model.SearchWithoutCategories, - WithoutManufacturers = model.SearchWithoutManufacturers, - IsPublished = model.SearchIsPublished, - HomePageProducts = model.SearchHomePageProducts - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var searchContext = new ProductSearchContext + { + ManufacturerId = model.SearchManufacturerId, + StoreId = model.SearchStoreId, + Keywords = model.SearchProductName, + SearchSku = !_catalogSettings.SuppressSkuSearch, + LanguageId = _workContext.WorkingLanguage.Id, + OrderBy = ProductSortingEnum.Position, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = (model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null), + WithoutCategories = model.SearchWithoutCategories, + WithoutManufacturers = model.SearchWithoutManufacturers, + IsPublished = model.SearchIsPublished, + HomePageProducts = model.SearchHomePageProducts + }; - if (model.SearchCategoryId > 0) - searchContext.CategoryIds.Add(model.SearchCategoryId); + if (model.SearchCategoryId > 0) + searchContext.CategoryIds.Add(model.SearchCategoryId); - if (command.SortDescriptors != null && command.SortDescriptors.Count > 0) - { - var sort = command.SortDescriptors.First(); - if (sort.Member == "Name") - { - searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.NameAsc : ProductSortingEnum.NameDesc); - } - else if (sort.Member == "Price") - { - searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.PriceAsc : ProductSortingEnum.PriceDesc); - } - else if (sort.Member == "CreatedOn") + if (command.SortDescriptors != null && command.SortDescriptors.Count > 0) { - searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.CreatedOnAsc : ProductSortingEnum.CreatedOn); + var sort = command.SortDescriptors.First(); + if (sort.Member == "Name") + { + searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.NameAsc : ProductSortingEnum.NameDesc); + } + else if (sort.Member == "Price") + { + searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.PriceAsc : ProductSortingEnum.PriceDesc); + } + else if (sort.Member == "CreatedOn") + { + searchContext.OrderBy = (sort.SortDirection == ListSortDirection.Ascending ? ProductSortingEnum.CreatedOnAsc : ProductSortingEnum.CreatedOn); + } } - } - var products = _productService.SearchProducts(searchContext); + var products = _productService.SearchProducts(searchContext); - gridModel.Data = products.Select(x => - { - var productModel = x.ToModel(); - productModel.FullDescription = ""; // Perf - PrepareProductPictureThumbnailModel(productModel, x); + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.FullDescription = ""; // Perf + PrepareProductPictureThumbnailModel(productModel, x); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); - productModel.UpdatedOn = _dateTimeHelper.ConvertToUserTime(x.UpdatedOnUtc, DateTimeKind.Utc); - productModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + productModel.UpdatedOn = _dateTimeHelper.ConvertToUserTime(x.UpdatedOnUtc, DateTimeKind.Utc); + productModel.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - return productModel; - }); + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); - gridModel.Total = products.TotalCount; + NotifyAccessDenied(); + } return new JsonResult { @@ -1288,30 +1295,35 @@ public ActionResult CopyProduct(ProductModel model) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductCategoryList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var productCategories = _categoryService.GetProductCategoriesByProductId(productId, true); - var productCategoriesModel = productCategories - .Select(x => - { - return new ProductModel.ProductCategoryModel() - { - Id = x.Id, - Category = _categoryService.GetCategoryById(x.CategoryId).GetCategoryBreadCrumb(_categoryService), - ProductId = x.ProductId, - CategoryId = x.CategoryId, - IsFeaturedProduct = x.IsFeaturedProduct, - DisplayOrder = x.DisplayOrder - }; - }) - .ToList(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productCategories = _categoryService.GetProductCategoriesByProductId(productId, true); + var productCategoriesModel = productCategories + .Select(x => + { + return new ProductModel.ProductCategoryModel() + { + Id = x.Id, + Category = _categoryService.GetCategoryById(x.CategoryId).GetCategoryBreadCrumb(_categoryService), + ProductId = x.ProductId, + CategoryId = x.CategoryId, + IsFeaturedProduct = x.IsFeaturedProduct, + DisplayOrder = x.DisplayOrder + }; + }) + .ToList(); - var model = new GridModel - { - Data = productCategoriesModel, - Total = productCategoriesModel.Count - }; + model.Data = productCategoriesModel; + model.Total = productCategoriesModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1322,23 +1334,23 @@ public ActionResult ProductCategoryList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult ProductCategoryInsert(GridCommand command, ProductModel.ProductCategoryModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productCategory = new ProductCategory + { + ProductId = model.ProductId, + CategoryId = Int32.Parse(model.Category), //use Category property (not CategoryId) because appropriate property is stored in it + IsFeaturedProduct = model.IsFeaturedProduct, + DisplayOrder = model.DisplayOrder + }; - var productCategory = new ProductCategory() - { - ProductId = model.ProductId, - CategoryId = Int32.Parse(model.Category), //use Category property (not CategoryId) because appropriate property is stored in it - IsFeaturedProduct = model.IsFeaturedProduct, - DisplayOrder = model.DisplayOrder - }; - _categoryService.InsertProductCategory(productCategory); + _categoryService.InsertProductCategory(productCategory); - - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), - model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); + var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), + model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); + _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); + } return ProductCategoryList(command, model.ProductId); } @@ -1346,27 +1358,25 @@ public ActionResult ProductCategoryInsert(GridCommand command, ProductModel.Prod [GridAction(EnableCustomBinding = true)] public ActionResult ProductCategoryUpdate(GridCommand command, ProductModel.ProductCategoryModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productCategory = _categoryService.GetProductCategoryById(model.Id); - var productCategory = _categoryService.GetProductCategoryById(model.Id); - if (productCategory == null) - throw new ArgumentException("No product category mapping found with the specified id"); - - bool categoryChanged = (Int32.Parse(model.Category) != productCategory.CategoryId); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var categoryChanged = (Int32.Parse(model.Category) != productCategory.CategoryId); - //use Category property (not CategoryId) because appropriate property is stored in it - productCategory.CategoryId = Int32.Parse(model.Category); - productCategory.IsFeaturedProduct = model.IsFeaturedProduct; - productCategory.DisplayOrder = model.DisplayOrder; - _categoryService.UpdateProductCategory(productCategory); + //use Category property (not CategoryId) because appropriate property is stored in it + productCategory.CategoryId = Int32.Parse(model.Category); + productCategory.IsFeaturedProduct = model.IsFeaturedProduct; + productCategory.DisplayOrder = model.DisplayOrder; + _categoryService.UpdateProductCategory(productCategory); - if (categoryChanged) - { - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), - model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); + if (categoryChanged) + { + var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedCategories), + model.Category, _catalogSettings.MostRecentlyUsedCategoriesMaxSize); - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); + _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedCategories, mru.ToString()); + } } return ProductCategoryList(command, productCategory.ProductId); @@ -1375,15 +1385,13 @@ public ActionResult ProductCategoryUpdate(GridCommand command, ProductModel.Prod [GridAction(EnableCustomBinding = true)] public ActionResult ProductCategoryDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var productCategory = _categoryService.GetProductCategoryById(id); - if (productCategory == null) - throw new ArgumentException("No product category mapping found with the specified id"); + var productCategory = _categoryService.GetProductCategoryById(id); + var productId = productCategory.ProductId; - var productId = productCategory.ProductId; - _categoryService.DeleteProductCategory(productCategory); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _categoryService.DeleteProductCategory(productCategory); + } return ProductCategoryList(command, productId); } @@ -1395,30 +1403,35 @@ public ActionResult ProductCategoryDelete(int id, GridCommand command) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductManufacturerList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var productManufacturers = _manufacturerService.GetProductManufacturersByProductId(productId, true); - var productManufacturersModel = productManufacturers - .Select(x => - { - return new ProductModel.ProductManufacturerModel() - { - Id = x.Id, - Manufacturer = _manufacturerService.GetManufacturerById(x.ManufacturerId).Name, - ProductId = x.ProductId, - ManufacturerId = x.ManufacturerId, - IsFeaturedProduct = x.IsFeaturedProduct, - DisplayOrder = x.DisplayOrder - }; - }) - .ToList(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productManufacturers = _manufacturerService.GetProductManufacturersByProductId(productId, true); + var productManufacturersModel = productManufacturers + .Select(x => + { + return new ProductModel.ProductManufacturerModel + { + Id = x.Id, + Manufacturer = _manufacturerService.GetManufacturerById(x.ManufacturerId).Name, + ProductId = x.ProductId, + ManufacturerId = x.ManufacturerId, + IsFeaturedProduct = x.IsFeaturedProduct, + DisplayOrder = x.DisplayOrder + }; + }) + .ToList(); - var model = new GridModel - { - Data = productManufacturersModel, - Total = productManufacturersModel.Count - }; + model.Data = productManufacturersModel; + model.Total = productManufacturersModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1429,22 +1442,23 @@ public ActionResult ProductManufacturerList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult ProductManufacturerInsert(GridCommand command, ProductModel.ProductManufacturerModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productManufacturer = new ProductManufacturer + { + ProductId = model.ProductId, + ManufacturerId = Int32.Parse(model.Manufacturer), //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it + IsFeaturedProduct = model.IsFeaturedProduct, + DisplayOrder = model.DisplayOrder + }; - var productManufacturer = new ProductManufacturer() - { - ProductId = model.ProductId, - ManufacturerId = Int32.Parse(model.Manufacturer), //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it - IsFeaturedProduct = model.IsFeaturedProduct, - DisplayOrder = model.DisplayOrder - }; - _manufacturerService.InsertProductManufacturer(productManufacturer); + _manufacturerService.InsertProductManufacturer(productManufacturer); - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), - model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); + var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), + model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); + _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); + } return ProductManufacturerList(command, model.ProductId); } @@ -1452,27 +1466,26 @@ public ActionResult ProductManufacturerInsert(GridCommand command, ProductModel. [GridAction(EnableCustomBinding = true)] public ActionResult ProductManufacturerUpdate(GridCommand command, ProductModel.ProductManufacturerModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productManufacturer = _manufacturerService.GetProductManufacturerById(model.Id); - var productManufacturer = _manufacturerService.GetProductManufacturerById(model.Id); - if (productManufacturer == null) - throw new ArgumentException("No product manufacturer mapping found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var manufacturerChanged = (Int32.Parse(model.Manufacturer) != productManufacturer.ManufacturerId); - bool manufacturerChanged = (Int32.Parse(model.Manufacturer) != productManufacturer.ManufacturerId); + //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it + productManufacturer.ManufacturerId = Int32.Parse(model.Manufacturer); + productManufacturer.IsFeaturedProduct = model.IsFeaturedProduct; + productManufacturer.DisplayOrder = model.DisplayOrder; - //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it - productManufacturer.ManufacturerId = Int32.Parse(model.Manufacturer); - productManufacturer.IsFeaturedProduct = model.IsFeaturedProduct; - productManufacturer.DisplayOrder = model.DisplayOrder; - _manufacturerService.UpdateProductManufacturer(productManufacturer); + _manufacturerService.UpdateProductManufacturer(productManufacturer); - if (manufacturerChanged) - { - var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), - model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); + if (manufacturerChanged) + { + var mru = new MostRecentlyUsedList(_workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.MostRecentlyUsedManufacturers), + model.Manufacturer, _catalogSettings.MostRecentlyUsedManufacturersMaxSize); - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); + _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.MostRecentlyUsedManufacturers, mru.ToString()); + } } return ProductManufacturerList(command, productManufacturer.ProductId); @@ -1481,15 +1494,13 @@ public ActionResult ProductManufacturerUpdate(GridCommand command, ProductModel. [GridAction(EnableCustomBinding = true)] public ActionResult ProductManufacturerDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productManufacturer = _manufacturerService.GetProductManufacturerById(id); + var productId = productManufacturer.ProductId; - var productManufacturer = _manufacturerService.GetProductManufacturerById(id); - if (productManufacturer == null) - throw new ArgumentException("No product manufacturer mapping found with the specified id"); - - var productId = productManufacturer.ProductId; - _manufacturerService.DeleteProductManufacturer(productManufacturer); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _manufacturerService.DeleteProductManufacturer(productManufacturer); + } return ProductManufacturerList(command, productId); } @@ -1501,34 +1512,39 @@ public ActionResult ProductManufacturerDelete(int id, GridCommand command) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult RelatedProductList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var relatedProducts = _productService.GetRelatedProductsByProductId1(productId, true); - var relatedProductsModel = relatedProducts - .Select(x => - { - var product2 = _productService.GetProductById(x.ProductId2); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var relatedProducts = _productService.GetRelatedProductsByProductId1(productId, true); + var relatedProductsModel = relatedProducts + .Select(x => + { + var product2 = _productService.GetProductById(x.ProductId2); - return new ProductModel.RelatedProductModel() - { - Id = x.Id, - ProductId2 = x.ProductId2, - Product2Name = product2.Name, - ProductTypeName = product2.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = product2.ProductTypeLabelHint, - DisplayOrder = x.DisplayOrder, - Product2Sku = product2.Sku, - Product2Published = product2.Published - }; - }) - .ToList(); + return new ProductModel.RelatedProductModel() + { + Id = x.Id, + ProductId2 = x.ProductId2, + Product2Name = product2.Name, + ProductTypeName = product2.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = product2.ProductTypeLabelHint, + DisplayOrder = x.DisplayOrder, + Product2Sku = product2.Sku, + Product2Published = product2.Published + }; + }) + .ToList(); - var model = new GridModel - { - Data = relatedProductsModel, - Total = relatedProductsModel.Count - }; + model.Data = relatedProductsModel; + model.Total = relatedProductsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1539,15 +1555,13 @@ public ActionResult RelatedProductList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult RelatedProductUpdate(GridCommand command, ProductModel.RelatedProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var relatedProduct = _productService.GetRelatedProductById(model.Id); - if (relatedProduct == null) - throw new ArgumentException("No related product found with the specified id"); + var relatedProduct = _productService.GetRelatedProductById(model.Id); - relatedProduct.DisplayOrder = model.DisplayOrder; - _productService.UpdateRelatedProduct(relatedProduct); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + relatedProduct.DisplayOrder = model.DisplayOrder; + _productService.UpdateRelatedProduct(relatedProduct); + } return RelatedProductList(command, relatedProduct.ProductId1); } @@ -1555,15 +1569,13 @@ public ActionResult RelatedProductUpdate(GridCommand command, ProductModel.Relat [GridAction(EnableCustomBinding = true)] public ActionResult RelatedProductDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var relatedProduct = _productService.GetRelatedProductById(id); - if (relatedProduct == null) - throw new ArgumentException("No related product found with the specified id"); + var relatedProduct = _productService.GetRelatedProductById(id); + var productId = relatedProduct.ProductId1; - var productId = relatedProduct.ProductId1; - _productService.DeleteRelatedProduct(relatedProduct); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productService.DeleteRelatedProduct(relatedProduct); + } return RelatedProductList(command, productId); } @@ -1623,36 +1635,45 @@ public ActionResult RelatedProductAddPopup(int productId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult RelatedProductAddPopupList(GridCommand command, ProductModel.AddRelatedProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var gridModel = new GridModel(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var ctx = new ProductSearchContext(); - var ctx = new ProductSearchContext(); + if (model.SearchCategoryId > 0) + ctx.CategoryIds.Add(model.SearchCategoryId); - if (model.SearchCategoryId > 0) - ctx.CategoryIds.Add(model.SearchCategoryId); + ctx.ManufacturerId = model.SearchManufacturerId; + ctx.StoreId = model.SearchStoreId; + ctx.Keywords = model.SearchProductName; + ctx.SearchSku = !_catalogSettings.SuppressSkuSearch; + ctx.LanguageId = _workContext.WorkingLanguage.Id; + ctx.OrderBy = ProductSortingEnum.Position; + ctx.PageIndex = command.Page - 1; + ctx.PageSize = command.PageSize; + ctx.ShowHidden = true; + ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; - ctx.ManufacturerId = model.SearchManufacturerId; - ctx.StoreId = model.SearchStoreId; - ctx.Keywords = model.SearchProductName; - ctx.SearchSku = !_catalogSettings.SuppressSkuSearch; - ctx.LanguageId = _workContext.WorkingLanguage.Id; - ctx.OrderBy = ProductSortingEnum.Position; - ctx.PageIndex = command.Page - 1; - ctx.PageSize = command.PageSize; - ctx.ShowHidden = true; - ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; + var products = _productService.SearchProducts(ctx); - var products = _productService.SearchProducts(ctx); - gridModel.Data = products.Select(x => + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - return productModel; - }); - gridModel.Total = products.TotalCount; return new JsonResult { Data = gridModel @@ -1732,33 +1753,38 @@ public ActionResult CreateAllMutuallyRelatedProducts(int productId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CrossSellProductList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var crossSellProducts = _productService.GetCrossSellProductsByProductId1(productId, true); - var crossSellProductsModel = crossSellProducts - .Select(x => - { - var product2 = _productService.GetProductById(x.ProductId2); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var crossSellProducts = _productService.GetCrossSellProductsByProductId1(productId, true); + var crossSellProductsModel = crossSellProducts + .Select(x => + { + var product2 = _productService.GetProductById(x.ProductId2); - return new ProductModel.CrossSellProductModel() - { - Id = x.Id, - ProductId2 = x.ProductId2, - Product2Name = product2.Name, - ProductTypeName = product2.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = product2.ProductTypeLabelHint, - Product2Sku = product2.Sku, - Product2Published = product2.Published - }; - }) - .ToList(); + return new ProductModel.CrossSellProductModel + { + Id = x.Id, + ProductId2 = x.ProductId2, + Product2Name = product2.Name, + ProductTypeName = product2.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = product2.ProductTypeLabelHint, + Product2Sku = product2.Sku, + Product2Published = product2.Published + }; + }) + .ToList(); - var model = new GridModel - { - Data = crossSellProductsModel, - Total = crossSellProductsModel.Count - }; + model.Data = crossSellProductsModel; + model.Total = crossSellProductsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1769,15 +1795,13 @@ public ActionResult CrossSellProductList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult CrossSellProductDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var crossSellProduct = _productService.GetCrossSellProductById(id); - if (crossSellProduct == null) - throw new ArgumentException("No cross-sell product found with the specified id"); + var crossSellProduct = _productService.GetCrossSellProductById(id); + var productId = crossSellProduct.ProductId1; - var productId = crossSellProduct.ProductId1; - _productService.DeleteCrossSellProduct(crossSellProduct); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productService.DeleteCrossSellProduct(crossSellProduct); + } return CrossSellProductList(command, productId); } @@ -1837,37 +1861,45 @@ public ActionResult CrossSellProductAddPopup(int productId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CrossSellProductAddPopupList(GridCommand command, ProductModel.AddCrossSellProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var gridModel = new GridModel(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var ctx = new ProductSearchContext(); - var ctx = new ProductSearchContext(); + if (model.SearchCategoryId > 0) + ctx.CategoryIds.Add(model.SearchCategoryId); - if (model.SearchCategoryId > 0) - ctx.CategoryIds.Add(model.SearchCategoryId); + ctx.ManufacturerId = model.SearchManufacturerId; + ctx.StoreId = model.SearchStoreId; + ctx.Keywords = model.SearchProductName; + ctx.SearchSku = !_catalogSettings.SuppressSkuSearch; + ctx.LanguageId = _workContext.WorkingLanguage.Id; + ctx.OrderBy = ProductSortingEnum.Position; + ctx.PageIndex = command.Page - 1; + ctx.PageSize = command.PageSize; + ctx.ShowHidden = true; + ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; - ctx.ManufacturerId = model.SearchManufacturerId; - ctx.StoreId = model.SearchStoreId; - ctx.Keywords = model.SearchProductName; - ctx.SearchSku = !_catalogSettings.SuppressSkuSearch; - ctx.LanguageId = _workContext.WorkingLanguage.Id; - ctx.OrderBy = ProductSortingEnum.Position; - ctx.PageIndex = command.Page - 1; - ctx.PageSize = command.PageSize; - ctx.ShowHidden = true; - ctx.ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null; + var products = _productService.SearchProducts(ctx); - var products = _productService.SearchProducts(ctx); + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + + return productModel; + }); - gridModel.Data = products.Select(x => + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - return productModel; - }); - gridModel.Total = products.TotalCount; return new JsonResult { Data = gridModel @@ -1946,38 +1978,43 @@ public ActionResult CreateAllMutuallyCrossSellProducts(int productId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AssociatedProductList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var searchContext = new ProductSearchContext() + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) { - ParentGroupedProductId = productId, - PageSize = int.MaxValue, - ShowHidden = true - }; - - var associatedProducts = _productService.SearchProducts(searchContext); - var associatedProductsModel = associatedProducts - .Select(x => + var searchContext = new ProductSearchContext { - return new ProductModel.AssociatedProductModel() + ParentGroupedProductId = productId, + PageSize = int.MaxValue, + ShowHidden = true + }; + + var associatedProducts = _productService.SearchProducts(searchContext); + var associatedProductsModel = associatedProducts + .Select(x => { - Id = x.Id, - ProductName = x.Name, - ProductTypeName = x.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = x.ProductTypeLabelHint, - DisplayOrder = x.DisplayOrder, - Sku = x.Sku, - Published = x.Published - }; - }) - .ToList(); + return new ProductModel.AssociatedProductModel + { + Id = x.Id, + ProductName = x.Name, + ProductTypeName = x.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = x.ProductTypeLabelHint, + DisplayOrder = x.DisplayOrder, + Sku = x.Sku, + Published = x.Published + }; + }) + .ToList(); - var model = new GridModel + model.Data = associatedProductsModel; + model.Total = associatedProductsModel.Count; + } + else { - Data = associatedProductsModel, - Total = associatedProductsModel.Count - }; + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1988,15 +2025,13 @@ public ActionResult AssociatedProductList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult AssociatedProductUpdate(GridCommand command, ProductModel.AssociatedProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var associatedProduct = _productService.GetProductById(model.Id); - if (associatedProduct == null) - throw new ArgumentException("No associated product found with the specified id"); - associatedProduct.DisplayOrder = model.DisplayOrder; - _productService.UpdateProduct(associatedProduct); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + associatedProduct.DisplayOrder = model.DisplayOrder; + _productService.UpdateProduct(associatedProduct); + } return AssociatedProductList(command, associatedProduct.ParentGroupedProductId); } @@ -2004,19 +2039,16 @@ public ActionResult AssociatedProductUpdate(GridCommand command, ProductModel.As [GridAction(EnableCustomBinding = true)] public ActionResult AssociatedProductDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var product = _productService.GetProductById(id); - if (product == null) - throw new ArgumentException("No associated product found with the specified id"); - var originalParentGroupedProductId = product.ParentGroupedProductId; - product.ParentGroupedProductId = 0; - _productService.UpdateProduct(product); - - return AssociatedProductList(command, originalParentGroupedProductId); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + product.ParentGroupedProductId = 0; + _productService.UpdateProduct(product); + } + + return AssociatedProductList(command, originalParentGroupedProductId); } public ActionResult AssociatedProductAddPopup(int productId) @@ -2057,33 +2089,41 @@ public ActionResult AssociatedProductAddPopup(int productId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AssociatedProductAddPopupList(GridCommand command, ProductModel.AddAssociatedProductModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var searchContext = new ProductSearchContext() - { - CategoryIds = new List() { model.SearchCategoryId }, - ManufacturerId = model.SearchManufacturerId, - StoreId = model.SearchStoreId, - Keywords = model.SearchProductName, - SearchSku = !_catalogSettings.SuppressSkuSearch, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var searchContext = new ProductSearchContext + { + CategoryIds = new List() { model.SearchCategoryId }, + ManufacturerId = model.SearchManufacturerId, + StoreId = model.SearchStoreId, + Keywords = model.SearchProductName, + SearchSku = !_catalogSettings.SuppressSkuSearch, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null + }; + + var products = _productService.SearchProducts(searchContext); - var products = _productService.SearchProducts(searchContext); + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); - var gridModel = new GridModel(); - gridModel.Data = products.Select(x => + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); - return productModel; - }); - gridModel.Total = products.TotalCount; + NotifyAccessDenied(); + } return new JsonResult { @@ -2222,34 +2262,39 @@ private void SaveFilteredAttributes(ProductBundleItem bundleItem, FormCollection [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult BundleItemList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var bundleItems = _productService.GetBundleItems(productId, true).Select(x => x.Item); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var bundleItems = _productService.GetBundleItems(productId, true).Select(x => x.Item); - var bundleItemsModel = bundleItems.Select(x => - { - return new ProductModel.BundleItemModel() + var bundleItemsModel = bundleItems.Select(x => { - Id = x.Id, - ProductId = x.Product.Id, - ProductName = x.Product.Name, - ProductTypeName = x.Product.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = x.Product.ProductTypeLabelHint, - Sku = x.Product.Sku, - Quantity = x.Quantity, - Discount = x.Discount, - DisplayOrder = x.DisplayOrder, - Visible = x.Visible, - Published = x.Published - }; - }).ToList(); + return new ProductModel.BundleItemModel + { + Id = x.Id, + ProductId = x.Product.Id, + ProductName = x.Product.Name, + ProductTypeName = x.Product.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = x.Product.ProductTypeLabelHint, + Sku = x.Product.Sku, + Quantity = x.Quantity, + Discount = x.Discount, + DisplayOrder = x.DisplayOrder, + Visible = x.Visible, + Published = x.Published + }; + }).ToList(); - var model = new GridModel() + model.Data = bundleItemsModel; + model.Total = bundleItemsModel.Count; + } + else { - Data = bundleItemsModel, - Total = bundleItemsModel.Count - }; + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { Data = model }; } @@ -2257,15 +2302,12 @@ public ActionResult BundleItemList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult BundleItemDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var bundleItem = _productService.GetBundleItemById(id); - if (bundleItem == null) - throw new ArgumentException("No product bundle item found with the specified id"); - - _productService.DeleteBundleItem(bundleItem); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productService.DeleteBundleItem(bundleItem); + } return BundleItemList(command, bundleItem.BundleProductId); } @@ -2369,34 +2411,42 @@ public ActionResult BundleItemAddPopup(string btnId, string formId, ProductModel [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult BundleItemAddPopupList(GridCommand command, ProductModel.AddBundleItemModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var searchContext = new ProductSearchContext() - { - CategoryIds = new List() { model.SearchCategoryId }, - ManufacturerId = model.SearchManufacturerId, - StoreId = model.SearchStoreId, - Keywords = model.SearchProductName, - SearchSku = !_catalogSettings.SuppressSkuSearch, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var searchContext = new ProductSearchContext + { + CategoryIds = new List { model.SearchCategoryId }, + ManufacturerId = model.SearchManufacturerId, + StoreId = model.SearchStoreId, + Keywords = model.SearchProductName, + SearchSku = !_catalogSettings.SuppressSkuSearch, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null + }; - var products = _productService.SearchProducts(searchContext); + var products = _productService.SearchProducts(searchContext); - var gridModel = new GridModel(); - gridModel.Data = products.Select(x => + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + productModel.ProductSelectCheckboxClass = (!x.CanBeBundleItem() ? " hide" : ""); + + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); - productModel.ProductSelectCheckboxClass = (!x.CanBeBundleItem() ? " hide" : ""); + gridModel.Data = Enumerable.Empty(); - return productModel; - }); - gridModel.Total = products.TotalCount; + NotifyAccessDenied(); + } return new JsonResult { @@ -2498,29 +2548,34 @@ public ActionResult ProductPictureAdd(int pictureId, int displayOrder, int produ [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductPictureList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var productPictures = _productService.GetProductPicturesByProductId(productId); - var productPicturesModel = productPictures - .Select(x => - { - return new ProductModel.ProductPictureModel() - { - Id = x.Id, - ProductId = x.ProductId, - PictureId = x.PictureId, - PictureUrl = _pictureService.GetPictureUrl(x.PictureId), - DisplayOrder = x.DisplayOrder - }; - }) - .ToList(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productPictures = _productService.GetProductPicturesByProductId(productId); + var productPicturesModel = productPictures + .Select(x => + { + return new ProductModel.ProductPictureModel + { + Id = x.Id, + ProductId = x.ProductId, + PictureId = x.PictureId, + PictureUrl = _pictureService.GetPictureUrl(x.PictureId), + DisplayOrder = x.DisplayOrder + }; + }) + .ToList(); - var model = new GridModel - { - Data = productPicturesModel, - Total = productPicturesModel.Count - }; + model.Data = productPicturesModel; + model.Total = productPicturesModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -2531,15 +2586,13 @@ public ActionResult ProductPictureList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult ProductPictureUpdate(ProductModel.ProductPictureModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productPicture = _productService.GetProductPictureById(model.Id); - var productPicture = _productService.GetProductPictureById(model.Id); - if (productPicture == null) - throw new ArgumentException("No product picture found with the specified id"); - - productPicture.DisplayOrder = model.DisplayOrder; - _productService.UpdateProductPicture(productPicture); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + productPicture.DisplayOrder = model.DisplayOrder; + _productService.UpdateProductPicture(productPicture); + } return ProductPictureList(command, productPicture.ProductId); } @@ -2547,18 +2600,16 @@ public ActionResult ProductPictureUpdate(ProductModel.ProductPictureModel model, [GridAction(EnableCustomBinding = true)] public ActionResult ProductPictureDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var productPicture = _productService.GetProductPictureById(id); + var productId = productPicture.ProductId; - var productPicture = _productService.GetProductPictureById(id); - if (productPicture == null) - throw new ArgumentException("No product picture found with the specified id"); - - var productId = productPicture.ProductId; - _productService.DeleteProductPicture(productPicture); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productService.DeleteProductPicture(productPicture); - var picture = _pictureService.GetPictureById(productPicture.PictureId); - _pictureService.DeletePicture(picture); + var picture = _pictureService.GetPictureById(productPicture.PictureId); + _pictureService.DeletePicture(picture); + } return ProductPictureList(command, productId); } @@ -2589,51 +2640,56 @@ public ActionResult ProductSpecificationAttributeAdd(int specificationAttributeO [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductSpecAttrList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var productrSpecs = _specificationAttributeService.GetProductSpecificationAttributesByProductId(productId); + var model = new GridModel(); - var productrSpecsModel = productrSpecs - .Select(x => - { - var psaModel = new ProductSpecificationAttributeModel() - { - Id = x.Id, - SpecificationAttributeName = x.SpecificationAttributeOption.SpecificationAttribute.Name, - SpecificationAttributeOptionName = x.SpecificationAttributeOption.Name, - SpecificationAttributeOptionAttributeId = x.SpecificationAttributeOption.SpecificationAttributeId, - SpecificationAttributeOptionId = x.SpecificationAttributeOptionId, - AllowFiltering = x.AllowFiltering, - ShowOnProductPage = x.ShowOnProductPage, - DisplayOrder = x.DisplayOrder - }; - return psaModel; - }) - .ToList(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productrSpecs = _specificationAttributeService.GetProductSpecificationAttributesByProductId(productId); - foreach(var attr in productrSpecsModel) { + var productrSpecsModel = productrSpecs + .Select(x => + { + var psaModel = new ProductSpecificationAttributeModel + { + Id = x.Id, + SpecificationAttributeName = x.SpecificationAttributeOption.SpecificationAttribute.Name, + SpecificationAttributeOptionName = x.SpecificationAttributeOption.Name, + SpecificationAttributeOptionAttributeId = x.SpecificationAttributeOption.SpecificationAttributeId, + SpecificationAttributeOptionId = x.SpecificationAttributeOptionId, + AllowFiltering = x.AllowFiltering, + ShowOnProductPage = x.ShowOnProductPage, + DisplayOrder = x.DisplayOrder + }; + return psaModel; + }) + .ToList(); - var options = _specificationAttributeService.GetSpecificationAttributeOptionsBySpecificationAttribute(attr.SpecificationAttributeOptionAttributeId); + foreach (var attr in productrSpecsModel) + { + var options = _specificationAttributeService.GetSpecificationAttributeOptionsBySpecificationAttribute(attr.SpecificationAttributeOptionAttributeId); - foreach(var option in options) { + foreach (var option in options) + { + attr.SpecificationAttributeOptions.Add(new ProductSpecificationAttributeModel.SpecificationAttributeOption + { + id = option.Id, + name = option.Name, + text = option.Name + }); + } - attr.SpecificationAttributeOptions.Add(new ProductSpecificationAttributeModel.SpecificationAttributeOption() - { - id = option.Id, - name = option.Name, - text = option.Name - }); - } + attr.SpecificationAttributeOptionsJsonString = JsonConvert.SerializeObject(attr.SpecificationAttributeOptions); + } - attr.SpecificationAttributeOptionsJsonString = JsonConvert.SerializeObject(attr.SpecificationAttributeOptions); - } + model.Data = productrSpecsModel; + model.Total = productrSpecsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); - var model = new GridModel - { - Data = productrSpecsModel, - Total = productrSpecsModel.Count - }; + NotifyAccessDenied(); + } return new JsonResult { @@ -2642,17 +2698,17 @@ public ActionResult ProductSpecAttrList(GridCommand command, int productId) } [GridAction(EnableCustomBinding = true)] - public ActionResult ProductSpecAttrUpdate(int psaId, ProductSpecificationAttributeModel model, - GridCommand command) + public ActionResult ProductSpecAttrUpdate(int psaId, ProductSpecificationAttributeModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId); - var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId); - psa.AllowFiltering = model.AllowFiltering; - psa.ShowOnProductPage = model.ShowOnProductPage; - psa.DisplayOrder = model.DisplayOrder; - _specificationAttributeService.UpdateProductSpecificationAttribute(psa); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + psa.AllowFiltering = model.AllowFiltering; + psa.ShowOnProductPage = model.ShowOnProductPage; + psa.DisplayOrder = model.DisplayOrder; + _specificationAttributeService.UpdateProductSpecificationAttribute(psa); + } return ProductSpecAttrList(command, psa.ProductId); } @@ -2660,15 +2716,13 @@ public ActionResult ProductSpecAttrUpdate(int psaId, ProductSpecificationAttribu [GridAction(EnableCustomBinding = true)] public ActionResult ProductSpecAttrDelete(int psaId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId); - if (psa == null) - throw new ArgumentException("No specification attribute found with the specified id"); + var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId); + var productId = psa.ProductId; - var productId = psa.ProductId; - _specificationAttributeService.DeleteProductSpecificationAttribute(psa); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _specificationAttributeService.DeleteProductSpecificationAttribute(psa); + } return ProductSpecAttrList(command, productId); } @@ -2688,28 +2742,33 @@ public ActionResult ProductTags() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductTags(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var tags = _productTagService.GetAllProductTags() + .OrderByDescending(x => _productTagService.GetProductCount(x.Id, 0)) + .Select(x => + { + return new ProductTagModel + { + Id = x.Id, + Name = x.Name, + ProductCount = _productTagService.GetProductCount(x.Id, 0) + }; + }) + .ForCommand(command); + + model.Data = tags.PagedForCommand(command); + model.Total = tags.Count(); + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var tags = _productTagService.GetAllProductTags() - //order by product count - .OrderByDescending(x => _productTagService.GetProductCount(x.Id, 0)) - .Select(x => - { - return new ProductTagModel() - { - Id = x.Id, - Name = x.Name, - ProductCount = _productTagService.GetProductCount(x.Id, 0) - }; - }) - .ForCommand(command); - - var model = new GridModel - { - Data = tags.PagedForCommand(command), - Total = tags.Count() - }; return new JsonResult { Data = model @@ -2719,13 +2778,12 @@ public ActionResult ProductTags(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult ProductTagDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var tag = _productTagService.GetProductTagById(id); - var tag = _productTagService.GetProductTagById(id); - if (tag == null) - throw new ArgumentException("No product tag found with the specified id"); - _productTagService.DeleteProductTag(tag); + _productTagService.DeleteProductTag(tag); + } return ProductTags(command); } @@ -2799,21 +2857,29 @@ public ActionResult LowStockReport() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult LowStockReportList(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var allProducts = _productService.GetLowStockProducts(); - var model = new GridModel() + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) { - Data = allProducts.PagedForCommand(command).Select(x => + var allProducts = _productService.GetLowStockProducts(); + + model.Data = allProducts.PagedForCommand(command).Select(x => { var productModel = x.ToModel(); productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; - }), - Total = allProducts.Count - }; + }); + + model.Total = allProducts.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = model @@ -2860,47 +2926,54 @@ public ActionResult BulkEdit() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult BulkEditSelect(GridCommand command, BulkEditListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var searchContext = new ProductSearchContext + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) { - StoreId = model.SearchStoreId, - ManufacturerId = model.SearchManufacturerId, - Keywords = model.SearchProductName, - SearchSku = !_catalogSettings.SuppressSkuSearch, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null - }; + var searchContext = new ProductSearchContext + { + StoreId = model.SearchStoreId, + ManufacturerId = model.SearchManufacturerId, + Keywords = model.SearchProductName, + SearchSku = !_catalogSettings.SuppressSkuSearch, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null + }; - if (model.SearchCategoryId > 0) - searchContext.CategoryIds.Add(model.SearchCategoryId); + if (model.SearchCategoryId > 0) + searchContext.CategoryIds.Add(model.SearchCategoryId); - var products = _productService.SearchProducts(searchContext); - var gridModel = new GridModel(); + var products = _productService.SearchProducts(searchContext); - gridModel.Data = products.Select(x => - { - var productModel = new BulkEditProductModel() + gridModel.Data = products.Select(x => { - Id = x.Id, - Name = x.Name, - ProductTypeName = x.GetProductTypeLabel(_localizationService), - ProductTypeLabelHint = x.ProductTypeLabelHint, - Sku = x.Sku, - OldPrice = x.OldPrice, - Price = x.Price, - ManageInventoryMethod = x.ManageInventoryMethod.GetLocalizedEnum(_localizationService, _workContext.WorkingLanguage.Id), - StockQuantity = x.StockQuantity, - Published = x.Published - }; + var productModel = new BulkEditProductModel + { + Id = x.Id, + Name = x.Name, + ProductTypeName = x.GetProductTypeLabel(_localizationService), + ProductTypeLabelHint = x.ProductTypeLabelHint, + Sku = x.Sku, + OldPrice = x.OldPrice, + Price = x.Price, + ManageInventoryMethod = x.ManageInventoryMethod.GetLocalizedEnum(_localizationService, _workContext.WorkingLanguage.Id), + StockQuantity = x.StockQuantity, + Published = x.Published + }; - return productModel; - }); + return productModel; + }); + + gridModel.Total = products.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); - gridModel.Total = products.TotalCount; + NotifyAccessDenied(); + } return new JsonResult { @@ -2915,39 +2988,39 @@ public ActionResult BulkEditSave(GridCommand command, [Bind(Prefix = "deleted")]IEnumerable deletedProducts, BulkEditListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - if (updatedProducts != null) + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) { - foreach (var pModel in updatedProducts) + if (updatedProducts != null) { - //update - var product = _productService.GetProductById(pModel.Id); - if (product != null) + foreach (var pModel in updatedProducts) { - product.Sku = pModel.Sku; - product.Price = pModel.Price; - product.OldPrice = pModel.OldPrice; - product.StockQuantity = pModel.StockQuantity; - product.Published = pModel.Published; + var product = _productService.GetProductById(pModel.Id); + if (product != null) + { + product.Sku = pModel.Sku; + product.Price = pModel.Price; + product.OldPrice = pModel.OldPrice; + product.StockQuantity = pModel.StockQuantity; + product.Published = pModel.Published; - _productService.UpdateProduct(product); + _productService.UpdateProduct(product); + } } } - } - if (deletedProducts != null) - { - foreach (var pModel in deletedProducts) + if (deletedProducts != null) { - var product = _productService.GetProductById(pModel.Id); - if (product != null) + foreach (var pModel in deletedProducts) { - _productService.DeleteProduct(product); + var product = _productService.GetProductById(pModel.Id); + if (product != null) + { + _productService.DeleteProduct(product); + } } } } + return BulkEditSelect(command, model); } @@ -2958,64 +3031,67 @@ public ActionResult BulkEditSave(GridCommand command, [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult TierPriceList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var product = _productService.GetProductById(productId); - if (product == null) - throw new ArgumentException("No product variant found with the specified id"); + var model = new GridModel(); - var allStores = _services.StoreService.GetAllStores(); - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - string allRolesString = T("Admin.Catalog.Products.TierPrices.Fields.CustomerRole.AllRoles"); - string allStoresString = T("Admin.Common.StoresAll"); - string deletedString = "[{0}]".FormatInvariant(T("Admin.Common.Deleted")); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var product = _productService.GetProductById(productId); - var tierPricesModel = product.TierPrices - .OrderBy(x => x.StoreId) - .ThenBy(x => x.Quantity) - .ThenBy(x => x.CustomerRoleId) - .Select(x => - { - var tierPriceModel = new ProductModel.TierPriceModel + var allStores = _services.StoreService.GetAllStores(); + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + string allRolesString = T("Admin.Catalog.Products.TierPrices.Fields.CustomerRole.AllRoles"); + string allStoresString = T("Admin.Common.StoresAll"); + string deletedString = "[{0}]".FormatInvariant(T("Admin.Common.Deleted")); + + var tierPricesModel = product.TierPrices + .OrderBy(x => x.StoreId) + .ThenBy(x => x.Quantity) + .ThenBy(x => x.CustomerRoleId) + .Select(x => { - Id = x.Id, - StoreId = x.StoreId, - ProductId = x.ProductId, - CustomerRoleId = x.CustomerRoleId ?? 0, - Quantity = x.Quantity, - Price1 = x.Price - }; + var tierPriceModel = new ProductModel.TierPriceModel + { + Id = x.Id, + StoreId = x.StoreId, + ProductId = x.ProductId, + CustomerRoleId = x.CustomerRoleId ?? 0, + Quantity = x.Quantity, + Price1 = x.Price + }; - if (x.CustomerRoleId.HasValue) - { - var role = allCustomerRoles.FirstOrDefault(r => r.Id == x.CustomerRoleId.Value); - tierPriceModel.CustomerRole = (role == null ? allRolesString : role.Name); - } - else - { - tierPriceModel.CustomerRole = allRolesString; - } + if (x.CustomerRoleId.HasValue) + { + var role = allCustomerRoles.FirstOrDefault(r => r.Id == x.CustomerRoleId.Value); + tierPriceModel.CustomerRole = (role == null ? allRolesString : role.Name); + } + else + { + tierPriceModel.CustomerRole = allRolesString; + } - if (x.StoreId > 0) - { - var store = allStores.FirstOrDefault(s => s.Id == x.StoreId); - tierPriceModel.Store = (store == null ? deletedString : store.Name); - } - else - { - tierPriceModel.Store = allStoresString; - } + if (x.StoreId > 0) + { + var store = allStores.FirstOrDefault(s => s.Id == x.StoreId); + tierPriceModel.Store = (store == null ? deletedString : store.Name); + } + else + { + tierPriceModel.Store = allStoresString; + } - return tierPriceModel; - }) - .ToList(); + return tierPriceModel; + }) + .ToList(); - var model = new GridModel + model.Data = tierPricesModel; + model.Total = tierPricesModel.Count; + } + else { - Data = tierPricesModel, - Total = tierPricesModel.Count - }; + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -3026,27 +3102,26 @@ public ActionResult TierPriceList(GridCommand command, int productId) [GridAction(EnableCustomBinding = true)] public ActionResult TierPriceInsert(GridCommand command, ProductModel.TierPriceModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var product = _productService.GetProductById(model.ProductId); - var product = _productService.GetProductById(model.ProductId); - if (product == null) - throw new ArgumentException("No product found with the specified id"); + var tierPrice = new TierPrice + { + ProductId = model.ProductId, + // use Store property (not Store propertyId) because appropriate property is stored in it + StoreId = model.Store.ToInt(), + // use CustomerRole property (not CustomerRoleId) because appropriate property is stored in it + CustomerRoleId = model.CustomerRole.IsNumeric() && Int32.Parse(model.CustomerRole) != 0 ? Int32.Parse(model.CustomerRole) : (int?)null, + Quantity = model.Quantity, + Price = model.Price1 + }; - var tierPrice = new TierPrice() - { - ProductId = model.ProductId, - // use Store property (not Store propertyId) because appropriate property is stored in it - StoreId = model.Store.ToInt(), - // use CustomerRole property (not CustomerRoleId) because appropriate property is stored in it - CustomerRoleId = model.CustomerRole.IsNumeric() && Int32.Parse(model.CustomerRole) != 0 ? Int32.Parse(model.CustomerRole) : (int?)null, - Quantity = model.Quantity, - Price = model.Price1 - }; - _productService.InsertTierPrice(tierPrice); + _productService.InsertTierPrice(tierPrice); - //update "HasTierPrices" property - _productService.UpdateHasTierPricesProperty(product); + //update "HasTierPrices" property + _productService.UpdateHasTierPricesProperty(product); + } return TierPriceList(command, model.ProductId); } @@ -3054,20 +3129,19 @@ public ActionResult TierPriceInsert(GridCommand command, ProductModel.TierPriceM [GridAction(EnableCustomBinding = true)] public ActionResult TierPriceUpdate(GridCommand command, ProductModel.TierPriceModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var tierPrice = _productService.GetTierPriceById(model.Id); - if (tierPrice == null) - throw new ArgumentException("No tier price found with the specified id"); - //use Store property (not Store propertyId) because appropriate property is stored in it - tierPrice.StoreId = model.Store.ToInt(); - //use CustomerRole property (not CustomerRoleId) because appropriate property is stored in it - tierPrice.CustomerRoleId = model.CustomerRole.IsNumeric() && Int32.Parse(model.CustomerRole) != 0 ? Int32.Parse(model.CustomerRole) : (int?)null; - tierPrice.Quantity = model.Quantity; - tierPrice.Price = model.Price1; - _productService.UpdateTierPrice(tierPrice); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + //use Store property (not Store propertyId) because appropriate property is stored in it + tierPrice.StoreId = model.Store.ToInt(); + //use CustomerRole property (not CustomerRoleId) because appropriate property is stored in it + tierPrice.CustomerRoleId = model.CustomerRole.IsNumeric() && Int32.Parse(model.CustomerRole) != 0 ? Int32.Parse(model.CustomerRole) : (int?)null; + tierPrice.Quantity = model.Quantity; + tierPrice.Price = model.Price1; + + _productService.UpdateTierPrice(tierPrice); + } return TierPriceList(command, tierPrice.ProductId); } @@ -3075,22 +3149,18 @@ public ActionResult TierPriceUpdate(GridCommand command, ProductModel.TierPriceM [GridAction(EnableCustomBinding = true)] public ActionResult TierPriceDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var tierPrice = _productService.GetTierPriceById(id); - if (tierPrice == null) - throw new ArgumentException("No tier price found with the specified id"); - var productId = tierPrice.ProductId; - var product = _productService.GetProductById(productId); - if (product == null) - throw new ArgumentException("No product found with the specified id"); - _productService.DeleteTierPrice(tierPrice); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var product = _productService.GetProductById(productId); + + _productService.DeleteTierPrice(tierPrice); - //update "HasTierPrices" property - _productService.UpdateHasTierPricesProperty(product); + //update "HasTierPrices" property + _productService.UpdateHasTierPricesProperty(product); + } return TierPriceList(command, productId); } @@ -3102,40 +3172,45 @@ public ActionResult TierPriceDelete(int id, GridCommand command) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - var productVariantAttributes = _productAttributeService.GetProductVariantAttributesByProductId(productId); - var productVariantAttributesModel = productVariantAttributes - .Select(x => - { - var pvaModel = new ProductModel.ProductVariantAttributeModel() + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var productVariantAttributes = _productAttributeService.GetProductVariantAttributesByProductId(productId); + var productVariantAttributesModel = productVariantAttributes + .Select(x => { - Id = x.Id, - ProductId = x.ProductId, - ProductAttribute = _productAttributeService.GetProductAttributeById(x.ProductAttributeId).Name, - ProductAttributeId = x.ProductAttributeId, - TextPrompt = x.TextPrompt, - IsRequired = x.IsRequired, - AttributeControlType = x.AttributeControlType.GetLocalizedEnum(_localizationService, _workContext), - AttributeControlTypeId = x.AttributeControlTypeId, - DisplayOrder1 = x.DisplayOrder - }; + var pvaModel = new ProductModel.ProductVariantAttributeModel + { + Id = x.Id, + ProductId = x.ProductId, + ProductAttribute = _productAttributeService.GetProductAttributeById(x.ProductAttributeId).Name, + ProductAttributeId = x.ProductAttributeId, + TextPrompt = x.TextPrompt, + IsRequired = x.IsRequired, + AttributeControlType = x.AttributeControlType.GetLocalizedEnum(_localizationService, _workContext), + AttributeControlTypeId = x.AttributeControlTypeId, + DisplayOrder1 = x.DisplayOrder + }; - if (x.ShouldHaveValues()) - { - pvaModel.ViewEditUrl = Url.Action("EditAttributeValues", "Product", new { productVariantAttributeId = x.Id }); - pvaModel.ViewEditText = string.Format(_localizationService.GetResource("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.ViewLink"), x.ProductVariantAttributeValues != null ? x.ProductVariantAttributeValues.Count : 0); - } - return pvaModel; - }) - .ToList(); + if (x.ShouldHaveValues()) + { + pvaModel.ViewEditUrl = Url.Action("EditAttributeValues", "Product", new { productVariantAttributeId = x.Id }); + pvaModel.ViewEditText = string.Format(_localizationService.GetResource("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.ViewLink"), x.ProductVariantAttributeValues != null ? x.ProductVariantAttributeValues.Count : 0); + } + return pvaModel; + }) + .ToList(); - var model = new GridModel + model.Data = productVariantAttributesModel; + model.Total = productVariantAttributesModel.Count; + } + else { - Data = productVariantAttributesModel, - Total = productVariantAttributesModel.Count - }; + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -3146,19 +3221,20 @@ public ActionResult ProductVariantAttributeList(GridCommand command, int product [GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeInsert(GridCommand command, ProductModel.ProductVariantAttributeModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var pva = new ProductVariantAttribute() + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) { - ProductId = model.ProductId, - ProductAttributeId = Int32.Parse(model.ProductAttribute), //use ProductAttribute property (not ProductAttributeId) because appropriate property is stored in it - TextPrompt = model.TextPrompt, - IsRequired = model.IsRequired, - AttributeControlTypeId = Int32.Parse(model.AttributeControlType), //use AttributeControlType property (not AttributeControlTypeId) because appropriate property is stored in it - DisplayOrder = model.DisplayOrder1 - }; - _productAttributeService.InsertProductVariantAttribute(pva); + var pva = new ProductVariantAttribute + { + ProductId = model.ProductId, + ProductAttributeId = Int32.Parse(model.ProductAttribute), //use ProductAttribute property (not ProductAttributeId) because appropriate property is stored in it + TextPrompt = model.TextPrompt, + IsRequired = model.IsRequired, + AttributeControlTypeId = Int32.Parse(model.AttributeControlType), //use AttributeControlType property (not AttributeControlTypeId) because appropriate property is stored in it + DisplayOrder = model.DisplayOrder1 + }; + + _productAttributeService.InsertProductVariantAttribute(pva); + } return ProductVariantAttributeList(command, model.ProductId); } @@ -3166,21 +3242,20 @@ public ActionResult ProductVariantAttributeInsert(GridCommand command, ProductMo [GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeUpdate(GridCommand command, ProductModel.ProductVariantAttributeModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var pva = _productAttributeService.GetProductVariantAttributeById(model.Id); - if (pva == null) - throw new ArgumentException("No product variant attribute found with the specified id"); - //use ProductAttribute property (not ProductAttributeId) because appropriate property is stored in it - pva.ProductAttributeId = Int32.Parse(model.ProductAttribute); - pva.TextPrompt = model.TextPrompt; - pva.IsRequired = model.IsRequired; - //use AttributeControlType property (not AttributeControlTypeId) because appropriate property is stored in it - pva.AttributeControlTypeId = Int32.Parse(model.AttributeControlType); - pva.DisplayOrder = model.DisplayOrder1; - _productAttributeService.UpdateProductVariantAttribute(pva); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + //use ProductAttribute property (not ProductAttributeId) because appropriate property is stored in it + pva.ProductAttributeId = Int32.Parse(model.ProductAttribute); + pva.TextPrompt = model.TextPrompt; + pva.IsRequired = model.IsRequired; + //use AttributeControlType property (not AttributeControlTypeId) because appropriate property is stored in it + pva.AttributeControlTypeId = Int32.Parse(model.AttributeControlType); + pva.DisplayOrder = model.DisplayOrder1; + + _productAttributeService.UpdateProductVariantAttribute(pva); + } return ProductVariantAttributeList(command, pva.ProductId); } @@ -3188,16 +3263,13 @@ public ActionResult ProductVariantAttributeUpdate(GridCommand command, ProductMo [GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var pva = _productAttributeService.GetProductVariantAttributeById(id); - if (pva == null) - throw new ArgumentException("No product variant attribute found with the specified id"); - var productId = pva.ProductId; - _productAttributeService.DeleteProductVariantAttribute(pva); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productAttributeService.DeleteProductVariantAttribute(pva); + } return ProductVariantAttributeList(command, productId); } @@ -3254,22 +3326,19 @@ public ActionResult EditAttributeValues(int productVariantAttributeId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductAttributeValueList(int productVariantAttributeId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var pva = _productAttributeService.GetProductVariantAttributeById(productVariantAttributeId); - if (pva == null) - throw new ArgumentException("No product variant attribute found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var pva = _productAttributeService.GetProductVariantAttributeById(productVariantAttributeId); - var values = _productAttributeService.GetProductVariantAttributeValues(productVariantAttributeId); + var values = _productAttributeService.GetProductVariantAttributeValues(productVariantAttributeId); - var gridModel = new GridModel - { - Data = values.Select(x => + gridModel.Data = values.Select(x => { var linkedProduct = _productService.GetProductById(x.LinkedProductId); - var model = new ProductModel.ProductVariantAttributeValueModel() + var model = new ProductModel.ProductVariantAttributeValueModel { Id = x.Id, ProductVariantAttributeId = x.ProductVariantAttributeId, @@ -3300,9 +3369,17 @@ public ActionResult ProductAttributeValueList(int productVariantAttributeId, Gri } return model; - }), - Total = values.Count() - }; + }); + + gridModel.Total = values.Count(); + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel @@ -3493,14 +3570,12 @@ public ActionResult ProductAttributeValueEditPopup(string btnId, string formId, [GridAction(EnableCustomBinding = true)] public ActionResult ProductAttributeValueDelete(int pvavId, int productVariantAttributeId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var pvav = _productAttributeService.GetProductVariantAttributeValueById(pvavId); - if (pvav == null) - throw new ArgumentException("No product variant attribute value found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var pvav = _productAttributeService.GetProductVariantAttributeValueById(pvavId); - _productAttributeService.DeleteProductVariantAttributeValue(pvav); + _productAttributeService.DeleteProductVariantAttributeValue(pvav); + } return ProductAttributeValueList(productVariantAttributeId, command); } @@ -3543,33 +3618,41 @@ public ActionResult ProductAttributeValueLinkagePopup() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductAttributeValueLinkagePopupList(GridCommand command, ProductModel.ProductVariantAttributeValueModel.AddProductLinkageModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var searchContext = new ProductSearchContext() - { - CategoryIds = new List() { model.SearchCategoryId }, - ManufacturerId = model.SearchManufacturerId, - StoreId = model.SearchStoreId, - Keywords = model.SearchProductName, - SearchSku = !_catalogSettings.SuppressSkuSearch, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - ShowHidden = true, - ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var searchContext = new ProductSearchContext + { + CategoryIds = new List { model.SearchCategoryId }, + ManufacturerId = model.SearchManufacturerId, + StoreId = model.SearchStoreId, + Keywords = model.SearchProductName, + SearchSku = !_catalogSettings.SuppressSkuSearch, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + ShowHidden = true, + ProductType = model.SearchProductTypeId > 0 ? (ProductType?)model.SearchProductTypeId : null + }; + + var products = _productService.SearchProducts(searchContext); + + gridModel.Data = products.Select(x => + { + var productModel = x.ToModel(); + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); - var products = _productService.SearchProducts(searchContext); + return productModel; + }); - var gridModel = new GridModel(); - gridModel.Data = products.Select(x => + gridModel.Total = products.TotalCount; + } + else { - var productModel = x.ToModel(); - productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); + gridModel.Data = Enumerable.Empty(); - return productModel; - }); - gridModel.Total = products.TotalCount; + NotifyAccessDenied(); + } return new JsonResult { @@ -3704,65 +3787,69 @@ private void PrepareDeliveryTimes(ProductVariantAttributeCombinationModel model, [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeCombinationList(GridCommand command, int productId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var model = new GridModel(); - // TODO: Replace ProductModel.ProductVariantAttributeCombinationModel by AddProductVariantAttributeCombinationModel - // when there's no grid-inline-editing anymore. + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + // TODO: Replace ProductModel.ProductVariantAttributeCombinationModel by AddProductVariantAttributeCombinationModel + // when there's no grid-inline-editing anymore. - var product = _productService.GetProductById(productId); - if (product == null) - throw new ArgumentException("No product found with the specified id"); + var product = _productService.GetProductById(productId); - var allCombinations = _productAttributeService.GetAllProductVariantAttributeCombinations(product.Id, command.Page - 1, command.PageSize); + var allCombinations = _productAttributeService.GetAllProductVariantAttributeCombinations(product.Id, command.Page - 1, command.PageSize); - var productUrlTitle = _localizationService.GetResource("Common.OpenInShop"); - var productSeName = product.GetSeName(); + var productUrlTitle = _localizationService.GetResource("Common.OpenInShop"); + var productSeName = product.GetSeName(); - var productVariantAttributesModel = allCombinations.Select(x => - { - var pvacModel = x.ToModel(); - PrepareProductAttributeCombinationModel(pvacModel, x, product, true); + var productVariantAttributesModel = allCombinations.Select(x => + { + var pvacModel = x.ToModel(); + PrepareProductAttributeCombinationModel(pvacModel, x, product, true); - pvacModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(x.AttributesXml, product.Id, productSeName); - pvacModel.ProductUrlTitle = productUrlTitle; + pvacModel.ProductUrl = _productAttributeParser.GetProductUrlWithAttributes(x.AttributesXml, product.Id, productSeName); + pvacModel.ProductUrlTitle = productUrlTitle; - try - { - var firstAttribute = _productAttributeParser.DeserializeProductVariantAttributes(x.AttributesXml).FirstOrDefault(); - - var attribute = x.Product.ProductVariantAttributes.FirstOrDefault(y => y.Id == firstAttribute.Key); - var attributeValue = attribute.ProductVariantAttributeValues.FirstOrDefault(y => y.Id == int.Parse(firstAttribute.Value.First())); + try + { + var firstAttribute = _productAttributeParser.DeserializeProductVariantAttributes(x.AttributesXml).FirstOrDefault(); - pvacModel.DisplayOrder = attributeValue.DisplayOrder; - } - catch (Exception exc) - { - exc.Dump(); - } + var attribute = x.Product.ProductVariantAttributes.FirstOrDefault(y => y.Id == firstAttribute.Key); + var attributeValue = attribute.ProductVariantAttributeValues.FirstOrDefault(y => y.Id == int.Parse(firstAttribute.Value.First())); - //if (x.IsDefaultCombination) - // pvacModel.AttributesXml = "{0}".FormatWith(pvacModel.AttributesXml); + pvacModel.DisplayOrder = attributeValue.DisplayOrder; + } + catch (Exception exc) + { + exc.Dump(); + } - //warnings - var warnings = _shoppingCartService.GetShoppingCartItemAttributeWarnings( - _workContext.CurrentCustomer, - ShoppingCartType.ShoppingCart, - x.Product, - x.AttributesXml, - combination: x); - - pvacModel.Warnings.AddRange(warnings); + //if (x.IsDefaultCombination) + // pvacModel.AttributesXml = "{0}".FormatWith(pvacModel.AttributesXml); + + //warnings + var warnings = _shoppingCartService.GetShoppingCartItemAttributeWarnings( + _workContext.CurrentCustomer, + ShoppingCartType.ShoppingCart, + x.Product, + x.AttributesXml, + combination: x); - return pvacModel; - }) - .OrderBy(x => x.DisplayOrder).ToList(); + pvacModel.Warnings.AddRange(warnings); - var model = new GridModel + return pvacModel; + }) + .OrderBy(x => x.DisplayOrder) + .ToList(); + + model.Data = productVariantAttributesModel; + model.Total = allCombinations.TotalCount; + } + else { - Data = productVariantAttributesModel, - Total = allCombinations.TotalCount, - }; + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -3773,19 +3860,16 @@ public ActionResult ProductVariantAttributeCombinationList(GridCommand command, [GridAction(EnableCustomBinding = true)] public ActionResult ProductVariantAttributeCombinationDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - var pvac = _productAttributeService.GetProductVariantAttributeCombinationById(id); - if (pvac == null) - throw new ArgumentException("No product variant attribute combination found with the specified id"); - var productId = pvac.ProductId; - _productAttributeService.DeleteProductVariantAttributeCombination(pvac); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + _productAttributeService.DeleteProductVariantAttributeCombination(pvac); - var product = _productService.GetProductById(productId); - _productService.UpdateLowestAttributeCombinationPriceProperty(product); + var product = _productService.GetProductById(productId); + _productService.UpdateLowestAttributeCombinationPriceProperty(product); + } return ProductVariantAttributeCombinationList(command, productId); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs index 9858dc1745..dff5a6de80 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductReviewController.cs @@ -109,34 +109,40 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, ProductReviewListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); - DateTime? createdOnFromValue = (model.CreatedOnFrom == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnFrom.Value, _dateTimeHelper.CurrentTimeZone); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + DateTime? createdOnFromValue = (model.CreatedOnFrom == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnFrom.Value, _dateTimeHelper.CurrentTimeZone); - DateTime? createdToFromValue = (model.CreatedOnTo == null) ? null - : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + DateTime? createdToFromValue = (model.CreatedOnTo == null) ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.CreatedOnTo.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); + + var productReviews = _customerContentService.GetAllCustomerContent(0, null, createdOnFromValue, createdToFromValue); + + gridModel.Data = productReviews.PagedForCommand(command).Select(x => + { + var m = new ProductReviewModel(); + PrepareProductReviewModel(m, x, false, true); + return m; + }); + + gridModel.Total = productReviews.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var productReviews = _customerContentService.GetAllCustomerContent(0, null, - createdOnFromValue, createdToFromValue); - var gridModel = new GridModel - { - Data = productReviews.PagedForCommand(command).Select(x => - { - var m = new ProductReviewModel(); - PrepareProductReviewModel(m, x, false, true); - return m; - }), - Total = productReviews.Count, - }; return new JsonResult { Data = gridModel }; } - //edit public ActionResult Edit(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) @@ -186,7 +192,6 @@ public ActionResult Edit(ProductReviewModel model, bool continueEditing) return View(model); } - //delete [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/QueuedEmailController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/QueuedEmailController.cs index 58057c2d80..6bc007e811 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/QueuedEmailController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/QueuedEmailController.cs @@ -53,41 +53,47 @@ public ActionResult List() [GridAction(EnableCustomBinding = true)] public ActionResult QueuedEmailList(GridCommand command, QueuedEmailListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageMessageQueue)) - return AccessDeniedView(); + var gridModel = new GridModel(); - DateTime? startDateValue = (model.SearchStartDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.SearchStartDate.Value, _dateTimeHelper.CurrentTimeZone); - DateTime? endDateValue = (model.SearchEndDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.SearchEndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); - - var q = new SearchEmailsQuery + if (_permissionService.Authorize(StandardPermissionProvider.ManageMessageQueue)) { - EndTime = endDateValue, - From = model.SearchFromEmail, - MaxSendTries = model.SearchMaxSentTries, - OrderByLatest = true, - PageIndex = command.Page - 1, - PageSize = command.PageSize, - SendManually = model.SearchSendManually, - StartTime = startDateValue, - To = model.SearchToEmail, - UnsentOnly = model.SearchLoadNotSent - }; - var queuedEmails = _queuedEmailService.SearchEmails(q); + DateTime? startDateValue = (model.SearchStartDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.SearchStartDate.Value, _dateTimeHelper.CurrentTimeZone); + DateTime? endDateValue = (model.SearchEndDate == null) ? null : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.SearchEndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1); - var gridModel = new GridModel - { - Data = queuedEmails.Select(x => + var q = new SearchEmailsQuery + { + EndTime = endDateValue, + From = model.SearchFromEmail, + MaxSendTries = model.SearchMaxSentTries, + OrderByLatest = true, + PageIndex = command.Page - 1, + PageSize = command.PageSize, + SendManually = model.SearchSendManually, + StartTime = startDateValue, + To = model.SearchToEmail, + UnsentOnly = model.SearchLoadNotSent + }; + var queuedEmails = _queuedEmailService.SearchEmails(q); + + gridModel.Data = queuedEmails.Select(x => { - var m = x.ToModel(); - m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); + var m = x.ToModel(); + m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc); - if (x.SentOnUtc.HasValue) - m.SentOn = _dateTimeHelper.ConvertToUserTime(x.SentOnUtc.Value, DateTimeKind.Utc); + if (x.SentOnUtc.HasValue) + m.SentOn = _dateTimeHelper.ConvertToUserTime(x.SentOnUtc.Value, DateTimeKind.Utc); - return m; - }), - Total = queuedEmails.TotalCount - }; + return m; + }); + + gridModel.Total = queuedEmails.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/RecurringPaymentController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/RecurringPaymentController.cs index 39d549fc08..0f4b15968e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/RecurringPaymentController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/RecurringPaymentController.cs @@ -113,7 +113,6 @@ private void PrepareRecurringPaymentHistoryModel(RecurringPaymentModel.Recurring #region Recurring payment - //list public ActionResult Index() { return RedirectToAction("List"); @@ -131,27 +130,34 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var payments = _orderService.SearchRecurringPayments(0, 0, 0, null, true); + + gridModel.Data = payments.PagedForCommand(command).Select(x => + { + var m = new RecurringPaymentModel(); + PrepareRecurringPaymentModel(m, x, false); + return m; + }); + + gridModel.Total = payments.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var payments = _orderService.SearchRecurringPayments(0, 0, 0, null, true); - var gridModel = new GridModel - { - Data = payments.PagedForCommand(command).Select(x => - { - var m = new RecurringPaymentModel(); - PrepareRecurringPaymentModel(m, x, false); - return m; - }), - Total = payments.Count, - }; return new JsonResult { Data = gridModel }; } - //edit public ActionResult Edit(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) @@ -189,7 +195,6 @@ public ActionResult Edit(RecurringPaymentModel model, bool continueEditing) return continueEditing ? RedirectToAction("Edit", payment.Id) : RedirectToAction("List"); } - //delete [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { @@ -214,26 +219,30 @@ public ActionResult DeleteConfirmed(int id) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult HistoryList(int recurringPaymentId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); - - var payment = _orderService.GetRecurringPaymentById(recurringPaymentId); - if (payment == null) - throw new ArgumentException("No recurring payment found with the specified id"); - - var historyModel = payment.RecurringPaymentHistory.OrderBy(x => x.CreatedOnUtc) - .Select(x => - { - var m = new RecurringPaymentModel.RecurringPaymentHistoryModel(); - PrepareRecurringPaymentHistoryModel(m, x); - return m; - }) - .ToList(); - var model = new GridModel - { - Data = historyModel, - Total = historyModel.Count - }; + var model = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var payment = _orderService.GetRecurringPaymentById(recurringPaymentId); + + var historyModel = payment.RecurringPaymentHistory.OrderBy(x => x.CreatedOnUtc) + .Select(x => + { + var m = new RecurringPaymentModel.RecurringPaymentHistoryModel(); + PrepareRecurringPaymentHistoryModel(m, x); + return m; + }) + .ToList(); + + model.Data = historyModel; + model.Total = historyModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ReturnRequestController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ReturnRequestController.cs index 1180d71fc7..f210fef4fd 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ReturnRequestController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ReturnRequestController.cs @@ -171,7 +171,6 @@ private bool PrepareReturnRequestModel(ReturnRequestModel model, ReturnRequest r #region Methods - //list public ActionResult Index() { return RedirectToAction("List"); @@ -191,26 +190,31 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, ReturnRequestListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var data = new List(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests)) + { + var data = new List(); - var returnRequests = _orderService.SearchReturnRequests(model.SearchStoreId, 0, 0, model.SearchReturnRequestStatus, - command.Page - 1, command.PageSize, model.SearchId ?? 0); + var returnRequests = _orderService.SearchReturnRequests(model.SearchStoreId, 0, 0, model.SearchReturnRequestStatus, + command.Page - 1, command.PageSize, model.SearchId ?? 0); - foreach (var rr in returnRequests) - { - var m = new ReturnRequestModel(); - if (PrepareReturnRequestModel(m, rr, false)) - data.Add(m); - } + foreach (var rr in returnRequests) + { + var m = new ReturnRequestModel(); + if (PrepareReturnRequestModel(m, rr, false)) + data.Add(m); + } - var gridModel = new GridModel + gridModel.Data = data; + gridModel.Total = returnRequests.TotalCount; + } + else { - Data = data, - Total = returnRequests.TotalCount, - }; + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -218,7 +222,6 @@ public ActionResult List(GridCommand command, ReturnRequestListModel model) }; } - //edit public ActionResult Edit(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests)) @@ -300,7 +303,6 @@ public ActionResult NotifyCustomer(ReturnRequestModel model) return RedirectToAction("Edit", returnRequest.Id); } - //delete [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs index 086be63a26..85ea07067b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs @@ -1532,44 +1532,49 @@ public ActionResult AllSettings() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult AllSettings(GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) - return AccessDeniedView(); + var model = new GridModel(); - var stores = _services.StoreService.GetAllStores(); - string allStoresString = _services.Localization.GetResource("Admin.Common.StoresAll"); - - var settings = _services.Settings - .GetAllSettings() - .Select(x => - { - var settingModel = new SettingModel() - { - Id = x.Id, - Name = x.Name, - Value = x.Value, - StoreId = x.StoreId - }; + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) + { + var stores = _services.StoreService.GetAllStores(); + string allStoresString = _services.Localization.GetResource("Admin.Common.StoresAll"); - if (x.StoreId == 0) - { - settingModel.Store = allStoresString; - } - else + var settings = _services.Settings + .GetAllSettings() + .Select(x => { - var store = stores.FirstOrDefault(s => s.Id == x.StoreId); - settingModel.Store = store != null ? store.Name : "Unknown"; - } - - return settingModel; - }) - .ForCommand(command) - .ToList(); - - var model = new GridModel - { - Data = settings.PagedForCommand(command), - Total = settings.Count - }; + var settingModel = new SettingModel() + { + Id = x.Id, + Name = x.Name, + Value = x.Value, + StoreId = x.StoreId + }; + + if (x.StoreId == 0) + { + settingModel.Store = allStoresString; + } + else + { + var store = stores.FirstOrDefault(s => s.Id == x.StoreId); + settingModel.Store = store != null ? store.Name : "".NaIfEmpty(); + } + + return settingModel; + }) + .ForCommand(command) + .ToList(); + + model.Data = settings.PagedForCommand(command); + model.Total = settings.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -1580,80 +1585,76 @@ public ActionResult AllSettings(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult SettingUpdate(SettingModel model, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) - return AccessDeniedView(); - - if (model.Name != null) - model.Name = model.Name.Trim(); - if (model.Value != null) - model.Value = model.Value.Trim(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) + { + if (model.Name != null) + model.Name = model.Name.Trim(); + if (model.Value != null) + model.Value = model.Value.Trim(); - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - var setting = _services.Settings.GetSettingById(model.Id); - if (setting == null) - return Content(_services.Localization.GetResource("Admin.Configuration.Settings.NoneWithThatId")); + var setting = _services.Settings.GetSettingById(model.Id); + if (setting == null) + return Content(T("Admin.Configuration.Settings.NoneWithThatId")); - var storeId = model.Store.ToInt(); //use Store property (not StoreId) because appropriate property is stored in it + var storeId = model.Store.ToInt(); //use Store property (not StoreId) because appropriate property is stored in it - if (!setting.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase) || - setting.StoreId != storeId) - { - //setting name or store has been changed - _services.Settings.DeleteSetting(setting); - } + if (!setting.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase) || setting.StoreId != storeId) + { + //setting name or store has been changed + _services.Settings.DeleteSetting(setting); + } - _services.Settings.SetSetting(model.Name, model.Value, storeId); + _services.Settings.SetSetting(model.Name, model.Value, storeId); - //activity log - _customerActivityService.InsertActivity("EditSettings", _services.Localization.GetResource("ActivityLog.EditSettings")); + //activity log + _customerActivityService.InsertActivity("EditSettings", T("ActivityLog.EditSettings")); + } return AllSettings(command); } [GridAction(EnableCustomBinding = true)] public ActionResult SettingAdd([Bind(Exclude = "Id")] SettingModel model, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) - return AccessDeniedView(); - - if (model.Name != null) - model.Name = model.Name.Trim(); - if (model.Value != null) - model.Value = model.Value.Trim(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) + { + if (model.Name != null) + model.Name = model.Name.Trim(); + if (model.Value != null) + model.Value = model.Value.Trim(); - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - var storeId = model.Store.ToInt(); //use Store property (not StoreId) because appropriate property is stored in it - _services.Settings.SetSetting(model.Name, model.Value, storeId); + var storeId = model.Store.ToInt(); //use Store property (not StoreId) because appropriate property is stored in it + _services.Settings.SetSetting(model.Name, model.Value, storeId); - //activity log - _customerActivityService.InsertActivity("AddNewSetting", _services.Localization.GetResource("ActivityLog.AddNewSetting"), model.Name); + //activity log + _customerActivityService.InsertActivity("AddNewSetting", T("ActivityLog.AddNewSetting", model.Name)); + } return AllSettings(command); } [GridAction(EnableCustomBinding = true)] public ActionResult SettingDelete(int id, GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) - return AccessDeniedView(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageSettings)) + { + var setting = _services.Settings.GetSettingById(id); - var setting = _services.Settings.GetSettingById(id); - if (setting == null) - throw new ArgumentException("No setting found with the specified id"); - _services.Settings.DeleteSetting(setting); + _services.Settings.DeleteSetting(setting); - //activity log - _customerActivityService.InsertActivity("DeleteSetting", _services.Localization.GetResource("ActivityLog.DeleteSetting"), setting.Name); + //activity log + _customerActivityService.InsertActivity("DeleteSetting", T("ActivityLog.DeleteSetting", setting.Name)); + } return AllSettings(command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs index a63f02506f..887b5dabbc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ShippingController.cs @@ -149,19 +149,24 @@ public ActionResult Methods() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Methods(GridCommand command) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageShippingSettings)) - return AccessDeniedView(); + var model = new GridModel(); - var shippingMethodsModel = _shippingService.GetAllShippingMethods() - .Select(x => x.ToModel()) - .ForCommand(command) - .ToList(); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageShippingSettings)) + { + var shippingMethodsModel = _shippingService.GetAllShippingMethods() + .Select(x => x.ToModel()) + .ForCommand(command) + .ToList(); - var model = new GridModel - { - Data = shippingMethodsModel, - Total = shippingMethodsModel.Count - }; + model.Data = shippingMethodsModel; + model.Total = shippingMethodsModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ShoppingCartController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ShoppingCartController.cs index b6868c18ce..fccebd040d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ShoppingCartController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ShoppingCartController.cs @@ -65,28 +65,32 @@ public ActionResult CurrentCarts() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CurrentCarts(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); - - var customers = _customerService.GetAllCustomers(null, null, null, null, null, - null, null, 0, 0, null, null, null, true, ShoppingCartType.ShoppingCart, - command.Page - 1, command.PageSize); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var customers = _customerService.GetAllCustomers(null, null, null, null, null, null, null, + 0, 0, null, null, null, true, ShoppingCartType.ShoppingCart, command.Page - 1, command.PageSize); + + gridModel.Data = customers.Select(x => + { + return new ShoppingCartModel + { + CustomerId = x.Id, + CustomerEmail = x.IsGuest() ? T("Admin.Customers.Guest").Text : x.Email, + TotalItems = x.CountProductsInCart(ShoppingCartType.ShoppingCart) + }; + }); + + gridModel.Total = customers.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var gridModel = new GridModel - { - Data = customers.Select(x => - { - return new ShoppingCartModel() - { - CustomerId = x.Id, - CustomerEmail = x.IsGuest() ? - _localizationService.GetResource("Admin.Customers.Guest") : - x.Email, - TotalItems = x.CountProductsInCart(ShoppingCartType.ShoppingCart) - }; - }), - Total = customers.TotalCount - }; return new JsonResult { Data = gridModel @@ -96,35 +100,43 @@ public ActionResult CurrentCarts(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult GetCartDetails(int customerId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var customer = _customerService.GetCustomerById(customerId); - var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var customer = _customerService.GetCustomerById(customerId); + var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart); - var gridModel = new GridModel() - { - Data = cart.Select(sci => - { - decimal taxRate; + gridModel.Data = cart.Select(sci => + { + decimal taxRate; var store = _storeService.GetStoreById(sci.Item.StoreId); - var sciModel = new ShoppingCartItemModel() - { - Id = sci.Item.Id, - Store = store != null ? store.Name : "Unknown", + + var sciModel = new ShoppingCartItemModel + { + Id = sci.Item.Id, + Store = store != null ? store.Name : "".NaIfEmpty(), ProductId = sci.Item.ProductId, - Quantity = sci.Item.Quantity, + Quantity = sci.Item.Quantity, ProductName = sci.Item.Product.Name, ProductTypeName = sci.Item.Product.GetProductTypeLabel(_localizationService), ProductTypeLabelHint = sci.Item.Product.ProductTypeLabelHint, - UnitPrice = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate)), - Total = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetSubTotal(sci, true), out taxRate)), - UpdatedOn = _dateTimeHelper.ConvertToUserTime(sci.Item.UpdatedOnUtc, DateTimeKind.Utc) - }; - return sciModel; - }), - Total = cart.Count - }; + UnitPrice = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate)), + Total = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetSubTotal(sci, true), out taxRate)), + UpdatedOn = _dateTimeHelper.ConvertToUserTime(sci.Item.UpdatedOnUtc, DateTimeKind.Utc) + }; + return sciModel; + }); + + gridModel.Total = cart.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel @@ -144,28 +156,32 @@ public ActionResult CurrentWishlists() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CurrentWishlists(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); - - var customers = _customerService.GetAllCustomers(null, null, null, null, null, - null, null, 0, 0, null, null, null, - true, ShoppingCartType.Wishlist, command.Page - 1, command.PageSize); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var customers = _customerService.GetAllCustomers(null, null, null, null, null, null, null, + 0, 0, null, null, null, true, ShoppingCartType.Wishlist, command.Page - 1, command.PageSize); + + gridModel.Data = customers.Select(x => + { + return new ShoppingCartModel + { + CustomerId = x.Id, + CustomerEmail = x.IsGuest() ? T("Admin.Customers.Guest").Text : x.Email, + TotalItems = x.CountProductsInCart(ShoppingCartType.Wishlist) + }; + }); + + gridModel.Total = customers.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var gridModel = new GridModel - { - Data = customers.Select(x => - { - return new ShoppingCartModel() - { - CustomerId = x.Id, - CustomerEmail = x.IsGuest() ? - _localizationService.GetResource("Admin.Customers.Guest") : - x.Email, - TotalItems = x.CountProductsInCart(ShoppingCartType.Wishlist) - }; - }), - Total = customers.TotalCount - }; return new JsonResult { Data = gridModel @@ -175,35 +191,43 @@ public ActionResult CurrentWishlists(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult GetWishlistDetails(int customerId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var customer = _customerService.GetCustomerById(customerId); - var cart = customer.GetCartItems(ShoppingCartType.Wishlist); + if (_permissionService.Authorize(StandardPermissionProvider.ManageOrders)) + { + var customer = _customerService.GetCustomerById(customerId); + var cart = customer.GetCartItems(ShoppingCartType.Wishlist); - var gridModel = new GridModel() - { - Data = cart.Select(sci => - { - decimal taxRate; - var store = _storeService.GetStoreById(sci.Item.StoreId); - var sciModel = new ShoppingCartItemModel() - { - Id = sci.Item.Id, - Store = store != null ? store.Name : "Unknown", - ProductId = sci.Item.ProductId, - Quantity = sci.Item.Quantity, + gridModel.Data = cart.Select(sci => + { + decimal taxRate; + var store = _storeService.GetStoreById(sci.Item.StoreId); + + var sciModel = new ShoppingCartItemModel + { + Id = sci.Item.Id, + Store = store != null ? store.Name : "".NaIfEmpty(), + ProductId = sci.Item.ProductId, + Quantity = sci.Item.Quantity, ProductName = sci.Item.Product.Name, ProductTypeName = sci.Item.Product.GetProductTypeLabel(_localizationService), ProductTypeLabelHint = sci.Item.Product.ProductTypeLabelHint, - UnitPrice = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate)), - Total = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetSubTotal(sci, true), out taxRate)), - UpdatedOn = _dateTimeHelper.ConvertToUserTime(sci.Item.UpdatedOnUtc, DateTimeKind.Utc) - }; - return sciModel; - }), - Total = cart.Count - }; + UnitPrice = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetUnitPrice(sci, true), out taxRate)), + Total = _priceFormatter.FormatPrice(_taxService.GetProductPrice(sci.Item.Product, _priceCalculationService.GetSubTotal(sci, true), out taxRate)), + UpdatedOn = _dateTimeHelper.ConvertToUserTime(sci.Item.UpdatedOnUtc, DateTimeKind.Utc) + }; + return sciModel; + }); + + gridModel.Total = cart.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { Data = gridModel diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SpecificationAttributeController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SpecificationAttributeController.cs index 92877bbb9b..a15ca73afc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SpecificationAttributeController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SpecificationAttributeController.cs @@ -121,28 +121,31 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var optionsText = _localizationService.GetResource("Admin.Catalog.Attributes.SpecificationAttributes.Options"); - - var data = _specificationAttributeService.GetSpecificationAttributes() - .Expand(x => x.SpecificationAttributeOptions) - .ForCommand(command) - .Select(x => - { - var model = x.ToModel(); - model.OptionCount = x.SpecificationAttributeOptions.Count; - - return model; - }) - .ToList(); + var gridModel = new GridModel(); - var gridModel = new GridModel + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var data = _specificationAttributeService.GetSpecificationAttributes() + .Expand(x => x.SpecificationAttributeOptions) + .ForCommand(command) + .Select(x => + { + var model = x.ToModel(); + model.OptionCount = x.SpecificationAttributeOptions.Count; + + return model; + }) + .ToList(); + + gridModel.Data = data.PagedForCommand(command); + gridModel.Total = data.Count; + } + else { - Data = data.PagedForCommand(command), - Total = data.Count - }; + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -150,7 +153,6 @@ public ActionResult List(GridCommand command) }; } - //create public ActionResult Create() { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) @@ -185,7 +187,6 @@ public ActionResult Create(SpecificationAttributeModel model, bool continueEditi return View(model); } - //edit public ActionResult Edit(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) @@ -233,7 +234,6 @@ public ActionResult Edit(SpecificationAttributeModel model, bool continueEditing return View(model); } - //delete [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { @@ -289,35 +289,31 @@ public ActionResult ProductMappingEdit(int specificationAttributeId, string fiel #region Specification attribute options - //list [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult OptionList(int specificationAttributeId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); + var gridModel = new GridModel(); + + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var options = _specificationAttributeService.GetSpecificationAttributeOptionsBySpecificationAttribute(specificationAttributeId); + + gridModel.Data = options.Select(x => x.ToModel()); + gridModel.Total = options.Count(); + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } - var options = _specificationAttributeService.GetSpecificationAttributeOptionsBySpecificationAttribute(specificationAttributeId); - var gridModel = new GridModel - { - Data = options.Select(x => - { - var model = x.ToModel(); - //locales - //AddLocales(_languageService, model.Locales, (locale, languageId) => - //{ - // locale.Name = x.GetLocalized(y => y.Name, languageId, false, false); - //}); - return model; - }), - Total = options.Count() - }; return new JsonResult { Data = gridModel }; } - //create public ActionResult OptionCreatePopup(int specificationAttributeId) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) @@ -367,7 +363,6 @@ public ActionResult OptionCreatePopup(string btnId, string formId, Specification return View(model); } - //edit public ActionResult OptionEditPopup(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) @@ -414,23 +409,19 @@ public ActionResult OptionEditPopup(string btnId, string formId, SpecificationAt return View(model); } - //delete [GridAction(EnableCustomBinding = true)] public ActionResult OptionDelete(int optionId, int specificationAttributeId, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) - return AccessDeniedView(); - - var sao = _specificationAttributeService.GetSpecificationAttributeOptionById(optionId); - if (sao == null) - throw new ArgumentException("No specification attribute option found with the specified id"); + if (_permissionService.Authorize(StandardPermissionProvider.ManageCatalog)) + { + var sao = _specificationAttributeService.GetSpecificationAttributeOptionById(optionId); - _specificationAttributeService.DeleteSpecificationAttributeOption(sao); + _specificationAttributeService.DeleteSpecificationAttributeOption(sao); + } return OptionList(specificationAttributeId, command); } - //ajax [AcceptVerbs(HttpVerbs.Get)] public ActionResult GetOptionsByAttributeId(string attributeId) @@ -468,7 +459,6 @@ public ActionResult SetAttributeValue(string pk, string value, string name, Form } } - #endregion } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/StoreController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/StoreController.cs index 78a441460e..38df324a4c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/StoreController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/StoreController.cs @@ -81,27 +81,32 @@ from m in stores [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageStores)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var storeModels = _storeService.GetAllStores() - .Select(x => - { - var model = x.ToModel(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageStores)) + { + var storeModels = _storeService.GetAllStores() + .Select(x => + { + var model = x.ToModel(); - PrepareStoreModel(model, x); + PrepareStoreModel(model, x); - model.Hosts = model.Hosts.EmptyNull().Replace(",", "
"); + model.Hosts = model.Hosts.EmptyNull().Replace(",", "
"); - return model; - }) - .ToList(); + return model; + }) + .ToList(); - var gridModel = new GridModel + gridModel.Data = storeModels; + gridModel.Total = storeModels.Count(); + } + else { - Data = storeModels, - Total = storeModels.Count() - }; + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/TaxController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/TaxController.cs index b78404407d..a694f7df48 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/TaxController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/TaxController.cs @@ -116,18 +116,24 @@ public ActionResult Categories() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult Categories(GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) - return AccessDeniedView(); + var model = new GridModel(); - var categoriesModel = _taxCategoryService.GetAllTaxCategories() - .Select(x => x.ToModel()) - .ForCommand(command) - .ToList(); - var model = new GridModel - { - Data = categoriesModel, - Total = categoriesModel.Count - }; + if (_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) + { + var categoriesModel = _taxCategoryService.GetAllTaxCategories() + .Select(x => x.ToModel()) + .ForCommand(command) + .ToList(); + + model.Data = categoriesModel; + model.Total = categoriesModel.Count; + } + else + { + model.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { @@ -138,19 +144,19 @@ public ActionResult Categories(GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult CategoryUpdate(TaxCategoryModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var taxCategory = _taxCategoryService.GetTaxCategoryById(model.Id); + taxCategory = model.ToEntity(taxCategory); - var taxCategory = _taxCategoryService.GetTaxCategoryById(model.Id); - taxCategory = model.ToEntity(taxCategory); - _taxCategoryService.UpdateTaxCategory(taxCategory); + _taxCategoryService.UpdateTaxCategory(taxCategory); + } return Categories(command); } @@ -158,19 +164,19 @@ public ActionResult CategoryUpdate(TaxCategoryModel model, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult CategoryAdd([Bind(Exclude = "Id")] TaxCategoryModel model, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) + { + if (!ModelState.IsValid) + { + var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); + return Content(modelStateErrors.FirstOrDefault()); + } - if (!ModelState.IsValid) - { - //display the first model error - var modelStateErrors = this.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage); - return Content(modelStateErrors.FirstOrDefault()); - } + var taxCategory = new TaxCategory(); + taxCategory = model.ToEntity(taxCategory); - var taxCategory = new TaxCategory(); - taxCategory = model.ToEntity(taxCategory); - _taxCategoryService.InsertTaxCategory(taxCategory); + _taxCategoryService.InsertTaxCategory(taxCategory); + } return Categories(command); } @@ -178,13 +184,12 @@ public ActionResult CategoryAdd([Bind(Exclude = "Id")] TaxCategoryModel model, G [GridAction(EnableCustomBinding = true)] public ActionResult CategoryDelete(int id, GridCommand command) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) - return AccessDeniedView(); + if (_permissionService.Authorize(StandardPermissionProvider.ManageTaxSettings)) + { + var taxCategory = _taxCategoryService.GetTaxCategoryById(id); - var taxCategory = _taxCategoryService.GetTaxCategoryById(id); - if (taxCategory == null) - throw new ArgumentException("No tax category found with the specified id"); - _taxCategoryService.DeleteTaxCategory(taxCategory); + _taxCategoryService.DeleteTaxCategory(taxCategory); + } return Categories(command); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/TopicController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/TopicController.cs index 29f29762c6..03b4a60c62 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/TopicController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/TopicController.cs @@ -137,20 +137,29 @@ public ActionResult List() [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, TopicListModel model) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageTopics)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var topics = _topicService.GetAllTopics(model.SearchStoreId); - var gridModel = new GridModel - { - Data = topics.Select(x => { + if (_permissionService.Authorize(StandardPermissionProvider.ManageTopics)) + { + var topics = _topicService.GetAllTopics(model.SearchStoreId); + + gridModel.Data = topics.Select(x => + { var item = x.ToModel(); // otherwise maxJsonLength could be exceeded item.Body = ""; return item; - }), - Total = topics.Count - }; + }); + + gridModel.Total = topics.Count; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } + return new JsonResult { MaxJsonLength = int.MaxValue, diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs index c088c0805d..d51b32bb19 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/UrlRecordController.cs @@ -107,20 +107,19 @@ public ActionResult List(string entityName, int? entityId) [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult List(GridCommand command, UrlRecordListModel model) { - if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) - return AccessDeniedView(); + var gridModel = new GridModel(); - var allLanguages = _languageService.GetAllLanguages(true); - var defaultLanguageName = T("Admin.System.SeNames.Language.Standard"); + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageUrlRecords)) + { + var allLanguages = _languageService.GetAllLanguages(true); + var defaultLanguageName = T("Admin.System.SeNames.Language.Standard"); - var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, - model.SeName, model.EntityName, model.EntityId, model.LanguageId, model.IsActive); + var urlRecords = _urlRecordService.GetAllUrlRecords(command.Page - 1, command.PageSize, + model.SeName, model.EntityName, model.EntityId, model.LanguageId, model.IsActive); - var slugsPerEntity = _urlRecordService.CountSlugsPerEntity(urlRecords.Select(x => x.Id).Distinct().ToArray()); + var slugsPerEntity = _urlRecordService.CountSlugsPerEntity(urlRecords.Select(x => x.Id).Distinct().ToArray()); - var gridModel = new GridModel - { - Data = urlRecords.Select(x => + gridModel.Data = urlRecords.Select(x => { string languageName; @@ -141,9 +140,16 @@ public ActionResult List(GridCommand command, UrlRecordListModel model) urlRecordModel.SlugsPerEntity = (slugsPerEntity.ContainsKey(x.Id) ? slugsPerEntity[x.Id] : 0); return urlRecordModel; - }), - Total = urlRecords.TotalCount - }; + }); + + gridModel.Total = urlRecords.TotalCount; + } + else + { + gridModel.Data = Enumerable.Empty(); + + NotifyAccessDenied(); + } return new JsonResult { From 1fa83bdc2017f11dd20ee50efd901e4336aceabe Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Wed, 6 Jan 2016 12:57:00 +0100 Subject: [PATCH 241/732] Localized some hard-coded strings --- .../201512151526290_ImportFramework.cs | 9 +++++++ .../Controllers/ByTotalController.cs | 6 +++-- .../Controllers/FixedRateController.cs | 2 +- .../Providers/ByTotalProvider.cs | 27 ++++++++++--------- .../Providers/FixedRateProvider.cs | 25 +++++++++-------- .../ByWeightShippingComputationMethod.cs | 23 ++++++++++------ .../Controllers/ShippingByWeightController.cs | 6 +++-- .../Controllers/TaxByRegionController.cs | 4 +-- 8 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 7de4cc953b..35b6497ae1 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -76,6 +76,7 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Legt die Anzahl der zu berspringenden Datenstze fest."); builder.AddOrUpdate("Common.Unknown", "Unknown", "Unbekannt"); + builder.AddOrUpdate("Common.Unavailable", "Unavailable", "Nicht verfgbar"); builder.AddOrUpdate("Common.Language", "Language", "Sprache"); builder.AddOrUpdate("Admin.Common.ImportFile", "Import file", "Importdatei"); builder.AddOrUpdate("Admin.Common.ImportFiles", "Import files", "Importdateien"); @@ -303,6 +304,14 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Configuration.Countries.States.CantDeleteWithAddresses", "The state\\province cannot be deleted because it has associated addresses.", "Das Bundesland\\Region kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); + + builder.AddOrUpdate("Admin.Configuration.Shipping.Methods.NoMethodsLoaded", + "No shipping methods could be loaded.", + "Es konnten keine Versandarten geladen werden."); + + builder.AddOrUpdate("Admin.System.Warnings.NoShipmentItems", + "No shipment items", + "Keine Versand-Artikel"); } } } diff --git a/src/Plugins/SmartStore.Shipping/Controllers/ByTotalController.cs b/src/Plugins/SmartStore.Shipping/Controllers/ByTotalController.cs index 1bf1f643b1..5c5e03f8ab 100644 --- a/src/Plugins/SmartStore.Shipping/Controllers/ByTotalController.cs +++ b/src/Plugins/SmartStore.Shipping/Controllers/ByTotalController.cs @@ -43,7 +43,7 @@ public ActionResult Configure() var shippingMethods = _shippingService.GetAllShippingMethods(); if (shippingMethods.Count == 0) { - return Content("No shipping methods can be loaded"); + return Content(T("Admin.Configuration.Shipping.Methods.NoMethodsLoaded")); } var model = new ByTotalListModel(); @@ -100,10 +100,11 @@ public ActionResult RateUpdate(ByTotalModel model, GridCommand command) { if (!ModelState.IsValid) { - return new JsonResult { Data = "error" }; + return new JsonResult { Data = T("Admin.Common.UnknownError").Text }; } var shippingByTotalRecord = _shippingByTotalService.GetShippingByTotalRecordById(model.Id); + shippingByTotalRecord.Zip = model.Zip == "*" ? null : model.Zip; shippingByTotalRecord.From = model.From; shippingByTotalRecord.To = model.To; @@ -112,6 +113,7 @@ public ActionResult RateUpdate(ByTotalModel model, GridCommand command) shippingByTotalRecord.ShippingChargePercentage = model.ShippingChargePercentage; shippingByTotalRecord.BaseCharge = model.BaseCharge; shippingByTotalRecord.MaxCharge = model.MaxCharge; + _shippingByTotalService.UpdateShippingByTotalRecord(shippingByTotalRecord); return RatesList(command); diff --git a/src/Plugins/SmartStore.Shipping/Controllers/FixedRateController.cs b/src/Plugins/SmartStore.Shipping/Controllers/FixedRateController.cs index f0e510f3b7..353bafe59d 100644 --- a/src/Plugins/SmartStore.Shipping/Controllers/FixedRateController.cs +++ b/src/Plugins/SmartStore.Shipping/Controllers/FixedRateController.cs @@ -29,7 +29,7 @@ public ActionResult Configure() { var shippingMethods = _shippingService.GetAllShippingMethods(); if (shippingMethods.Count == 0) - return Content("No shipping methods can be loaded"); + return Content(T("Admin.Configuration.Shipping.Methods.NoMethodsLoaded")); var tmp = new List(); foreach (var shippingMethod in shippingMethods) diff --git a/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs b/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs index 882a576f15..82ec989cf4 100644 --- a/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs +++ b/src/Plugins/SmartStore.Shipping/Providers/ByTotalProvider.cs @@ -1,20 +1,20 @@ using System; -using System.Data.Entity.Migrations; using System.Web.Routing; using SmartStore.Core; using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Localization; +using SmartStore.Core.Logging; using SmartStore.Core.Plugins; -using SmartStore.Shipping.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Configuration; using SmartStore.Services.Localization; -using SmartStore.Core.Logging; using SmartStore.Services.Shipping; using SmartStore.Services.Shipping.Tracking; +using SmartStore.Shipping.Services; namespace SmartStore.Shipping { - [SystemName("Shipping.ByTotal")] + [SystemName("Shipping.ByTotal")] [FriendlyName("Shipping by total")] [DisplayOrder(1)] public class ByTotalProvider : IShippingRateComputationMethod, IConfigurable @@ -28,7 +28,6 @@ public class ByTotalProvider : IShippingRateComputationMethod, IConfigurable private readonly ISettingService _settingService; private readonly ILocalizationService _localizationService; - /// /// Ctor /// @@ -56,14 +55,18 @@ public ByTotalProvider(IShippingService shippingService, this._logger = logger; this._settingService = settingService; this._localizationService = localizationService; - } - #region Properties + T = NullLocalizer.Instance; + } - /// - /// Gets a shipping rate computation method type - /// - public ShippingRateComputationMethodType ShippingRateComputationMethodType + #region Properties + + public Localizer T { get; set; } + + /// + /// Gets a shipping rate computation method type + /// + public ShippingRateComputationMethodType ShippingRateComputationMethodType { get { @@ -158,7 +161,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0) { - response.AddError("No shipment items"); + response.AddError(T("Admin.System.Warnings.NoShipmentItems")); return response; } diff --git a/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs b/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs index a4ae31c94e..cc303cee6e 100644 --- a/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs +++ b/src/Plugins/SmartStore.Shipping/Providers/FixedRateProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Web.Routing; using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Localization; using SmartStore.Core.Plugins; using SmartStore.Services.Configuration; using SmartStore.Services.Localization; @@ -10,27 +11,29 @@ namespace SmartStore.Shipping { - /// - /// Fixed rate shipping computation provider - /// - [SystemName("Shipping.FixedRate")] + /// + /// Fixed rate shipping computation provider + /// + [SystemName("Shipping.FixedRate")] [FriendlyName("Fixed Rate Shipping")] [DisplayOrder(0)] public class FixedRateProvider : IShippingRateComputationMethod, IConfigurable { private readonly ISettingService _settingService; private readonly IShippingService _shippingService; - private readonly ILocalizationService _localizationService; public FixedRateProvider(ISettingService settingService, - IShippingService shippingService, ILocalizationService localizationService) + IShippingService shippingService) { this._settingService = settingService; this._shippingService = shippingService; - _localizationService = localizationService; - } - - private decimal GetRate(int shippingMethodId) + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + private decimal GetRate(int shippingMethodId) { string key = string.Format("ShippingRateComputationMethod.FixedRate.Rate.ShippingMethodId{0}", shippingMethodId); decimal rate = this._settingService.GetSettingByKey(key); @@ -51,7 +54,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0) { - response.AddError("No shipment items"); + response.AddError(T("Admin.System.Warnings.NoShipmentItems")); return response; } diff --git a/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs b/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs index 0c59828d2a..40334476a9 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs +++ b/src/Plugins/SmartStore.ShippingByWeight/ByWeightShippingComputationMethod.cs @@ -3,15 +3,16 @@ using System.Web.Routing; using SmartStore.Core; using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Localization; using SmartStore.Core.Plugins; -using SmartStore.ShippingByWeight.Data; -using SmartStore.ShippingByWeight.Data.Migrations; -using SmartStore.ShippingByWeight.Services; using SmartStore.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Localization; using SmartStore.Services.Shipping; using SmartStore.Services.Shipping.Tracking; +using SmartStore.ShippingByWeight.Data; +using SmartStore.ShippingByWeight.Data.Migrations; +using SmartStore.ShippingByWeight.Services; namespace SmartStore.ShippingByWeight { @@ -32,6 +33,7 @@ public class ByWeightShippingComputationMethod : BasePlugin, IShippingRateComput #endregion #region Ctor + public ByWeightShippingComputationMethod(IShippingService shippingService, IStoreContext storeContext, IShippingByWeightService shippingByWeightService, @@ -51,12 +53,17 @@ public ByWeightShippingComputationMethod(IShippingService shippingService, this._localizationService = localizationService; this._priceFormatter = priceFormatter; this._services = services; - } - #endregion - #region Utilities + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + #endregion + + #region Utilities - private decimal? GetRate(decimal subTotal, decimal weight, int shippingMethodId, int storeId, int countryId, string zip) + private decimal? GetRate(decimal subTotal, decimal weight, int shippingMethodId, int storeId, int countryId, string zip) { decimal? shippingTotal = null; @@ -120,7 +127,7 @@ public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest get if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0) { - response.AddError("No shipment items"); + response.AddError(T("Admin.System.Warnings.NoShipmentItems")); return response; } diff --git a/src/Plugins/SmartStore.ShippingByWeight/Controllers/ShippingByWeightController.cs b/src/Plugins/SmartStore.ShippingByWeight/Controllers/ShippingByWeightController.cs index aaa92a955f..383d77dfa2 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/Controllers/ShippingByWeightController.cs +++ b/src/Plugins/SmartStore.ShippingByWeight/Controllers/ShippingByWeightController.cs @@ -50,8 +50,10 @@ public ShippingByWeightController( public ActionResult Configure() { var shippingMethods = _shippingService.GetAllShippingMethods(); - if (shippingMethods.Count == 0) - return Content("No shipping methods can be loaded"); + if (shippingMethods.Count == 0) + { + return Content(T("Admin.Configuration.Shipping.Methods.NoMethodsLoaded")); + } var model = new ShippingByWeightListModel(); var countries = _countryService.GetAllCountries(true); diff --git a/src/Plugins/SmartStore.Tax/Controllers/TaxByRegionController.cs b/src/Plugins/SmartStore.Tax/Controllers/TaxByRegionController.cs index 53bc5628e7..ca43d31f14 100644 --- a/src/Plugins/SmartStore.Tax/Controllers/TaxByRegionController.cs +++ b/src/Plugins/SmartStore.Tax/Controllers/TaxByRegionController.cs @@ -70,7 +70,7 @@ public ActionResult Configure() var tc = _taxCategoryService.GetTaxCategoryById(x.TaxCategoryId); m.TaxCategoryName = (tc != null) ? tc.Name : ""; var c = _countryService.GetCountryById(x.CountryId); - m.CountryName = (c != null) ? c.Name : "Unavailable"; + m.CountryName = (c != null) ? c.Name : T("Common.Unavailable").Text; var s = _stateProvinceService.GetStateProvinceById(x.StateProvinceId); m.StateProvinceName = (s != null) ? s.Name : "*"; m.Zip = (!String.IsNullOrEmpty(x.Zip)) ? x.Zip : "*"; @@ -99,7 +99,7 @@ public ActionResult RatesList(GridCommand command) var tc = _taxCategoryService.GetTaxCategoryById(x.TaxCategoryId); m.TaxCategoryName = (tc != null) ? tc.Name : ""; var c = _countryService.GetCountryById(x.CountryId); - m.CountryName = (c != null) ? c.Name : "Unavailable"; + m.CountryName = (c != null) ? c.Name : T("Common.Unavailable").Text; var s = _stateProvinceService.GetStateProvinceById(x.StateProvinceId); m.StateProvinceName = (s != null) ? s.Name : "*"; m.Zip = (!String.IsNullOrEmpty(x.Zip)) ? x.Zip : "*"; From 07432d249b21eb3b5f8e089bb20ef63d322ea786 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 7 Jan 2016 11:52:49 +0100 Subject: [PATCH 242/732] GMC feed: Supporting fields multipack, bundle, adult, energy efficiency class and custom label (0 to 4) --- changelog.md | 1 + .../201512151526290_ImportFramework.cs | 4 + .../FeedGoogleMerchantCenterController.cs | 31 +++- .../Data/GoogleProductRecordMap.cs | 10 +- .../201601061649324_IsBundle.Designer.cs | 29 ++++ .../Migrations/201601061649324_IsBundle.cs | 34 ++++ .../Migrations/201601061649324_IsBundle.resx | 126 +++++++++++++++ .../Description.txt | 2 +- .../Domain/GoogleProductRecord.cs | 13 +- .../SmartStore.GoogleMerchantCenter/Events.cs | 39 ++--- .../Extensions/MiscExtensions.cs | 13 +- .../Localization/resources.de-de.xml | 84 +++++++--- .../Localization/resources.en-us.xml | 84 +++++++--- .../Models/FeedGoogleMerchantCenterModel.cs | 53 +++++-- .../Models/ProfileConfigurationModel.cs | 4 - .../Providers/GmcXmlExportProvider.cs | 59 ++++++- .../Services/GoogleFeedService.cs | 54 ++++++- .../SmartStore.GoogleMerchantCenter.csproj | 7 + .../FeedGoogleMerchantCenter/Configure.cshtml | 146 ++++++++++++++---- .../ProductEditTab.cshtml | 97 +++++++++++- .../ProfileConfiguration.cshtml | 15 +- .../changelog.md | 7 + 22 files changed, 748 insertions(+), 164 deletions(-) create mode 100644 src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.Designer.cs create mode 100644 src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs create mode 100644 src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.resx diff --git a/changelog.md b/changelog.md index 2308726b85..f6fa16e556 100644 --- a/changelog.md +++ b/changelog.md @@ -32,6 +32,7 @@ * #840 Activity log: Have option to exclude search engine activity * #841 Activity log for deleting an order * More settings to control creation of SEO names +* GMC feed: Supporting fields multipack, bundle, adult, energy efficiency class and custom label (0 to 4) ### Improvements * (Perf) Implemented static caches for URL aliases and localized properties. Increases app startup and request speed by up to 30%. diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 35b6497ae1..7f2dec3086 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -312,6 +312,10 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.System.Warnings.NoShipmentItems", "No shipment items", "Keine Versand-Artikel"); + + builder.AddOrUpdate("Admin.System.Warnings.DigitsOnly", + "Please enter digits only.", + "Bitte nur Ziffern eingeben."); } } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs index 74b368fe98..6c37461c87 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs @@ -36,21 +36,31 @@ public ActionResult ProductEditTab(int productId) var culture = CultureInfo.InvariantCulture; var model = new GoogleProductModel { ProductId = productId }; var entity = _googleFeedService.GetGoogleProductRecord(productId); + string notSpecified = T("Common.Unspecified"); if (entity != null) { model.Taxonomy = entity.Taxonomy; model.Gender = entity.Gender; model.AgeGroup = entity.AgeGroup; + model.IsAdult = entity.IsAdult; model.Color = entity.Color; model.Size = entity.Size; model.Material = entity.Material; model.Pattern = entity.Pattern; - model.Exporting = entity.Export; + model.Export2 = entity.Export; + model.Multipack2 = entity.Multipack; + model.IsBundle = entity.IsBundle; + model.EnergyEfficiencyClass = entity.EnergyEfficiencyClass; + model.CustomLabel0 = entity.CustomLabel0; + model.CustomLabel1 = entity.CustomLabel1; + model.CustomLabel2 = entity.CustomLabel2; + model.CustomLabel3 = entity.CustomLabel3; + model.CustomLabel4 = entity.CustomLabel4; } else { - model.Exporting = true; + model.Export2 = true; } ViewBag.DefaultCategory = ""; @@ -58,8 +68,13 @@ public ActionResult ProductEditTab(int productId) ViewBag.DefaultSize = ""; ViewBag.DefaultMaterial = ""; ViewBag.DefaultPattern = ""; - ViewBag.DefaultGender = T("Common.Auto"); - ViewBag.DefaultAgeGroup = T("Common.Auto"); + ViewBag.DefaultGender = notSpecified; + ViewBag.DefaultAgeGroup = notSpecified; + ViewBag.DefaultIsAdult = ""; + ViewBag.DefaultMultipack2 = ""; + ViewBag.DefaultIsBundle = ""; + ViewBag.DefaultEnergyEfficiencyClass = notSpecified; + ViewBag.DefaultCustomLabel = ""; // we do not have export profile context here, so we simply use the first profile var profile = _exportService.GetExportProfilesBySystemName(GmcXmlExportProvider.SystemName).FirstOrDefault(); @@ -99,6 +114,11 @@ public ActionResult ProductEditTab(int productId) new SelectListItem { Value = "kids", Text = T("Plugins.Feed.Froogle.AgeGroupKids") }, }; + ViewBag.AvailableEnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text + .SplitSafe(",") + .Select(x => new SelectListItem { Value = x, Text = x }) + .ToList(); + var result = PartialView(model); result.ViewData.TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "CustomProperties[GMC]" }; return result; @@ -114,8 +134,9 @@ public ActionResult Configure() { var model = new FeedGoogleMerchantCenterModel(); - model.AvailableGoogleCategories = _googleFeedService.GetTaxonomyList(); model.GridPageSize = _adminAreaSettings.GridPageSize; + model.AvailableGoogleCategories = _googleFeedService.GetTaxonomyList(); + model.EnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text.SplitSafe(","); return View(model); } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs index 5b4e0817ab..98aaec1aa4 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs @@ -9,6 +9,14 @@ public GoogleProductRecordMap() { this.ToTable("GoogleProduct"); this.HasKey(x => x.Id); - } + + this.Property(x => x.EnergyEfficiencyClass).HasMaxLength(50); + + this.Property(x => x.CustomLabel0).HasMaxLength(100); + this.Property(x => x.CustomLabel1).HasMaxLength(100); + this.Property(x => x.CustomLabel2).HasMaxLength(100); + this.Property(x => x.CustomLabel3).HasMaxLength(100); + this.Property(x => x.CustomLabel4).HasMaxLength(100); + } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.Designer.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.Designer.cs new file mode 100644 index 0000000000..652bba012c --- /dev/null +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.Designer.cs @@ -0,0 +1,29 @@ +// +namespace SmartStore.GoogleMerchantCenter.Data.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class IsBundle : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(IsBundle)); + + string IMigrationMetadata.Id + { + get { return "201601061649324_IsBundle"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs new file mode 100644 index 0000000000..bc8af7c35e --- /dev/null +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs @@ -0,0 +1,34 @@ +namespace SmartStore.GoogleMerchantCenter.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class IsBundle : DbMigration + { + public override void Up() + { + AddColumn("dbo.GoogleProduct", "Multipack", c => c.Int(nullable: false)); + AddColumn("dbo.GoogleProduct", "IsBundle", c => c.Boolean()); + AddColumn("dbo.GoogleProduct", "IsAdult", c => c.Boolean()); + AddColumn("dbo.GoogleProduct", "EnergyEfficiencyClass", c => c.String(maxLength: 50)); + AddColumn("dbo.GoogleProduct", "CustomLabel0", c => c.String(maxLength: 100)); + AddColumn("dbo.GoogleProduct", "CustomLabel1", c => c.String(maxLength: 100)); + AddColumn("dbo.GoogleProduct", "CustomLabel2", c => c.String(maxLength: 100)); + AddColumn("dbo.GoogleProduct", "CustomLabel3", c => c.String(maxLength: 100)); + AddColumn("dbo.GoogleProduct", "CustomLabel4", c => c.String(maxLength: 100)); + } + + public override void Down() + { + DropColumn("dbo.GoogleProduct", "CustomLabel4"); + DropColumn("dbo.GoogleProduct", "CustomLabel3"); + DropColumn("dbo.GoogleProduct", "CustomLabel2"); + DropColumn("dbo.GoogleProduct", "CustomLabel1"); + DropColumn("dbo.GoogleProduct", "CustomLabel0"); + DropColumn("dbo.GoogleProduct", "EnergyEfficiencyClass"); + DropColumn("dbo.GoogleProduct", "IsAdult"); + DropColumn("dbo.GoogleProduct", "IsBundle"); + DropColumn("dbo.GoogleProduct", "Multipack"); + } + } +} diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.resx b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.resx new file mode 100644 index 0000000000..ebf95a7c54 --- /dev/null +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.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 + + + H4sIAAAAAAAEAM2a227jNhCG7wv0HQRdtUDW8iEF2sDeReIki6BxHETJ3tPS2FGXIlWSCuy+Wi/6SH2Fjs6iJNuSHQe9s0fkNwdS1K+x//37n/GXtU+NNxDS42xiDnp90wDmcNdjq4kZquWnX80vn3/8YXzj+mvjWzZuFI3DmUxOzFelggvLks4r+ET2fM8RXPKl6jnct4jLrWG//5s1GFiACBNZhjF+CpnyfIi/4NcpZw4EKiR0xl2gMrXjFTumGg/EBxkQByam7ROhbMUF9L5yvqIwA+G8EqamwBSI3jVRxDQuqUcwNhvo0jQIY1wRhZFfvEiwleBsZQdoIPR5EwCOWxIqIc3oohjeNrn+MErOKiZmKCeUivsdgYNRWi2rOv2gmpt5NbGeN1h3tYmyjms6MZMaPgruho56AocL1zSqfi+mVERzWlQfp3ms10A9M/bMPcu317A37PV7/TNjGlIVCpgwCJUg9Mx4DBfUc36HzTP/DmzCQkrL6WGCeE0zoAnDCECozRMs06TvMEdLn2dVJ+bTSnOSGtwxNRqaxgM6JwsK+e4p1SvO8iswEESB+0gU5sciBsT1r3mv+EoLt9/lbswzWXPG/U1GwZ2Pt7VpzMj6HthKvU5M/Ggat94a3MySkl+Yh6cATlIi3OsIM3VBnNzN5Qq+Ch4GJ3c05ZSfPh3b+wtO7mSGG1DgQXdyR+kmP7mfOwV+vAuKu+N0vuQzD/GMzT1dcU6BsM534lRAdBDM2YtyMhY+qOAZH4KdYS+B+36wm3XAhTo2vxke1B4+n78fd2DdyauQuRRq4eybduliAB1n3eDpvNrcLJee46Hk2UwpkXLHlvql/x4nS/xkvScLoP0dvgb9d3Y2+Ehnw490NvpIZ+fv7GxsFYKsLtNQFitUUyCatNp88Qc4KhoCa9Ug2VDppqpNpp71JBMXNqjtQhDvhiK8REw3KbvmlPLgCzFvJWo+U/3WFtk/npEgwOqWXgNSi2En7wDTT3Z3KewnDMuRDYo4jzb3hAqOrKByNZIGLtx6QqroPWNBovWdun5t2I6l2rIMmeMdq1EVtsXiZJOjz6m4aPGa1LiYFSdF8W+xHj5OjksDedzNrxA1SvwuRygRDbIa5Vbos23SfNfsklAuQ0rm9qxCLZdRhbU9KZPDZU5ma08p1G6ZU1jbk1I5W8akpvaMRKyWEYmlPaFQomVKYe2w6pnU1NY8M7bnaFJS24TlCx14hVzUaIW5w5ppilFbOu1Ke6IuG8tE/Up7YqYdy6zM1mFfFNpR2xiFucsKZAJSX4DM2oWUakodlBo71KhZZWolax7SYa9oolLbK9qVg4iDrcTBgcThVuLwQOJoK3F0IPF8K/G8iTi2Kg/J6qPaqj2rK+2nqg7YpaiqQ3LvubKqKKhxqmb2d1dr8iYZYhpYqjfPjaSNvZF4PCYCwv6TTiluWlUMmBHmLUGqpENnDvuDYaUd+/9pjVpSurR7f/TDO45eVOC9PcWOL9u1JmPs5cgWI3sjkcwUP/lk/XOZ1r2NeBSq2io8Cqa1A48ilVt+R4Gqbb2jYJXW3VGshvbccbxqC27hdd+lTe23SPKo92q/HQzT22+H5FZrvR1yG1cbb5VA2i2U1oQ7gLCzIZdtolpLruOt3NB/a0TH3ZyD2YMTsocnZI9OyD4/jt2tVVZv03TufrVtfiUKCc+ABccMk/C1gQf3x+qqbWyVfzkfX4P0VgUi+h2dgRPJoQKajbljS54tDuZdjigbUj1ZQBE82MilUN6SOAovOyBl3Pf8RmgY3bL+Atw7Ng9VEKpLKcFfUK1VMrZ2+4+bgHrM43kQfZPvkQKG6UVn85xdhR5187hv64fiNkS0k1LVhVHZKlJfq01OeuCsJSgt3zUEkcxh6hn8gCJMzplN3uCQ2F4k3MOKOJtMfG+H7F8Ivezja4+sBPFlyijmR/8GsaK/g3z+D2Zr4jJAIgAA + + + dbo + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt b/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt index 043c9f3476..55dfd4845a 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt @@ -1,7 +1,7 @@ FriendlyName: Google Merchant Center (GMC) feed SystemName: SmartStore.GoogleMerchantCenter Group: Marketing -Version: 2.2.0.4 +Version: 2.2.0.5 MinAppVersion: 2.2.0 Author: SmartStore AG DisplayOrder: 1 diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs index cca0f73827..7064e84880 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs @@ -29,5 +29,16 @@ public GoogleProductRecord() public DateTime UpdatedOnUtc { get; set; } public bool Export { get; set; } - } + + public int Multipack { get; set; } + public bool? IsBundle { get; set; } + public bool? IsAdult { get; set; } + public string EnergyEfficiencyClass { get; set; } + + public string CustomLabel0 { get; set; } + public string CustomLabel1 { get; set; } + public string CustomLabel2 { get; set; } + public string CustomLabel3 { get; set; } + public string CustomLabel4 { get; set; } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs index 3673c52c20..ea0bd90c3a 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs @@ -10,8 +10,7 @@ namespace SmartStore.GoogleMerchantCenter { public class Events : IConsumer, - IConsumer/*, - IConsumer*/ + IConsumer { private readonly IGoogleFeedService _googleService; @@ -25,6 +24,7 @@ public void HandleEvent(TabStripCreated eventMessage) if (eventMessage.TabStripName == "product-edit") { var productId = ((TabbableModel)eventMessage.Model).Id; + eventMessage.ItemFactory.Add().Text("GMC") .Name("tab-gmc") .Icon("fa fa-google fa-lg fa-fw") @@ -34,30 +34,6 @@ public void HandleEvent(TabStripCreated eventMessage) } } - //public void HandleEvent(RowExportingEvent eventMessage) - //{ - // if (eventMessage.EntityType != ExportEntityType.Product) - // return; - - // var row = eventMessage.Row; - // var product = eventMessage.Row.Entity as Product; - - // if (product == null) - // return; - - // var gmc = _googleService.GetGoogleProductRecord(product.Id); - // if (gmc == null) - // return; - - // row["_GMC_AgeGroup"] = gmc.AgeGroup; - // row["_GMC_Color"] = gmc.Color; - // row["_GMC_Gender"] = gmc.Gender; - // row["_GMC_Size"] = gmc.Size; - // row["_GMC_Taxonomy"] = gmc.Taxonomy; - // row["_GMC_Material"] = gmc.Material; - // row["_GMC_Pattern"] = gmc.Pattern; - //} - public void HandleEvent(ModelBoundEvent eventMessage) { if (!eventMessage.BoundModel.CustomProperties.ContainsKey("GMC")) @@ -88,8 +64,17 @@ public void HandleEvent(ModelBoundEvent eventMessage) entity.Taxonomy = model.Taxonomy; entity.Material = model.Material; entity.Pattern = model.Pattern; - entity.Export = model.Exporting; + entity.Export = model.Export2; entity.UpdatedOnUtc = utcNow; + entity.Multipack = model.Multipack2 ?? 0; + entity.IsBundle = model.IsBundle; + entity.IsAdult = model.IsAdult; + entity.EnergyEfficiencyClass = model.EnergyEfficiencyClass; + entity.CustomLabel0 = model.CustomLabel0; + entity.CustomLabel1 = model.CustomLabel1; + entity.CustomLabel2 = model.CustomLabel2; + entity.CustomLabel3 = model.CustomLabel3; + entity.CustomLabel4 = model.CustomLabel4; entity.IsTouched = entity.IsTouched(); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs index c140111c1e..5a8dd0168d 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs @@ -9,7 +9,7 @@ public static string XEditableLink(this HtmlHelper hlp, string fieldName, string { string displayText = null; - if (fieldName == "Gender" || fieldName == "AgeGroup" || fieldName == "Exporting") + if (fieldName == "Gender" || fieldName == "AgeGroup" || fieldName == "Export2" || fieldName == "IsBundle" || fieldName == "IsAdult") displayText = "<#= {0}Localize #>".FormatInvariant(fieldName); else displayText = "<#= {0} #>".FormatInvariant(fieldName); @@ -22,12 +22,15 @@ public static string XEditableLink(this HtmlHelper hlp, string fieldName, string return skeleton.FormatInvariant(fieldName, fieldName.ToLower(), type, displayText); } - public static bool IsTouched(this GoogleProductRecord product) + public static bool IsTouched(this GoogleProductRecord p) { - if (product != null) + if (p != null) { - return product.Taxonomy.HasValue() || product.Gender.HasValue() || product.AgeGroup.HasValue() || product.Color.HasValue() || - product.Size.HasValue() || product.Material.HasValue() || product.Pattern.HasValue() || product.ItemGroupId.HasValue() || !product.Export; + return + p.Taxonomy.HasValue() || p.Gender.HasValue() || p.AgeGroup.HasValue() || p.Color.HasValue() || + p.Size.HasValue() || p.Material.HasValue() || p.Pattern.HasValue() || p.ItemGroupId.HasValue() || + !p.Export || p.Multipack != 0 || p.IsBundle.HasValue || p.IsAdult.HasValue || p.EnergyEfficiencyClass.HasValue() || + p.CustomLabel0.HasValue() || p.CustomLabel1.HasValue() || p.CustomLabel2.HasValue() || p.CustomLabel3.HasValue() || p.CustomLabel4.HasValue(); } return false; } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml index e4aa14c576..17b90e856c 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml @@ -37,24 +37,9 @@ Google-Kategorie - - Geschlecht - - - Altersgruppe - - - Farbe - - - Größe - - - Material - - - Muster - + + Legt die dem Artikel entsprechende Google-Kategorie fest. Zwingend erforderlich. + Zusätzliche Bilder @@ -148,12 +133,6 @@ Muster oder grafisches Druckdesign eines Produktes. Angabe ist z.B. bei Bekleidung sinnvoll. - - Nur online zu kaufen - - - Legt fest, ob Produkte nur online erworben werden können. Aktivieren Sie diese Option nicht, falls Produkte auch in Ihrem Ladengeschäft erhältlich sind. - Leer @@ -196,4 +175,61 @@ Legt fest, ob der Grundpreis eines Produktes exportiert werden soll. + + Multipack + + + Anzahl identischer Produkte in einem händlerdefinierten Multipack. Muss größer 1 sein. + + + Bundle + + + Händlerdefiniertes Produktpaket bestehend aus einem Haupt- und mehreren Zubehörartikel bzw. Add-ons. + + + Nicht jugendfrei + + + Produkt, welches nur für erwachsene Nutzer bestimmt ist. + + + Energieeffizienz + + + Die Energieeffizienzklasse des Produktes gemäß EU-Richtlinie 2010/30/EU. Mögliche Werte sind G, F, E, D, C, B, A, A+, A++, A+++. + + + A+++,A++,A+,A,B,C,D,E,F,G + + + Label 0 + + + Benutzerdefiniertes Label 0. Dient der individuellen Gruppierung von Produkten. + + + Label 1 + + + Benutzerdefiniertes Label 1. Dient der individuellen Gruppierung von Produkten. + + + Label 2 + + + Benutzerdefiniertes Label 2. Dient der individuellen Gruppierung von Produkten. + + + Label 3 + + + Benutzerdefiniertes Label 3. Dient der individuellen Gruppierung von Produkten. + + + Label 4 + + + Benutzerdefiniertes Label 4. Dient der individuellen Gruppierung von Produkten. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml index a80721be22..3b75a8c306 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml @@ -37,24 +37,9 @@ Google Category - - Gender - - - Age group - - - Color - - - Size - - - Material - - - Pattern - + + Specifies the Google category corresponding to the product. Mandatory. + Additional images @@ -148,12 +133,6 @@ The pattern or graphic print featured on a product. Usefull for clothes for instance. - - Only online available - - - Check the box if products can only be purchased online. - Empty @@ -196,4 +175,61 @@ Activate this option if you want to export the base price of the product. + + Multipack + + + Number of identical products in a merchant defined multipack. Must be greater than 1. + + + Bundle + + + Merchant defined product package consisting of one main and several accessories or add-ons. + + + Adult + + + Product, which is intended only for adult users. + + + Energy efficiency + + + The energy efficiency class of the product in accordance with EU directive 2010/30/EU. Possible values are G, F, E, D, C, B, A, A +, A++, A+++. + + + A+++,A++,A+,A,B,C,D,E,F,G + + + Label 0 + + + Custom label 0 serves the individual grouping of products. + + + Label 1 + + + Custom label 1 serves the individual grouping of products. + + + Label 2 + + + Custom label 2 serves the individual grouping of products. + + + Label 3 + + + Custom label 3 serves the individual grouping of products. + + + Label 4 + + + Custom label 4 serves the individual grouping of products. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs index 0ec41770fe..36c5f02799 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Text; using Newtonsoft.Json; using SmartStore.Core.Domain.Catalog; using SmartStore.Web.Framework; @@ -18,10 +19,12 @@ public string AvailableGoogleCategoriesAsJson { if (AvailableGoogleCategories != null && AvailableGoogleCategories.Length > 0) return JsonConvert.SerializeObject(AvailableGoogleCategories); - return ""; + return "[ ]"; } } + public string[] EnergyEfficiencyClasses { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.SearchProductName")] public string SearchProductName { get; set; } @@ -29,7 +32,6 @@ public string AvailableGoogleCategoriesAsJson public string SearchIsTouched { get; set; } } - public class GoogleProductModel : ModelBase { public int TotalCount { get; set; } @@ -74,35 +76,66 @@ public string ProductTypeLabelHint [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.GoogleCategory")] public string Taxonomy { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.Gender")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Gender")] public string Gender { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.AgeGroup")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.AgeGroup")] public string AgeGroup { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.Color")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Color")] public string Color { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.Size")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Size")] public string Size { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.Material")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Material")] public string Material { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.Pattern")] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] public string Pattern { get; set; } [SmartResourceDisplayName("Common.Export")] public int Export { get; set; } [SmartResourceDisplayName("Common.Export")] - public bool Exporting + public bool Export2 { get { return Export != 0; } set { Export = (value ? 1 : 0); } } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] + public int Multipack { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] + public int? Multipack2 + { + get { return Multipack > 0 ? Multipack : (int?)null; } + set { Multipack = (value ?? 0); } + } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.IsBundle")] + public bool? IsBundle { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.IsAdult")] + public bool? IsAdult { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.EnergyEfficiencyClass")] + public string EnergyEfficiencyClass { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel0")] + public string CustomLabel0 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel1")] + public string CustomLabel1 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel2")] + public string CustomLabel2 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel3")] + public string CustomLabel3 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel4")] + public string CustomLabel4 { get; set; } + public string GenderLocalize { get; set; } public string AgeGroupLocalize { get; set; } - public string ExportingLocalize { get; set; } + public string Export2Localize { get; set; } + public string IsBundleLocalize { get; set; } + public string IsAdultLocalize { get; set; } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs index 6b96944375..69d81d50dd 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs @@ -14,7 +14,6 @@ public class ProfileConfigurationModel public ProfileConfigurationModel() { Condition = "new"; - OnlineOnly = true; AdditionalImages = true; SpecialPrice = true; } @@ -66,9 +65,6 @@ public string AvailableGoogleCategoriesAsJson [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] public string Pattern { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.OnlineOnly")] - public bool OnlineOnly { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.ExpirationDays")] public int ExpirationDays { get; set; } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index b43d2abe70..3a5cc536f6 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -157,6 +157,7 @@ protected override void Export(IExportExecuteContext context) { dynamic currency = context.Currency; string measureWeightSystemKey = ""; + var dateFormat = "yyyy-MM-ddTHH:mmZ"; var measureWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId); @@ -287,9 +288,15 @@ protected override void Export(IExportExecuteContext context) writer.WriteElementString("g", "condition", _googleNamespace, condition); writer.WriteElementString("g", "availability", _googleNamespace, availability); + if (availability == "preorder" && entity.AvailableStartDateTimeUtc.HasValue && entity.AvailableStartDateTimeUtc.Value > DateTime.UtcNow) + { + var availabilityDate = entity.AvailableStartDateTimeUtc.Value.ToString(dateFormat); + + writer.WriteElementString("g", "availability_date", _googleNamespace, availabilityDate); + } + if (config.SpecialPrice && specialPrice.HasValue && entity.SpecialPriceStartDateTimeUtc.HasValue && entity.SpecialPriceEndDateTimeUtc.HasValue) { - var dateFormat = "yyyy-MM-ddTHH:mmZ"; var specialPriceDate = "{0}/{1}".FormatInvariant( entity.SpecialPriceStartDateTimeUtc.Value.ToString(dateFormat), entity.SpecialPriceEndDateTimeUtc.Value.ToString(dateFormat)); @@ -321,7 +328,6 @@ protected override void Export(IExportExecuteContext context) writer.WriteCData("pattern", gmc != null && gmc.Pattern.HasValue() ? gmc.Pattern : config.Pattern, "g", _googleNamespace); writer.WriteCData("item_group_id", gmc != null && gmc.ItemGroupId.HasValue() ? gmc.ItemGroupId : "", "g", _googleNamespace); - writer.WriteElementString("g", "online_only", _googleNamespace, config.OnlineOnly ? "y" : "n"); writer.WriteElementString("g", "identifier_exists", _googleNamespace, gtin.HasValue() || brand.HasValue() || mpn.HasValue() ? "TRUE" : "FALSE"); if (config.ExpirationDays > 0) @@ -360,11 +366,56 @@ protected override void Export(IExportExecuteContext context) } } + if (gmc != null && gmc.Multipack > 1) + { + writer.WriteElementString("g", "multipack", _googleNamespace, gmc.Multipack.ToString()); + } + + if (gmc != null && gmc.IsBundle.HasValue) + { + writer.WriteElementString("g", "is_bundle", _googleNamespace, gmc.IsBundle.Value ? "TRUE" : "FALSE"); + } + + if (gmc != null && gmc.IsAdult.HasValue) + { + writer.WriteElementString("g", "adult", _googleNamespace, gmc.IsAdult.Value ? "TRUE" : "FALSE"); + } + + if (gmc != null && gmc.EnergyEfficiencyClass.HasValue()) + { + writer.WriteElementString("g", "energy_efficiency_class", _googleNamespace, gmc.EnergyEfficiencyClass); + } + + if (gmc != null && gmc.CustomLabel0.HasValue()) + { + writer.WriteElementString("g", "custom_label_0", _googleNamespace, gmc.CustomLabel0); + } + + if (gmc != null && gmc.CustomLabel1.HasValue()) + { + writer.WriteElementString("g", "custom_label_1", _googleNamespace, gmc.CustomLabel1); + } + + if (gmc != null && gmc.CustomLabel2.HasValue()) + { + writer.WriteElementString("g", "custom_label_2", _googleNamespace, gmc.CustomLabel2); + } + + if (gmc != null && gmc.CustomLabel3.HasValue()) + { + writer.WriteElementString("g", "custom_label_3", _googleNamespace, gmc.CustomLabel3); + } + + if (gmc != null && gmc.CustomLabel4.HasValue()) + { + writer.WriteElementString("g", "custom_label_4", _googleNamespace, gmc.CustomLabel4); + } + ++context.RecordsSucceeded; } - catch (Exception exc) + catch (Exception exception) { - context.RecordException(exc, entity.Id); + context.RecordException(exception, entity.Id); } writer.WriteEndElement(); // item diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs index 48d574215e..b5fab3af04 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs @@ -125,9 +125,36 @@ public void Upsert(int pk, string name, string value) case "Pattern": product.Pattern = value; break; - case "Exporting": + case "Export2": product.Export = value.ToBool(true); break; + case "Multipack2": + product.Multipack = value.ToInt(); + break; + case "IsBundle": + product.IsBundle = (value.IsEmpty() ? (bool?)null : value.ToBool()); + break; + case "IsAdult": + product.IsAdult = (value.IsEmpty() ? (bool?)null : value.ToBool()); + break; + case "EnergyEfficiencyClass": + product.EnergyEfficiencyClass = value; + break; + case "CustomLabel0": + product.CustomLabel0 = value; + break; + case "CustomLabel1": + product.CustomLabel1 = value; + break; + case "CustomLabel2": + product.CustomLabel2 = value; + break; + case "CustomLabel3": + product.CustomLabel3 = value; + break; + case "CustomLabel4": + product.CustomLabel4 = value; + break; } product.UpdatedOnUtc = utcNow; @@ -156,7 +183,8 @@ public GridModel GetGridModel(GridCommand command, string se string yes = T("Admin.Common.Yes"); string no = T("Admin.Common.No"); - // there's no way to share a context instance across repositories which makes GoogleProductObjectContext pretty useless here. + // there's no way to share a context instance across repositories in EF. + // so we have to fallback to pure SQL here to get the data paged and filtered. var whereClause = new StringBuilder("(NOT ([t2].[Deleted] = 1)) AND ([t2].[VisibleIndividually] = 1)"); @@ -181,11 +209,11 @@ public GridModel GetGridModel(GridCommand command, string se { // fastest possible paged data query sql = - "SELECT [TotalCount], [t3].[Id], [t3].[Name], [t3].[SKU], [t3].[ProductTypeId], [t3].[value] AS [Taxonomy], [t3].[value2] AS [Gender], [t3].[value3] AS [AgeGroup], [t3].[value4] AS [Color], [t3].[value5] AS [Size], [t3].[value6] AS [Material], [t3].[value7] AS [Pattern], [t3].[value8] AS [Export]" + + "SELECT [TotalCount], [t3].[Id], [t3].[Name], [t3].[SKU], [t3].[ProductTypeId], [t3].[value] AS [Taxonomy], [t3].[value2] AS [Gender], [t3].[value3] AS [AgeGroup], [t3].[value4] AS [Color], [t3].[value5] AS [Size], [t3].[value6] AS [Material], [t3].[value7] AS [Pattern], [t3].[value8] AS [Export], [t3].[value9] AS [Multipack], [t3].[value10] AS [IsBundle], [t3].[value11] AS [IsAdult], [t3].[value12] AS [EnergyEfficiencyClass], [t3].[value13] AS [CustomLabel0], [t3].[value14] AS [CustomLabel1], [t3].[value15] AS [CustomLabel2], [t3].[value16] AS [CustomLabel3], [t3].[value17] AS [CustomLabel4]" + " FROM (" + - " SELECT COUNT(id) OVER() [TotalCount], ROW_NUMBER() OVER (ORDER BY [t2].[Name]) AS [ROW_NUMBER], [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8]" + + " SELECT COUNT(id) OVER() [TotalCount], ROW_NUMBER() OVER (ORDER BY [t2].[Name]) AS [ROW_NUMBER], [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8], [t2].[value9], [t2].[value10], [t2].[value11], [t2].[value12], [t2].[value13], [t2].[value14], [t2].[value15], [t2].[value16], [t2].[value17]" + " FROM (" + - " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched]" + + " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched]" + " FROM [Product] AS [t0]" + " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + " ) AS [t2]" + @@ -198,9 +226,9 @@ public GridModel GetGridModel(GridCommand command, string se { // OFFSET... FETCH NEXT requires SQL Server 2012 or SQL CE 4 sql = - "SELECT [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value] AS [Taxonomy], [t2].[value2] AS [Gender], [t2].[value3] AS [AgeGroup], [t2].[value4] AS [Color], [t2].[value5] AS [Size], [t2].[value6] AS [Material], [t2].[value7] AS [Pattern], [t2].[value8] AS [Export]" + + "SELECT [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value] AS [Taxonomy], [t2].[value2] AS [Gender], [t2].[value3] AS [AgeGroup], [t2].[value4] AS [Color], [t2].[value5] AS [Size], [t2].[value6] AS [Material], [t2].[value7] AS [Pattern], [t2].[value8] AS [Export], [t2].[value9] AS [Multipack], [t2].[value10] AS [IsBundle], [t2].[value11] AS [IsAdult], [t2].[value12] AS [EnergyEfficiencyClass], [t2].[value13] AS [CustomLabel0], [t2].[value14] AS [CustomLabel1], [t2].[value15] AS [CustomLabel2], [t2].[value16] AS [CustomLabel3], [t2].[value17] AS [CustomLabel4]" + " FROM (" + - " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched] AS [IsTouched]" + + " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched] AS [IsTouched]" + " FROM [Product] AS [t0]" + " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + " ) AS [t2]" + @@ -232,7 +260,17 @@ public GridModel GetGridModel(GridCommand command, string se if (x.AgeGroup.HasValue()) x.AgeGroupLocalize = T("Plugins.Feed.Froogle.AgeGroup" + textInfo.ToTitleCase(x.AgeGroup)); - x.ExportingLocalize = (x.Export == 0 ? no : yes); + x.Export2Localize = (x.Export == 0 ? no : yes); + + if (x.IsBundle.HasValue) + x.IsBundleLocalize = (x.IsBundle.Value ? yes : no); + else + x.IsBundleLocalize = null; + + if (x.IsAdult.HasValue) + x.IsAdultLocalize = (x.IsAdult.Value ? yes : no); + else + x.IsAdultLocalize = null; }); model.Data = data; diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj index aa2b7dca53..f027f7dd39 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj @@ -169,6 +169,10 @@ 201504211854125_IsActive.cs + + + 201601061649324_IsBundle.cs + @@ -218,6 +222,9 @@ 201504211854125_IsActive.cs + + 201601061649324_IsBundle.cs + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml index 6312648112..d91ec16571 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/Configure.cshtml @@ -3,7 +3,6 @@ @using SmartStore.GoogleMerchantCenter.Models; @using SmartStore.GoogleMerchantCenter.Providers; @using SmartStore.Web.Framework; -@using SmartStore.Web.Framework.Plugins; @using Telerik.Web.Mvc.UI; @using SmartStore.Web.Framework.UI; @@ -53,7 +52,8 @@ @Html.SmartLabelFor(m => m.SearchIsTouched) - @Html.DropDownList("SearchIsTouched", new List { + @Html.DropDownList("SearchIsTouched", new List + { new SelectListItem { Text = T("Common.Unspecified"), Value = "" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.SearchIsTouched.Touched"), Value = "touched" }, new SelectListItem { Text = T("Plugins.Feed.Froogle.SearchIsTouched.Untouched"), Value = "untouched" } @@ -82,25 +82,56 @@ }) .Columns(c => { - c.Bound(x => x.ProductId).ReadOnly().Visible(false); + c.Bound(x => x.ProductId) + .ReadOnly() + .Visible(false); c.Bound(x => x.Name) - .ReadOnly().Visible(true).Width(420) + .ReadOnly().Visible(true) .Template(x => @Html.LabeledProductName(x.ProductId, x.Name, x.ProductTypeName, x.ProductTypeLabelHint)) .ClientTemplate(@Html.LabeledProductName("ProductId", "Name")); - c.Bound(x => x.SKU).ReadOnly().Visible(true); - c.Bound(x => x.Exporting).ClientTemplate(Html.XEditableLink("Exporting", "select2")); - c.Bound(x => x.Taxonomy).ClientTemplate(Html.XEditableLink("Taxonomy", "typeahead")); - c.Bound(x => x.Gender).ClientTemplate(Html.XEditableLink("Gender", "select2")).Width(100); - c.Bound(x => x.AgeGroup).ClientTemplate(Html.XEditableLink("AgeGroup", "select2")).Width(100); - c.Bound(x => x.Color).ClientTemplate(Html.XEditableLink("Color", "text")); - c.Bound(x => x.Size).ClientTemplate(Html.XEditableLink("Size", "text")); - c.Bound(x => x.Material).ClientTemplate(Html.XEditableLink("Material", "text")); - c.Bound(x => x.Pattern).ClientTemplate(Html.XEditableLink("Pattern", "text")); + c.Bound(x => x.SKU) + .ReadOnly() + .Visible(true); + c.Bound(x => x.Export2) + .ClientTemplate(Html.XEditableLink("Export2", "select2")); + c.Bound(x => x.Taxonomy) + .ClientTemplate(Html.XEditableLink("Taxonomy", "typeahead")); + c.Bound(x => x.Gender) + .ClientTemplate(Html.XEditableLink("Gender", "select2")); + c.Bound(x => x.AgeGroup) + .ClientTemplate(Html.XEditableLink("AgeGroup", "select2")); + c.Bound(x => x.IsAdult) + .ClientTemplate(Html.XEditableLink("IsAdult", "select2")); + c.Bound(x => x.Color) + .ClientTemplate(Html.XEditableLink("Color", "text")); + c.Bound(x => x.Size) + .ClientTemplate(Html.XEditableLink("Size", "text")); + c.Bound(x => x.Material) + .ClientTemplate(Html.XEditableLink("Material", "text")); + c.Bound(x => x.Pattern) + .ClientTemplate(Html.XEditableLink("Pattern", "text")); + c.Bound(x => x.Multipack2) + .ClientTemplate(Html.XEditableLink("Multipack2", "text")); + c.Bound(x => x.IsBundle) + .ClientTemplate(Html.XEditableLink("IsBundle", "select2")); + c.Bound(x => x.EnergyEfficiencyClass) + .ClientTemplate(Html.XEditableLink("EnergyEfficiencyClass", "select2")); + c.Bound(x => x.CustomLabel0) + .ClientTemplate(Html.XEditableLink("CustomLabel0", "text")); + c.Bound(x => x.CustomLabel1) + .ClientTemplate(Html.XEditableLink("CustomLabel1", "text")); + c.Bound(x => x.CustomLabel2) + .ClientTemplate(Html.XEditableLink("CustomLabel2", "text")); + c.Bound(x => x.CustomLabel3) + .ClientTemplate(Html.XEditableLink("CustomLabel3", "text")); + c.Bound(x => x.CustomLabel4) + .ClientTemplate(Html.XEditableLink("CustomLabel4", "text")); }) .ClientEvents(e => { e.OnDataBound("OnGridDataBound"); e.OnDataBinding("OnGridDataBinding"); + e.OnError("OnGridError"); }) .DataBinding(dataBinding => { @@ -127,6 +158,11 @@ return false; }); + function OnGridError(e) { + e.preventDefault(); + alert(e.XMLHttpRequest.responseText); + } + function OnGridDataBinding(e) { e.data = { searchProductName: $('#SearchProductName').val(), @@ -135,28 +171,32 @@ } function OnGridDataBound(e) { - var grid = $('#gmc-products-grid'), - options = { - emptytext: '@T("Plugins.Feed.Froogle.EmptyGridCell")', - url: '@Url.Action("GoogleProductEdit", "FeedGoogleMerchantCenter", new { Namespaces = "SmartStore.GoogleMerchantCenter.Controllers", area = GoogleMerchantCenterFeedPlugin.SystemName })' - }, - select2Options = { - placeholder: '@T("Plugins.Feed.Froogle.EmptyGridCell")', - minimumResultsForSearch: 20, - allowClear: true - }; + var grid = $('#gmc-products-grid'); + + var emptyCell = '@T("Plugins.Feed.Froogle.EmptyGridCell")'; + + var options = { + emptytext: emptyCell, + url: '@Url.Action("GoogleProductEdit", "FeedGoogleMerchantCenter", new { Namespaces = "SmartStore.GoogleMerchantCenter.Controllers", area = GoogleMerchantCenterFeedPlugin.SystemName })' + }; + var select2Options = { + placeholder: emptyCell, + minimumResultsForSearch: 20, + allowClear: true + }; + var boolSource = [ + { id: 'true', text: '@T("Admin.Common.Yes")' }, + { id: 'false', text: '@T("Admin.Common.No")' } + ]; - grid.find('a.edit-link-exporting').editable($.extend(options, { + grid.find('a.edit-link-export2').editable($.extend({}, options, { select2: { minimumResultsForSearch: 20 }, - source: [ - { id: 'true', text: '@T("Admin.Common.Yes")' }, - { id: 'false', text: '@T("Admin.Common.No")' } - ] + source: boolSource })); - grid.find('a.edit-link-gender').editable($.extend(options, { + grid.find('a.edit-link-gender').editable($.extend({}, options, { select2: select2Options, source: [ { id: 'male', text: '@T("Plugins.Feed.Froogle.GenderMale")' }, @@ -165,7 +205,7 @@ ] })); - grid.find('a.edit-link-agegroup').editable($.extend(options, { + grid.find('a.edit-link-agegroup').editable($.extend({}, options, { select2: select2Options, source: [ { id: 'adult', text: '@T("Plugins.Feed.Froogle.AgeGroupAdult").Text' }, @@ -173,6 +213,11 @@ ] })); + grid.find('a.edit-link-isadult').editable($.extend({}, options, { + select2: select2Options, + source: boolSource + })); + grid.find('a.edit-link-color').editable(options); grid.find('a.edit-link-size').editable(options); @@ -181,7 +226,46 @@ grid.find('a.edit-link-pattern').editable(options); - grid.find('a.edit-link-taxonomy').editable($.extend(options, { + grid.find('a.edit-link-multipack2').editable($.extend({}, options, { + validate: function(value) { + if (value.length !== 0) { + var regex = /^[0-9]+$/; + if (!regex.test(value)) { + return '@T("Admin.System.Warnings.DigitsOnly")'; + } + } + return null; + } + })); + + grid.find('a.edit-link-isbundle').editable($.extend({}, options, { + select2: select2Options, + source: boolSource + })); + + grid.find('a.edit-link-energyefficiencyclass').editable($.extend({}, options, { + select2: select2Options, + source: [ + @if (Model.EnergyEfficiencyClasses != null && Model.EnergyEfficiencyClasses.Length > 0) + { + foreach (var str in Model.EnergyEfficiencyClasses.Select(x => x.Replace("'", "\""))) + { + { id: '@str', text: '@str' }, + } + } + ] + })); + + grid.find('a.edit-link-customlabel0').editable(options); + grid.find('a.edit-link-customlabel1').editable(options); + grid.find('a.edit-link-customlabel2').editable(options); + grid.find('a.edit-link-customlabel3').editable(options); + grid.find('a.edit-link-customlabel4').editable(options); + + grid.find('a.edit-link-taxonomy').editable($.extend({}, options, { + select2: { + minimumResultsForSearch: 20 + }, source: @Html.Raw(Model.AvailableGoogleCategoriesAsJson) })); } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml index fa3273da09..c20ef9acd7 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml @@ -6,24 +6,24 @@ - } - + if (ViewBag.RefreshPage == true) + { + + } - +
@@ -51,7 +49,7 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) @@ -59,7 +57,7 @@ @Html.SmartLabelFor(model => model.SearchProductTypeId) @@ -71,8 +69,9 @@
@Html.SmartLabelFor(model => model.SearchProductName) @@ -43,7 +41,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
- @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
-

-

+ +

+
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/Tree.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/Tree.cshtml index 68162d1e32..1f3d747077 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/Tree.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/Tree.cshtml @@ -15,26 +15,29 @@ -@using (Html.BeginForm()) +@if (Model.AvailableStores.Count > 1) { - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) -
-   - - -
+ using (Html.BeginForm()) + { + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
+   + + +
+ } }

diff --git a/src/Presentation/SmartStore.Web/Administration/Views/ContentSlider/Index.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/ContentSlider/Index.cshtml index 51251e2491..e847170de8 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/ContentSlider/Index.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/ContentSlider/Index.cshtml @@ -120,24 +120,27 @@ - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) -
-   - - -
+ if (Model.AvailableStores.Count > 1) + { + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
+   + + +
+ }
@@ -164,7 +167,7 @@ .Width(100) .Template(x => @Html.SymbolForBool(x.LimitedToStores)) .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) - .Hidden(Model.StoreCount <= 1) + .Hidden(Model.AvailableStores.Count <= 1) .Centered(); columns.Bound(x => x.Published) .Width(100) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml index 98165844ae..919a1af379 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByNumberOfOrders.cshtml @@ -1,59 +1,59 @@ @model BestCustomersReportModel - @using Telerik.Web.Mvc.UI - - - - - - - - - - - - - - - - - - - - - - - - - -
- @Html.SmartLabelFor(model => model.StartDate) - - @Html.EditorFor(model => model.StartDate) -
- @Html.SmartLabelFor(model => model.EndDate) - - @Html.EditorFor(model => Model.EndDate) -
- @Html.SmartLabelFor(model => model.OrderStatusId) - - @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses) -
- @Html.SmartLabelFor(model => model.PaymentStatusId) - - @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses) -
- @Html.SmartLabelFor(model => model.ShippingStatusId) - - @Html.DropDownList("ShippingStatusId", Model.AvailableShippingStatuses) -
-   - - -
-

-

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.StartDate) + + @Html.EditorFor(model => model.StartDate) +
+ @Html.SmartLabelFor(model => model.EndDate) + + @Html.EditorFor(model => Model.EndDate) +
+ @Html.SmartLabelFor(model => model.OrderStatusId) + + @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses, T("Admin.Common.All")) +
+ @Html.SmartLabelFor(model => model.PaymentStatusId) + + @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses, T("Admin.Common.All")) +
+ @Html.SmartLabelFor(model => model.ShippingStatusId) + + @Html.DropDownList("ShippingStatusId", Model.AvailableShippingStatuses, T("Admin.Common.All")) +
+   + + +
+ +

+
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml index 13c0f4570f..af0e21e9bb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_ReportBestCustomersByOrderTotal.cshtml @@ -4,7 +4,7 @@ @using (Html.BeginForm()) { - +
@@ -34,7 +34,7 @@ @Html.SmartLabelFor(model => model.PaymentStatusId) @@ -42,7 +42,7 @@ @Html.SmartLabelFor(model => model.ShippingStatusId) @@ -54,8 +54,9 @@
@Html.SmartLabelFor(model => model.StartDate) @@ -26,7 +26,7 @@ @Html.SmartLabelFor(model => model.OrderStatusId) - @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses) + @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses, T("Admin.Common.All"))
- @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses) + @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses, T("Admin.Common.All"))
- @Html.DropDownList("ShippingStatusId", Model.AvailableShippingStatuses) + @Html.DropDownList("ShippingStatusId", Model.AvailableShippingStatuses, T("Admin.Common.All"))
-

-

+ +

+
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml index fdcacfa7ea..96727f6ecb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/List.cshtml @@ -1,9 +1,6 @@ @model GiftCardListModel @using Telerik.Web.Mvc.UI @{ - var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - - //page title ViewBag.Title = T("Admin.GiftCards").Text; } @using (Html.BeginForm()) @@ -20,34 +17,36 @@ - - - - - - - - - - - + +
- @Html.SmartLabelFor(model => model.ActivatedId) - - @Html.DropDownListFor(model => model.ActivatedId, Model.ActivatedList) -
- @Html.SmartLabelFor(model => model.CouponCode) - - @Html.EditorFor(model => model.CouponCode) -
+ + + + + + + + + + + + - -
+ @Html.SmartLabelFor(model => model.ActivatedId) + + @Html.DropDownListFor(model => model.ActivatedId, Model.ActivatedList, T("Admin.Common.All")) +
+ @Html.SmartLabelFor(model => model.CouponCode) + + @Html.EditorFor(model => model.CouponCode) +
  - -
-

-

+ +
+ +

+
@@ -75,7 +74,7 @@ .HeaderTemplate("{0}".FormatWith(T("Admin.Common.Edit").Text)) .Filterable(false); }) - .Pageable(settings => settings.PageSize(gridPageSize).Position(GridPagerPosition.Both)) + .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) .DataBinding(dataBinding => dataBinding.Ajax().Select("GiftCardList", "GiftCard", Model)) .PreserveGridState() .EnableCustomBinding(true)) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml index fbd9a6d127..05d898f309 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Log/List.cshtml @@ -21,7 +21,8 @@ - + +
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml index c5f8fccc99..112515ff69 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/List.cshtml @@ -19,23 +19,35 @@ @Html.Widget("admin_button_toolbar_after") -
@Html.SmartLabelFor(model => model.CreatedOnFrom) @@ -51,7 +52,7 @@ @Html.SmartLabelFor(model => model.LogLevelId) - @Html.DropDownList("LogLevelId", Model.AvailableLogLevels) + @Html.DropDownList("LogLevelId", Model.AvailableLogLevels, T("Admin.Common.All"))
- - - - - + +
- @Html.SmartLabelFor(model => model.SearchManufacturerName) - - @Html.EditorFor(model => Model.SearchManufacturerName) -
+ + + + + @if (Model.AvailableStores.Count > 1) + { + + + + + } + - - + +
+ @Html.SmartLabelFor(model => model.SearchManufacturerName) + + @Html.EditorFor(model => Model.SearchManufacturerName) +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownListFor(model => model.SearchStoreId, Model.AvailableStores, T("Admin.Common.All")) +
  - -
+ +

@@ -57,11 +69,9 @@ .HtmlAttributes(new { style = "text-align:center" }) .HeaderHtmlAttributes(new { style = "text-align:center" }); columns.Bound(x => x.Name) - .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })) .ClientTemplate("\"><#= Name #>"); columns.Bound(x => x.Published) .Width(160) - .Template(item => @Html.SymbolForBool(item.Published)) .ClientTemplate(@Html.SymbolForBool("Published")) .Centered(); columns.Bound(x => x.DisplayOrder) @@ -69,8 +79,8 @@ .Centered(); columns.Bound(x => x.LimitedToStores) .Width(160) - .Template(item => @Html.SymbolForBool(item.LimitedToStores)) .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Hidden(Model.AvailableStores.Count <= 1) .Centered(); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) @@ -129,7 +139,8 @@ function onDataBinding(e) { var searchModel = { - SearchManufacturerName: $('#@Html.FieldIdFor(model => model.SearchManufacturerName)').val() + SearchManufacturerName: $('#@Html.FieldIdFor(model => model.SearchManufacturerName)').val(), + SearchStoreId: $('#@Html.FieldIdFor(model => model.SearchStoreId)').val() }; e.data = searchModel; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/ProductAddPopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/ProductAddPopup.cshtml index 15185b0812..d0864667b3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/ProductAddPopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Manufacturer/ProductAddPopup.cshtml @@ -5,7 +5,6 @@ @{ var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - //page title ViewBag.Title = T("Admin.Catalog.Manufacturers.Products.AddNew").Text; } @using Telerik.Web.Mvc.UI; @@ -17,17 +16,16 @@ - if (ViewBag.RefreshPage == true) - { - - } + if (ViewBag.RefreshPage == true) + { + + } - - +
@@ -49,7 +47,7 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) @@ -57,7 +55,7 @@ @Html.SmartLabelFor(model => model.SearchProductTypeId) @@ -69,8 +67,9 @@
@Html.SmartLabelFor(model => model.SearchProductName) @@ -41,7 +39,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
- @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
-

-

+ +

+ - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + }
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml index 29c941466b..ed38d5c49c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/MessageTemplate/List.cshtml @@ -16,26 +16,29 @@ - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) -
-   - - -
+@if (Model.AvailableStores.Count > 1) +{ + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
+   + + +
+}

diff --git a/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml index dfcd240b18..aa82033bf7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/News/List.cshtml @@ -25,26 +25,29 @@ - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) -
-   - - -
+@if (Model.AvailableStores.Count > 1) +{ + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
+   + + +
+}

@@ -81,6 +84,7 @@ columns.Bound(x => x.LimitedToStores) .Template(item => @Html.SymbolForBool(item.LimitedToStores)) .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) + .Hidden(Model.AvailableStores.Count <= 1) .Centered(); columns.Bound(x => x.CreatedOn); }) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml index 5953d03c49..da8b2d4fd5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/NewsLetterSubscription/List.cshtml @@ -24,14 +24,17 @@ @Html.EditorFor(model => Model.SearchEmail)
- @Html.SmartLabelFor(model => model.StoreId) - - @Html.DropDownList("StoreId", Model.AvailableStores) -
+ @Html.SmartLabelFor(model => model.StoreId) + + @Html.DropDownList("StoreId", Model.AvailableStores, T("Admin.Common.All")) +
  @@ -64,6 +67,7 @@ .Centered() .Width(100); columns.Bound(x => x.StoreName) + .Hidden(Model.AvailableStores.Count <= 1) .ReadOnly(); columns.Bound(x => x.CreatedOn) .ReadOnly() diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/AddProductToOrder.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/AddProductToOrder.cshtml index 574db39957..2c99ed1a86 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/AddProductToOrder.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/AddProductToOrder.cshtml @@ -2,7 +2,6 @@ @{ var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - //page title ViewBag.Title = string.Format(T("Admin.Orders.Products.AddNew.Title1").Text, Model.OrderId); } @using Telerik.Web.Mvc.UI; @@ -19,7 +18,7 @@ @T("Admin.Orders.Products.AddNew.Note1") - +
@@ -41,7 +40,7 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) @@ -49,7 +48,7 @@ @Html.SmartLabelFor(model => model.SearchProductTypeId) @@ -63,7 +62,9 @@
@Html.SmartLabelFor(model => model.SearchProductName) @@ -33,7 +32,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
- @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
+

+
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/BestsellersReport.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/BestsellersReport.cshtml index c6fb84b696..e035acfa1e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/BestsellersReport.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/BestsellersReport.cshtml @@ -1,7 +1,6 @@ @model BestsellersReportModel @using Telerik.Web.Mvc.UI @{ - //page title ViewBag.Title = T("Admin.SalesReport.Bestsellers").Text; } @using (Html.BeginForm()) @@ -14,7 +13,8 @@
- + +
@@ -44,7 +44,7 @@ @Html.SmartLabelFor(model => model.PaymentStatusId) @@ -56,8 +56,9 @@
@Html.SmartLabelFor(model => model.StartDate) @@ -36,7 +36,7 @@ @Html.SmartLabelFor(model => model.OrderStatusId) - @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses) + @Html.DropDownList("OrderStatusId", Model.AvailableOrderStatuses, T("Admin.Common.All"))
- @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses) + @Html.DropDownList("PaymentStatusId", Model.AvailablePaymentStatuses, T("Admin.Common.All"))
-

-

+ +

+ - @if (Model.StoreCount > 1) + @if (Model.AvailableStores.Count > 1) {
@@ -106,5 +107,4 @@ } - } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml index 47e12fdb99..af55c9bde6 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Order/List.cshtml @@ -97,7 +97,7 @@ @Html.DropDownList("ShippingStatusIds", Model.AvailableShippingStatuses, null, new { multiple = "multiple" })
@@ -176,7 +176,7 @@ columns.Bound(x => x.CustomerName); columns.Bound(x => x.CustomerEmail); columns.Bound(x => x.StoreName) - .Hidden(Model.StoreCount <= 1); + .Hidden(Model.AvailableStores.Count <= 1); columns.Bound(x => x.CreatedOn); columns.Bound(x => x.OrderTotal) .Width(180) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/AssociatedProductAddPopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/AssociatedProductAddPopup.cshtml index b5fd79b0e4..a46a602417 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/AssociatedProductAddPopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/AssociatedProductAddPopup.cshtml @@ -27,7 +27,7 @@ } - +
@@ -49,23 +49,26 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml index 6eb786b6ba..ebdea889fa 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/BulkEdit.cshtml @@ -3,7 +3,6 @@ @{ var allString = T("Admin.Common.All").Text; - //page title ViewBag.Title = T("Admin.Catalog.BulkEdit").Text; } @using (Html.BeginForm()) @@ -38,7 +37,7 @@ @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, allString) - @if (Model.StoreCount > 1) + @if (Model.AvailableStores.Count > 1) {
@Html.SmartLabelFor(model => model.SearchProductName) @@ -41,7 +41,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
@Html.SmartLabelFor(model => model.SearchProductTypeId) - @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/BundleItemAddPopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/BundleItemAddPopup.cshtml index b31b86f3a2..490d650ce4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/BundleItemAddPopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/BundleItemAddPopup.cshtml @@ -47,7 +47,7 @@ } - +
@@ -69,23 +69,26 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/CrossSellProductAddPopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/CrossSellProductAddPopup.cshtml index ac06795dec..31f0e00244 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/CrossSellProductAddPopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/CrossSellProductAddPopup.cshtml @@ -5,7 +5,6 @@ @{ var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - //page title ViewBag.Title = T("Admin.Catalog.Products.CrossSells.AddNew").Text; } @using Telerik.Web.Mvc.UI; @@ -27,7 +26,7 @@ } -
@Html.SmartLabelFor(model => model.SearchProductName) @@ -61,7 +61,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
@Html.SmartLabelFor(model => model.SearchProductTypeId) - @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
+
@@ -49,23 +48,26 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml index feb7fe9c84..1b18b8a636 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/List.cshtml @@ -44,7 +44,7 @@ @Html.DropDownListFor(model => model.SearchProductTypeId, Model.AvailableProductTypes, allString) - @if (Model.StoreCount > 1) + @if (Model.AvailableStores.Count > 1) { + + + + } + @if (Model.StoreCount > 1) + { + + + + + }
@Html.SmartLabelFor(model => model.SearchProductName) @@ -41,7 +40,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
@Html.SmartLabelFor(model => model.SearchProductTypeId) - @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
@@ -170,7 +170,7 @@ .Sortable(false) .Template(item => @Html.SymbolForBool(item.LimitedToStores)) .ClientTemplate(@Html.SymbolForBool("LimitedToStores")) - .Hidden(Model.StoreCount <= 1) + .Hidden(Model.AvailableStores.Count <= 1) .Centered(); columns.Bound(x => x.CreatedOn); columns.Bound(x => x.UpdatedOn) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/ProductAttributeValueLinkagePopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/ProductAttributeValueLinkagePopup.cshtml index 0ef7426f3c..62cde68c17 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/ProductAttributeValueLinkagePopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/ProductAttributeValueLinkagePopup.cshtml @@ -34,7 +34,7 @@ @Html.HiddenFor(model => model.ProductId) - +
@@ -56,23 +56,26 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Product/RelatedProductAddPopup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Product/RelatedProductAddPopup.cshtml index 7d5db715ca..a9a5b0b701 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Product/RelatedProductAddPopup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Product/RelatedProductAddPopup.cshtml @@ -5,7 +5,6 @@ @{ var gridPageSize = EngineContext.Current.Resolve().GridPageSize; - //page title ViewBag.Title = T("Admin.Catalog.Products.RelatedProducts.AddNew").Text; } @using Telerik.Web.Mvc.UI; @@ -26,8 +25,7 @@ } - -
@Html.SmartLabelFor(model => model.SearchProductName) @@ -48,7 +48,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
@Html.SmartLabelFor(model => model.SearchProductTypeId) - @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
+
@@ -49,23 +47,26 @@ @Html.SmartLabelFor(model => model.SearchManufacturerId) - - - - + @if (Model.AvailableStores.Count > 1) + { + + + + + } @@ -77,6 +78,7 @@
@Html.SmartLabelFor(model => model.SearchProductName) @@ -41,7 +39,7 @@ @Html.SmartLabelFor(model => model.SearchCategoryId) - @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All").Text) + @Html.DropDownList("SearchCategoryId", Model.AvailableCategories, T("Admin.Common.All"))
- @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All").Text) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) + @Html.DropDownList("SearchManufacturerId", Model.AvailableManufacturers, T("Admin.Common.All"))
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
@Html.SmartLabelFor(model => model.SearchProductTypeId) - @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes) + @Html.DropDownList("SearchProductTypeId", Model.AvailableProductTypes, T("Admin.Common.All"))
+

diff --git a/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml index e8f2359885..c9a62c9b5c 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/ReturnRequest/List.cshtml @@ -13,83 +13,88 @@ -
- - - - - - - - - - - - - +
- @Html.SmartLabelFor(model => model.SearchId) - - @Html.EditorFor(model => Model.SearchId) -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownListFor(model => model.SearchStoreId, Model.AvailableStores) -
- @Html.SmartLabelFor(model => model.SearchReturnRequestStatusId) - - @Html.DropDownListFor(model => model.SearchReturnRequestStatusId, Model.AvailableReturnRequestStatus) -
+ + + + + @if (Model.AvailableStores.Count > 1) + { + + + + + } + + + + + - - + +
+ @Html.SmartLabelFor(model => model.SearchId) + + @Html.EditorFor(model => Model.SearchId) +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownListFor(model => model.SearchStoreId, Model.AvailableStores, T("Admin.Common.All")) +
+ @Html.SmartLabelFor(model => model.SearchReturnRequestStatusId) + + @Html.DropDownListFor(model => model.SearchReturnRequestStatusId, Model.AvailableReturnRequestStatus, T("Admin.Common.All")) +
  - -
+ +
+

@(Html.Telerik().Grid() - .Name("returnrequests-grid") - .Columns(columns => - { + .Name("returnrequests-grid") + .Columns(columns => + { columns.Bound(x => x.Id) .Centered() .Width(80); columns.Bound(x => x.ProductName) .Template(x => @Html.LabeledProductName(x.ProductId, x.ProductName, x.ProductTypeName, x.ProductTypeLabelHint)) .ClientTemplate(@Html.LabeledProductName("ProductId", "ProductName")); - columns.Bound(x => x.Quantity) + columns.Bound(x => x.Quantity) .Width(80) - .Centered(); - columns.Bound(x => x.CustomerId) - .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "Edit", "Customer", new { id = x.CustomerId }, new { })) + .Centered(); + columns.Bound(x => x.CustomerId) + .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "Edit", "Customer", new { id = x.CustomerId }, new { })) .ClientTemplate("\"><#= CustomerFullName #>"); - columns.Bound(x => x.OrderId) + columns.Bound(x => x.OrderId) .Width(80) - .Centered() - .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "Edit", "Order", new { id = x.OrderId }, new { })) + .Centered() + .Template(x => Html.ActionLink(T("Admin.Common.View").Text, "Edit", "Order", new { id = x.OrderId }, new { })) .ClientTemplate("\"><#= OrderId #>"); columns.Bound(x => x.ReturnRequestStatusStr) - .Width(180); - columns.Bound(x => x.CreatedOn) .Width(180); - columns.Bound(x => x.StoreName); - columns.Bound(x => x.Id) + columns.Bound(x => x.CreatedOn) + .Width(180); + columns.Bound(x => x.StoreName) + .Hidden(Model.AvailableStores.Count <= 1); + columns.Bound(x => x.Id) .Width(100) - .Centered() - .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id })) - .ClientTemplate("\">" + T("Admin.Common.Edit").Text + "") - .Title(T("Admin.Common.Edit").Text); - }) + .Centered() + .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id })) + .ClientTemplate("\">" + T("Admin.Common.Edit").Text + "") + .Title(T("Admin.Common.Edit").Text); + }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "ReturnRequest")) + .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "ReturnRequest")) .ClientEvents(events => events.OnDataBinding("returnRequestGrid_onDataBinding")) .PreserveGridState() - .EnableCustomBinding(true)) + .EnableCustomBinding(true))
diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml index 08de2f50ab..18fda90102 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Topic/List.cshtml @@ -16,26 +16,29 @@ - - - - - - - - - -
- @Html.SmartLabelFor(model => model.SearchStoreId) - - @Html.DropDownList("SearchStoreId", Model.AvailableStores) -
-   - - -
+@if (Model.AvailableStores.Count > 1) +{ + + + + + + + + + +
+ @Html.SmartLabelFor(model => model.SearchStoreId) + + @Html.DropDownList("SearchStoreId", Model.AvailableStores, T("Admin.Common.All")) +
+   + + +
+}

From 49da71aa6d88631916c7e9bf681732af73ff0f24 Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Thu, 7 Jan 2016 18:56:27 +0100 Subject: [PATCH 244/732] Updated Google Analytics admin instructions Bulk update of string resources may fail because of outdated EfRepository.AutoCommitEnabledInternal state --- .../Localization/LocalizationService.cs | 9 +-------- .../Importer/NewsLetterSubscriptionImporter.cs | 4 +--- .../Localization/resources.de-de.xml | 11 ++++++----- .../Localization/resources.en-us.xml | 12 ++++++------ 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs index 31f83eb7dc..8610072e92 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs @@ -437,7 +437,6 @@ public virtual void ImportResourcesFromXml( foreach (var xel in nodes.Cast()) { - string name = xel.GetAttribute("Name").TrimSafe(); string value = ""; var valueNode = xel.SelectSingleNode("Value"); @@ -488,13 +487,7 @@ public virtual void ImportResourcesFromXml( _lsrRepository.InsertRange(toAdd, 500); toAdd.Clear(); - _lsrRepository.AutoCommitEnabled = null; - toUpdate.Each(x => - { - _lsrRepository.Update(x); - }); - - _lsrRepository.Context.SaveChanges(); + _lsrRepository.UpdateRange(toUpdate); toUpdate.Clear(); //clear cache diff --git a/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs b/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs index 5bf83c4755..17f3c10ed0 100644 --- a/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs +++ b/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs @@ -104,9 +104,7 @@ public void Execute(IImportExecuteContext context) toAdd.Clear(); // update modified subscribers - _subscriptionRepository.AutoCommitEnabled = null; - toUpdate.Each(x => _subscriptionRepository.Update(x)); - _subscriptionRepository.Context.SaveChanges(); + _subscriptionRepository.UpdateRange(toUpdate); toUpdate.Clear(); } catch (Exception exception) diff --git a/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.de-de.xml b/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.de-de.xml index 627698df96..d7f6055f92 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.de-de.xml @@ -5,13 +5,14 @@ Google Analytics ist ein kostenloser Statistikdienst von Google. Der Dienst erfasst Statistiken über Besucher und Ecommerce-Konversion auf Ihrer Webseite

+

Google Analytics ist ein kostenloser Statistikdienst von Google. Der Dienst erfasst Statistiken über Besucher und Ecommerce-Konversion auf Ihrer Webseite.

Führen Sie die folgenden Schritte aus um Google Analytics auf Ihrer Webseite einzubinden:

    -
  • erstellen Sie hier einen "Google Analytics"-Account und folgen Sie dem Wizard um Ihre Webseite zuzufügen
  • -
  • Kopieren Sie die "Google Analytics"-ID in das entspechende Feld in folgendem Formular
  • -
  • Kopieren Sie den Tracking-Code in die "Tracking-Code"-Box in folgendem Formular
  • -
  • KLicken Sie den "Speichern"-Button und Google Analytics wird in Ihre Webseite integriert
  • +
  • Erstellen Sie hier einen Google-Analytics-Account und folgen Sie dem Wizard um Ihre Webseite zuzufügen.
  • +
  • Kopieren Sie die Google-Analytics-ID in das entspechende Feld in folgendem Formular.
  • +
  • Kopieren Sie den Tracking-Code in die Tracking-Code-Box in folgendem Formular.
  • +
  • Klicken Sie den Speichern-Button.
  • +
  • Aktivieren Sie das Google Analytics Widget unter CMS > Widgets und Google Analytics wird in Ihre Webseite integriert.
]]>
diff --git a/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.en-us.xml b/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.en-us.xml index 2ac97f8895..d54877d501 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.GoogleAnalytics/Localization/resources.en-us.xml @@ -5,14 +5,14 @@ Google Analytics is a free website stats tool from Google. It keeps track of statistics - about the visitors and ecommerce conversion on your website.

+

Google Analytics is a free website statistics tool from Google. It keeps track of statistics about the visitors and ecommerce conversion on your website.

Follow the next steps to enable Google Analytics integration:

    -
  • Create a Google Analytics account and follow the wizard to add your website
  • -
  • Copy the Google Analytics ID into the 'ID' box below
  • -
  • Copy the tracking code from Google Analytics into the 'Tracking Code' box below
  • -
  • Click the 'Save' button below and Google Analytics will be integrated into your store
  • +
  • Create a Google Analytics account and follow the wizard to add your website.
  • +
  • Copy the Google Analytics ID into the ID field below.
  • +
  • Copy the tracking code from Google Analytics into the Tracking Code field below.
  • +
  • Click the Save button below.
  • +
  • Activate the Google Analytics widget under CMS > Widgets to integrate Google Analytics into your store.
]]>
From df31f4f375026c4129096ca0cc0cecdfbb7a1863 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Thu, 7 Jan 2016 20:22:45 +0100 Subject: [PATCH 245/732] TaskScheduler: logging the sweep call for debugging puposes (temp only) --- .../SmartStore.Core/Extensions/ConversionExtensions.cs | 6 +++++- src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs | 9 ++++++++- .../Controllers/TaskSchedulerController.cs | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs index 64297abe9d..387bc9a614 100644 --- a/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs @@ -239,7 +239,11 @@ public static string AsString(this Stream stream, Encoding encoding) // convert stream to string string result; - stream.Position = 0; + + if (stream.CanSeek) + { + stream.Position = 0; + } using (StreamReader sr = new StreamReader(stream, encoding)) { diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs index 22cecb7046..50b4df021b 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs @@ -173,8 +173,15 @@ protected internal virtual void CallEndpoint(string url) } else { + var response = t.Result; + + using (var logger = new TraceLogger()) + { + logger.Debug("TaskScheduler Sweep called successfully: {0}".FormatCurrent(response.GetResponseStream().AsString())); + } + _errCount = 0; - t.Result.Dispose(); + response.Dispose(); } }); } diff --git a/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs b/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs index 9b7b6763c2..860da5fa85 100644 --- a/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/TaskSchedulerController.cs @@ -65,7 +65,7 @@ public ActionResult Sweep() } } - return Content("{0} tasks executed".FormatInvariant(count)); + return Content("{0} of {1} pending tasks executed".FormatInvariant(count, pendingTasks.Count)); } [HttpPost] From d401828451b56e35817f1b01770bf533cf9d0e1c Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Fri, 8 Jan 2016 14:30:31 +0100 Subject: [PATCH 246/732] Performance: eliminated sending of the whole product model to data grid via json response in several locations --- .../Controllers/CategoryController.cs | 9 ++- .../Controllers/ManufacturerController.cs | 9 ++- .../Controllers/ProductController.cs | 64 +++++++++++++++++-- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs index a1b8d695d6..245cce40b5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CategoryController.cs @@ -911,7 +911,14 @@ public ActionResult ProductAddPopupList(GridCommand command, CategoryModel.AddCa gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs index 2e7460bff4..51f74261f5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ManufacturerController.cs @@ -596,7 +596,14 @@ public ActionResult ProductAddPopupList(GridCommand command, ManufacturerModel.A gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index 09139d349c..bb8c302ba9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -943,8 +943,18 @@ public ActionResult ProductList(GridCommand command, ProductListModel model) gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); - productModel.FullDescription = ""; // Perf + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id, + StockQuantity = x.StockQuantity, + Price = x.Price, + LimitedToStores = x.LimitedToStores + }; + PrepareProductPictureThumbnailModel(productModel, x); productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); @@ -1654,7 +1664,15 @@ public ActionResult RelatedProductAddPopupList(GridCommand command, ProductModel gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; @@ -1876,7 +1894,15 @@ public ActionResult CrossSellProductAddPopupList(GridCommand command, ProductMod gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; @@ -2095,7 +2121,15 @@ public ActionResult AssociatedProductAddPopupList(GridCommand command, ProductMo gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; @@ -2411,7 +2445,15 @@ public ActionResult BundleItemAddPopupList(GridCommand command, ProductModel.Add gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); productModel.ProductSelectCheckboxClass = (!x.CanBeBundleItem() ? " hide" : ""); @@ -3611,7 +3653,15 @@ public ActionResult ProductAttributeValueLinkagePopupList(GridCommand command, P gridModel.Data = products.Select(x => { - var productModel = x.ToModel(); + var productModel = new ProductModel + { + Sku = x.Sku, + Published = x.Published, + ProductTypeLabelHint = x.ProductTypeLabelHint, + Name = x.Name, + Id = x.Id + }; + productModel.ProductTypeName = x.GetProductTypeLabel(_localizationService); return productModel; From 6530f97a5ab382a831c056d9f02ed8176bfd9429 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Fri, 8 Jan 2016 19:38:35 +0100 Subject: [PATCH 247/732] Removed TaskScheduler sweep logging --- .../SmartStore.Services/Tasks/TaskScheduler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs index 50b4df021b..8cb0fbc0d7 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs @@ -173,14 +173,14 @@ protected internal virtual void CallEndpoint(string url) } else { + _errCount = 0; var response = t.Result; - using (var logger = new TraceLogger()) - { - logger.Debug("TaskScheduler Sweep called successfully: {0}".FormatCurrent(response.GetResponseStream().AsString())); - } + //using (var logger = new TraceLogger()) + //{ + // logger.Debug("TaskScheduler Sweep called successfully: {0}".FormatCurrent(response.GetResponseStream().AsString())); + //} - _errCount = 0; response.Dispose(); } }); From 94cc64cd43133ea5f0a0d36b25d2d53181369d7b Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sat, 9 Jan 2016 12:55:50 +0100 Subject: [PATCH 248/732] Resolves #760 Setting to set a customer role for new registered users --- changelog.md | 1 + .../Domain/Customers/CustomerSettings.cs | 9 +- .../201512151526290_ImportFramework.cs | 22 +++ .../Customers/CustomerRegistrationService.cs | 148 +++++++++--------- .../Controllers/SettingController.cs | 26 +-- .../Settings/CustomerUserSettingsModel.cs | 10 +- .../Views/Setting/CustomerUser.cshtml | 11 +- .../CustomerRegistrationServiceTests.cs | 6 +- 8 files changed, 141 insertions(+), 92 deletions(-) diff --git a/changelog.md b/changelog.md index f6fa16e556..6a82d8e636 100644 --- a/changelog.md +++ b/changelog.md @@ -33,6 +33,7 @@ * #841 Activity log for deleting an order * More settings to control creation of SEO names * GMC feed: Supporting fields multipack, bundle, adult, energy efficiency class and custom label (0 to 4) +* #760 Setting to set a customer role for new registered users ### Improvements * (Perf) Implemented static caches for URL aliases and localized properties. Increases app startup and request speed by up to 30%. diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs index 83380a63a3..d0dbf678e5 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs @@ -151,7 +151,6 @@ public CustomerSettings() /// public bool StoreLastVisitedPage { get; set; } - #region Form fields /// @@ -246,8 +245,12 @@ public CustomerSettings() #endregion - // codehint: sm-add (no ui, only db edit) public string PrefillLoginUsername { get; set; } public string PrefillLoginPwd { get; set; } - } + + /// + /// Identifier of a customer role that new registered customers will be assigned to + /// + public int RegisterCustomerRoleId { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 7f2dec3086..2699566e17 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -316,6 +316,28 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.System.Warnings.DigitsOnly", "Please enter digits only.", "Bitte nur Ziffern eingeben."); + + builder.AddOrUpdate("Account.Register.Errors.CannotRegisterSearchEngine", + "A search engine can't be registered.", + "Eine Suchmaschine kann nicht registriert werden."); + + builder.AddOrUpdate("Account.Register.Errors.CannotRegisterTaskAccount", + "A background task account can't be registered.", + "Das Konto einer geplanten Aufgabe kann nicht registriert werden."); + + builder.AddOrUpdate("Account.Register.Errors.AlreadyRegistered", + "The customer is already registered.", + "Der Kunde ist bereits registriert."); + + builder.AddOrUpdate("Admin.Customers.CustomerRoles.CannotFoundRole", + "The customer role \"{0}\" cannot be found.", + "Die Kundengruppe \"{0}\" wurde nicht gefunden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.RegisterCustomerRole", + "Customer role at registrations", + "Kundengruppe bei Registrierungen", + "Specifies a customer role that will be assigned to newly registered customers.", + "Legt eine Kundengruppe fest, die neu registrierten Kunden zugeordnet wird."); } } } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs index 1615a2ce96..49fb4587aa 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs @@ -3,23 +3,22 @@ using SmartStore.Core; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Events; -using SmartStore.Services.Localization; +using SmartStore.Core.Localization; using SmartStore.Services.Messages; using SmartStore.Services.Security; namespace SmartStore.Services.Customers { - /// - /// Customer registration service - /// - public partial class CustomerRegistrationService : ICustomerRegistrationService + /// + /// Customer registration service + /// + public partial class CustomerRegistrationService : ICustomerRegistrationService { #region Fields private readonly ICustomerService _customerService; private readonly IEncryptionService _encryptionService; private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; - private readonly ILocalizationService _localizationService; private readonly RewardPointsSettings _rewardPointsSettings; private readonly CustomerSettings _customerSettings; private readonly IStoreContext _storeContext; @@ -35,31 +34,33 @@ public partial class CustomerRegistrationService : ICustomerRegistrationService public CustomerRegistrationService(ICustomerService customerService, IEncryptionService encryptionService, INewsLetterSubscriptionService newsLetterSubscriptionService, - ILocalizationService localizationService, RewardPointsSettings rewardPointsSettings, CustomerSettings customerSettings, IStoreContext storeContext, IEventPublisher eventPublisher) { this._customerService = customerService; this._encryptionService = encryptionService; this._newsLetterSubscriptionService = newsLetterSubscriptionService; - this._localizationService = localizationService; this._rewardPointsSettings = rewardPointsSettings; this._customerSettings = customerSettings; this._storeContext = storeContext; this._eventPublisher = eventPublisher; - } - #endregion + T = NullLocalizer.Instance; + } - #region Methods + #endregion - /// - /// Validate customer - /// - /// Username or email - /// Password - /// Result - public virtual bool ValidateCustomer(string usernameOrEmail, string password) + public Localizer T { get; set; } + + #region Methods + + /// + /// Validate customer + /// + /// Username or email + /// Password + /// Result + public virtual bool ValidateCustomer(string usernameOrEmail, string password) { Customer customer = null; if (_customerSettings.UsernamesEnabled) @@ -112,48 +113,46 @@ public virtual bool ValidateCustomer(string usernameOrEmail, string password) /// Result public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationRequest request) { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.Customer == null) - throw new ArgumentException("Can't load current customer"); + Guard.ArgumentNotNull(() => request); + Guard.ArgumentNotNull(() => request.Customer); var result = new CustomerRegistrationResult(); + if (request.Customer.IsSearchEngineAccount()) { - result.AddError("Search engine can't be registered"); + result.AddError(T("Account.Register.Errors.CannotRegisterSearchEngine")); return result; } if (request.Customer.IsBackgroundTaskAccount()) { - result.AddError("Background task account can't be registered"); + result.AddError(T("Account.Register.Errors.CannotRegisterTaskAccount")); return result; } if (request.Customer.IsRegistered()) { - result.AddError("Current customer is already registered"); + result.AddError(T("Account.Register.Errors.AlreadyRegistered")); return result; } if (String.IsNullOrEmpty(request.Email)) { - result.AddError(_localizationService.GetResource("Account.Register.Errors.EmailIsNotProvided")); + result.AddError(T("Account.Register.Errors.EmailIsNotProvided")); return result; } if (!request.Email.IsEmail()) { - result.AddError(_localizationService.GetResource("Common.WrongEmail")); + result.AddError(T("Common.WrongEmail")); return result; } if (String.IsNullOrWhiteSpace(request.Password)) { - result.AddError(_localizationService.GetResource("Account.Register.Errors.PasswordIsNotProvided")); + result.AddError(T("Account.Register.Errors.PasswordIsNotProvided")); return result; } if (_customerSettings.UsernamesEnabled) { if (String.IsNullOrEmpty(request.Username)) { - result.AddError(_localizationService.GetResource("Account.Register.Errors.UsernameIsNotProvided")); + result.AddError(T("Account.Register.Errors.UsernameIsNotProvided")); return result; } } @@ -161,14 +160,14 @@ public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationR //validate unique user if (_customerService.GetCustomerByEmail(request.Email) != null) { - result.AddError(_localizationService.GetResource("Account.Register.Errors.EmailAlreadyExists")); + result.AddError(T("Account.Register.Errors.EmailAlreadyExists")); return result; } if (_customerSettings.UsernamesEnabled) { if (_customerService.GetCustomerByUsername(request.Username) != null) { - result.AddError(_localizationService.GetResource("Account.Register.Errors.UsernameAlreadyExists")); + result.AddError(T("Account.Register.Errors.UsernameAlreadyExists")); return result; } } @@ -180,45 +179,52 @@ public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationR switch (request.PasswordFormat) { - case PasswordFormat.Clear: - { - request.Customer.Password = request.Password; - } - break; - case PasswordFormat.Encrypted: - { - request.Customer.Password = _encryptionService.EncryptText(request.Password); - } - break; - case PasswordFormat.Hashed: - { - string saltKey = _encryptionService.CreateSaltKey(5); - request.Customer.PasswordSalt = saltKey; - request.Customer.Password = _encryptionService.CreatePasswordHash(request.Password, saltKey, _customerSettings.HashedPasswordFormat); - } - break; - default: - break; + case PasswordFormat.Clear: + request.Customer.Password = request.Password; + break; + case PasswordFormat.Encrypted: + request.Customer.Password = _encryptionService.EncryptText(request.Password); + break; + case PasswordFormat.Hashed: + string saltKey = _encryptionService.CreateSaltKey(5); + request.Customer.PasswordSalt = saltKey; + request.Customer.Password = _encryptionService.CreatePasswordHash(request.Password, saltKey, _customerSettings.HashedPasswordFormat); + break; } request.Customer.Active = request.IsApproved; - - //add to 'Registered' role - var registeredRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); - if (registeredRole == null) - throw new SmartException("'Registered' role could not be loaded"); + + if (_customerSettings.RegisterCustomerRoleId != 0) + { + var customerRole = _customerService.GetCustomerRoleById(_customerSettings.RegisterCustomerRoleId); + request.Customer.CustomerRoles.Add(customerRole); + } + + //add to 'Registered' role + var registeredRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); + if (registeredRole == null) + { + throw new SmartException(T("Admin.Customers.CustomerRoles.CannotFoundRole", "Registered")); + } + request.Customer.CustomerRoles.Add(registeredRole); + //remove from 'Guests' role var guestRole = request.Customer.CustomerRoles.FirstOrDefault(cr => cr.SystemName == SystemCustomerRoleNames.Guests); - if (guestRole != null) - request.Customer.CustomerRoles.Remove(guestRole); + if (guestRole != null) + { + request.Customer.CustomerRoles.Remove(guestRole); + } - //Add reward points for customer registration (if enabled) - if (_rewardPointsSettings.Enabled && _rewardPointsSettings.PointsForRegistration > 0) - request.Customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForRegistration, _localizationService.GetResource("RewardPoints.Message.RegisteredAsCustomer")); + //Add reward points for customer registration (if enabled) + if (_rewardPointsSettings.Enabled && _rewardPointsSettings.PointsForRegistration > 0) + { + request.Customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForRegistration, T("RewardPoints.Message.RegisteredAsCustomer")); + } _customerService.UpdateCustomer(request.Customer); - _eventPublisher.Publish(new CustomerRegisteredEvent{ Customer = request.Customer }); + _eventPublisher.Publish(new CustomerRegisteredEvent { Customer = request.Customer }); + return result; } @@ -235,19 +241,19 @@ public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request var result = new PasswordChangeResult(); if (String.IsNullOrWhiteSpace(request.Email)) { - result.AddError(_localizationService.GetResource("Account.ChangePassword.Errors.EmailIsNotProvided")); + result.AddError(T("Account.ChangePassword.Errors.EmailIsNotProvided")); return result; } if (String.IsNullOrWhiteSpace(request.NewPassword)) { - result.AddError(_localizationService.GetResource("Account.ChangePassword.Errors.PasswordIsNotProvided")); + result.AddError(T("Account.ChangePassword.Errors.PasswordIsNotProvided")); return result; } var customer = _customerService.GetCustomerByEmail(request.Email); if (customer == null) { - result.AddError(_localizationService.GetResource("Account.ChangePassword.Errors.EmailNotFound")); + result.AddError(T("Account.ChangePassword.Errors.EmailNotFound")); return result; } @@ -272,7 +278,7 @@ public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request bool oldPasswordIsValid = oldPwd == customer.Password; if (!oldPasswordIsValid) - result.AddError(_localizationService.GetResource("Account.ChangePassword.Errors.OldPasswordDoesntMatch")); + result.AddError(T("Account.ChangePassword.Errors.OldPasswordDoesntMatch")); if (oldPasswordIsValid) requestIsValid = true; @@ -327,14 +333,14 @@ public virtual void SetEmail(Customer customer, string newEmail) string oldEmail = customer.Email; if (!newEmail.IsEmail()) - throw new SmartException(_localizationService.GetResource("Account.EmailUsernameErrors.NewEmailIsNotValid")); + throw new SmartException(T("Account.EmailUsernameErrors.NewEmailIsNotValid")); if (newEmail.Length > 100) - throw new SmartException(_localizationService.GetResource("Account.EmailUsernameErrors.EmailTooLong")); + throw new SmartException(T("Account.EmailUsernameErrors.EmailTooLong")); var customer2 = _customerService.GetCustomerByEmail(newEmail); if (customer2 != null && customer.Id != customer2.Id) - throw new SmartException(_localizationService.GetResource("Account.EmailUsernameErrors.EmailAlreadyExists")); + throw new SmartException(T("Account.EmailUsernameErrors.EmailAlreadyExists")); customer.Email = newEmail; _customerService.UpdateCustomer(customer); @@ -370,11 +376,11 @@ public virtual void SetUsername(Customer customer, string newUsername) newUsername = newUsername.Trim(); if (newUsername.Length > 100) - throw new SmartException(_localizationService.GetResource("Account.EmailUsernameErrors.UsernameTooLong")); + throw new SmartException(T("Account.EmailUsernameErrors.UsernameTooLong")); var user2 = _customerService.GetCustomerByUsername(newUsername); if (user2 != null && customer.Id != user2.Id) - throw new SmartException(_localizationService.GetResource("Account.EmailUsernameErrors.UsernameAlreadyExists")); + throw new SmartException(T("Account.EmailUsernameErrors.UsernameAlreadyExists")); customer.Username = newUsername; _customerService.UpdateCustomer(customer); diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs index 85ea07067b..47df2024dc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs @@ -902,13 +902,15 @@ public ActionResult CustomerUser() var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); StoreDependingSettings.CreateViewDataObject(storeScope); + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + var customerSettings = _services.Settings.LoadSetting(storeScope); var addressSettings = _services.Settings.LoadSetting(storeScope); var dateTimeSettings = _services.Settings.LoadSetting(storeScope); var externalAuthenticationSettings = _services.Settings.LoadSetting(storeScope); - //merge settings - var model = new CustomerUserSettingsModel(); + //merge settings + var model = new CustomerUserSettingsModel(); model.CustomerSettings = customerSettings.ToModel(); StoreDependingSettings.GetOverrideKeys(customerSettings, model.CustomerSettings, storeScope, _services.Settings, false); @@ -919,14 +921,15 @@ public ActionResult CustomerUser() model.DateTimeSettings.AllowCustomersToSetTimeZone = dateTimeSettings.AllowCustomersToSetTimeZone; model.DateTimeSettings.DefaultStoreTimeZoneId = _dateTimeHelper.DefaultStoreTimeZone.Id; + foreach (TimeZoneInfo timeZone in _dateTimeHelper.GetSystemTimeZones()) { - model.DateTimeSettings.AvailableTimeZones.Add(new SelectListItem() - { - Text = timeZone.DisplayName, - Value = timeZone.Id, - Selected = timeZone.Id.Equals(_dateTimeHelper.DefaultStoreTimeZone.Id, StringComparison.InvariantCultureIgnoreCase) - }); + model.DateTimeSettings.AvailableTimeZones.Add(new SelectListItem + { + Text = timeZone.DisplayName, + Value = timeZone.Id, + Selected = timeZone.Id.Equals(_dateTimeHelper.DefaultStoreTimeZone.Id, StringComparison.InvariantCultureIgnoreCase) + }); } StoreDependingSettings.GetOverrideKeys(dateTimeSettings, model.DateTimeSettings, storeScope, _services.Settings, false); @@ -938,7 +941,12 @@ public ActionResult CustomerUser() model.CustomerSettings.AvailableCustomerNumberMethods = customerSettings.CustomerNumberMethod.ToSelectList(); model.CustomerSettings.AvailableCustomerNumberVisibilities = customerSettings.CustomerNumberVisibility.ToSelectList(); - return View(model); + model.CustomerSettings.AvailableRegisterCustomerRoles = allCustomerRoles + .Where(x => x.SystemName != SystemCustomerRoleNames.Registered && x.SystemName != SystemCustomerRoleNames.Guests) + .Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() }) + .ToList(); + + return View(model); } [HttpPost] diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CustomerUserSettingsModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CustomerUserSettingsModel.cs index 1ad5872150..2e076d6471 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CustomerUserSettingsModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CustomerUserSettingsModel.cs @@ -29,8 +29,9 @@ public partial class CustomerSettingsModel [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.DefaultSortOrderMode")] public CustomerNumberMethod CustomerNumberMethod { get; set; } public SelectList AvailableCustomerNumberMethods { get; set; } + public IList AvailableRegisterCustomerRoles { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.DefaultSortOrderMode")] + [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.DefaultSortOrderMode")] public CustomerNumberVisibility CustomerNumberVisibility { get; set; } public SelectList AvailableCustomerNumberVisibilities { get; set; } @@ -43,7 +44,10 @@ public partial class CustomerSettingsModel [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.UserRegistrationType")] public int UserRegistrationType { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.AllowCustomersToUploadAvatars")] + [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.RegisterCustomerRole")] + public int RegisterCustomerRoleId { get; set; } + + [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.AllowCustomersToUploadAvatars")] public bool AllowCustomersToUploadAvatars { get; set; } [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.DefaultAvatarEnabled")] @@ -131,7 +135,7 @@ public partial class CustomerSettingsModel public bool FaxEnabled { get; set; } [SmartResourceDisplayName("Admin.Configuration.Settings.CustomerUser.FaxRequired")] public bool FaxRequired { get; set; } - } + } public partial class AddressSettingsModel { diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Setting/CustomerUser.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Setting/CustomerUser.cshtml index b22985819c..d3c493aad1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Setting/CustomerUser.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Setting/CustomerUser.cshtml @@ -3,7 +3,6 @@ @using SmartStore.Core.Domain.Customers; @using SmartStore.Core.Domain.Security; @{ - //page title ViewBag.Title = T("Admin.Configuration.Settings.CustomerUser").Text; } @using (Html.BeginForm()) @@ -128,6 +127,16 @@ @Html.ValidationMessageFor(model => model.CustomerSettings.UserRegistrationType)
+ @Html.SmartLabelFor(model => model.CustomerSettings.RegisterCustomerRoleId) + + @Html.SettingOverrideCheckbox(model => Model.CustomerSettings.RegisterCustomerRoleId) + @Html.DropDownListFor(model => model.CustomerSettings.RegisterCustomerRoleId, Model.CustomerSettings.AvailableRegisterCustomerRoles, T("Common.Unspecified")) + @Html.ValidationMessageFor(model => model.CustomerSettings.RegisterCustomerRoleId) +
@Html.SmartLabelFor(model => model.CustomerSettings.AllowCustomersToUploadAvatars) diff --git a/src/Tests/SmartStore.Services.Tests/Customers/CustomerRegistrationServiceTests.cs b/src/Tests/SmartStore.Services.Tests/Customers/CustomerRegistrationServiceTests.cs index b356946e7c..990c27b02f 100644 --- a/src/Tests/SmartStore.Services.Tests/Customers/CustomerRegistrationServiceTests.cs +++ b/src/Tests/SmartStore.Services.Tests/Customers/CustomerRegistrationServiceTests.cs @@ -11,7 +11,6 @@ using SmartStore.Core.Events; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.Localization; using SmartStore.Services.Messages; using SmartStore.Services.Security; using SmartStore.Tests; @@ -29,7 +28,6 @@ public class CustomerRegistrationServiceTests : ServiceTest IEncryptionService _encryptionService; ICustomerService _customerService; ICustomerRegistrationService _customerRegistrationService; - ILocalizationService _localizationService; CustomerSettings _customerSettings; INewsLetterSubscriptionService _newsLetterSubscriptionService; IEventPublisher _eventPublisher; @@ -117,15 +115,13 @@ public class CustomerRegistrationServiceTests : ServiceTest _genericAttributeService = MockRepository.GenerateMock(); _newsLetterSubscriptionService = MockRepository.GenerateMock(); - _localizationService = MockRepository.GenerateMock(); _storeContext = MockRepository.GenerateMock(); _customerService = new CustomerService(new NullCache(), _customerRepo, _customerRoleRepo, _genericAttributeRepo, _rewardPointsHistoryRepo, _genericAttributeService, _eventPublisher, _rewardPointsSettings); _customerRegistrationService = new CustomerRegistrationService(_customerService, - _encryptionService, _newsLetterSubscriptionService, _localizationService, - _rewardPointsSettings, _customerSettings, _storeContext, _eventPublisher); + _encryptionService, _newsLetterSubscriptionService, _rewardPointsSettings, _customerSettings, _storeContext, _eventPublisher); } //[Test] From 019a4d24f9f72f7f45cfb798b19b102e30017efe Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Sun, 10 Jan 2016 11:49:19 +0100 Subject: [PATCH 249/732] Resolves #800 Multi-store: Option to display all orders of all stores for customer in frontend --- changelog.md | 1 + .../Domain/Orders/OrderSettings.cs | 6 +++- .../201512151526290_ImportFramework.cs | 9 ++++++ .../Controllers/SettingController.cs | 5 ++- .../Infrastructure/AutoMapperStartupTask.cs | 1 + .../Models/Settings/OrderSettingsModel.cs | 4 +++ .../Administration/Views/Setting/Order.cshtml | 12 +++++++ .../Controllers/CustomerController.cs | 31 ++++++++++++------- 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 6a82d8e636..519c0f8906 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,7 @@ * More settings to control creation of SEO names * GMC feed: Supporting fields multipack, bundle, adult, energy efficiency class and custom label (0 to 4) * #760 Setting to set a customer role for new registered users +* #800 Multi-store: Option to display all orders of all stores for customer in frontend ### Improvements * (Perf) Implemented static caches for URL aliases and localized properties. Increases app startup and request speed by up to 30%. diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs b/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs index 7a55c36f70..996271de79 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs @@ -83,5 +83,9 @@ public OrderSettings() /// public int MinimumOrderPlacementInterval { get; set; } - } + /// + /// Gets or sets a value indicating whether to display all orders of all stores to a customer + /// + public bool DisplayOrdersOfAllStores { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 2699566e17..767af5b90c 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -338,6 +338,15 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Kundengruppe bei Registrierungen", "Specifies a customer role that will be assigned to newly registered customers.", "Legt eine Kundengruppe fest, die neu registrierten Kunden zugeordnet wird."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.DisplayOrdersOfAllStores", + "Display orders of all stores", + "Auftrge aller Shops anzeigen", + "Specifies whether to display the orders of all stores to the customer. If this option is disabled, only the orders of the current store are displayed.", + "Legt fest, ob dem Kunden die Auftrge aller Shops angezeigt werden sollen. Ist diese Option deaktiviert, so werden nur die Auftrge des aktuellen Shops angezeigt."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.GiftCards_Deactivated") + .Value("de", "Geschenkgutschein wird deaktiviert, wenn Auftragsstatus..."); } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs index 47df2024dc..1d9becf2f3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/SettingController.cs @@ -685,13 +685,16 @@ public ActionResult Order() //load settings for a chosen store scope var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); var orderSettings = _services.Settings.LoadSetting(storeScope); - var store = (storeScope == 0 ? _services.StoreContext.CurrentStore : _services.StoreService.GetStoreById(storeScope)); + + var allStores = _services.StoreService.GetAllStores(); + var store = (storeScope == 0 ? _services.StoreContext.CurrentStore : allStores.FirstOrDefault(x => x.Id == storeScope)); var model = orderSettings.ToModel(); StoreDependingSettings.GetOverrideKeys(orderSettings, model, storeScope, _services.Settings); model.PrimaryStoreCurrencyCode = store.PrimaryStoreCurrency.CurrencyCode; + model.StoreCount = allStores.Count; //gift card activation/deactivation model.GiftCards_Activated_OrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList(); diff --git a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs index 9c6a24d877..3cbb5afe21 100644 --- a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs +++ b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs @@ -640,6 +640,7 @@ public void Execute() .ForMember(dest => dest.GiftCards_Activated_OrderStatuses, mo => mo.Ignore()) .ForMember(dest => dest.GiftCards_Deactivated_OrderStatuses, mo => mo.Ignore()) .ForMember(dest => dest.PrimaryStoreCurrencyCode, mo => mo.Ignore()) + .ForMember(dest => dest.StoreCount, mo => mo.Ignore()) .ForMember(dest => dest.OrderIdent, mo => mo.Ignore()) .ForMember(dest => dest.Locales, mo => mo.Ignore()); Mapper.CreateMap() diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Settings/OrderSettingsModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Settings/OrderSettingsModel.cs index ea2bc65410..c54a264eb5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Settings/OrderSettingsModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Settings/OrderSettingsModel.cs @@ -58,10 +58,14 @@ public OrderSettingsModel() public IList GiftCards_Deactivated_OrderStatuses { get; set; } public string PrimaryStoreCurrencyCode { get; set; } + public int StoreCount { get; set; } [SmartResourceDisplayName("Admin.Configuration.Settings.Order.OrderIdent")] public int? OrderIdent { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Settings.Order.DisplayOrdersOfAllStores")] + public bool DisplayOrdersOfAllStores { get; set; } + public IList Locales { get; set; } } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Order.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Order.cshtml index 3c29b263a1..df566da1ed 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Setting/Order.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Setting/Order.cshtml @@ -116,6 +116,18 @@
+ @Html.SmartLabelFor(model => model.DisplayOrdersOfAllStores) + + @Html.SettingEditorFor(model => model.DisplayOrdersOfAllStores) + @Html.ValidationMessageFor(model => model.DisplayOrdersOfAllStores) +
} diff --git a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs index 1c9fa88791..e6b81ab62f 100644 --- a/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs +++ b/src/Presentation/SmartStore.Web/Controllers/CustomerController.cs @@ -360,11 +360,14 @@ protected CustomerOrderListModel PrepareCustomerOrderListModel(Customer customer var model = new CustomerOrderListModel(); model.NavigationModel = GetCustomerNavigationModel(customer); model.NavigationModel.SelectedTab = CustomerNavigationEnum.Orders; - var orders = _orderService.SearchOrders(_storeContext.CurrentStore.Id, customer.Id, - null, null, null, null, null, null, null, null, 0, int.MaxValue); + + var storeScope = (_orderSettings.DisplayOrdersOfAllStores ? 0 : _storeContext.CurrentStore.Id); + + var orders = _orderService.SearchOrders(storeScope, customer.Id, null, null, null, null, null, null, null, null, 0, int.MaxValue); + foreach (var order in orders) { - var orderModel = new CustomerOrderListModel.OrderDetailsModel() + var orderModel = new CustomerOrderListModel.OrderDetailsModel { Id = order.Id, OrderNumber = order.GetOrderNumber(), @@ -372,6 +375,7 @@ protected CustomerOrderListModel PrepareCustomerOrderListModel(Customer customer OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext), IsReturnRequestAllowed = _orderProcessingService.IsReturnRequestAllowed(order) }; + var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate); orderModel.OrderTotal = _priceFormatter.FormatPrice(orderTotalInCustomerCurrency, true, order.CustomerCurrencyCode, false, _workContext.WorkingLanguage); @@ -382,7 +386,7 @@ protected CustomerOrderListModel PrepareCustomerOrderListModel(Customer customer foreach (var recurringPayment in recurringPayments) { - var recurringPaymentModel = new CustomerOrderListModel.RecurringOrderModel() + var recurringPaymentModel = new CustomerOrderListModel.RecurringOrderModel { Id = recurringPayment.Id, StartDate = _dateTimeHelper.ConvertToUserTime(recurringPayment.StartDateUtc, DateTimeKind.Utc).ToString(), @@ -1291,6 +1295,7 @@ public ActionResult Orders() var customer = _workContext.CurrentCustomer; var model = PrepareCustomerOrderListModel(customer); + return View(model); } @@ -1303,9 +1308,13 @@ public ActionResult CancelRecurringPayment(FormCollection form) //get recurring payment identifier int recurringPaymentId = 0; - foreach (var formValue in form.AllKeys) - if (formValue.StartsWith("cancelRecurringPayment", StringComparison.InvariantCultureIgnoreCase)) - recurringPaymentId = Convert.ToInt32(formValue.Substring("cancelRecurringPayment".Length)); + foreach (var formValue in form.AllKeys) + { + if (formValue.StartsWith("cancelRecurringPayment", StringComparison.InvariantCultureIgnoreCase)) + { + recurringPaymentId = Convert.ToInt32(formValue.Substring("cancelRecurringPayment".Length)); + } + } var recurringPayment = _orderService.GetRecurringPaymentById(recurringPaymentId); if (recurringPayment == null) @@ -1323,11 +1332,9 @@ public ActionResult CancelRecurringPayment(FormCollection form) return View(model); } - else - { - return RedirectToAction("Orders"); - } - } + + return RedirectToAction("Orders"); + } #endregion From 480cb8f4d23dd7e6379bf01eed349d47aadebabc Mon Sep 17 00:00:00 2001 From: Marcus Gesing Date: Mon, 11 Jan 2016 13:29:59 +0100 Subject: [PATCH 250/732] Resolves #790 Improve language editing --- changelog.md | 1 + .../201512151526290_ImportFramework.cs | 10 + .../Administration/Content/admin.less | 15 ++ .../Controllers/LanguageController.cs | 195 +++++++++--------- .../Infrastructure/AutoMapperStartupTask.cs | 4 +- .../Models/Localization/LanguageModel.cs | 13 +- .../Localization/LanguageValidator.cs | 29 +-- .../Administration/Views/Language/List.cshtml | 7 +- .../Views/Language/_CreateOrUpdate.cshtml | 86 ++++---- 9 files changed, 205 insertions(+), 155 deletions(-) diff --git a/changelog.md b/changelog.md index 519c0f8906..f8dec5cd62 100644 --- a/changelog.md +++ b/changelog.md @@ -70,6 +70,7 @@ * Product grid sortable by name, price and created on * #26 Display company or name in order list * Added inline editing of country grid +* #790 Improved language editing ### Bugfixes * #523 Redirecting to payment provider performed by core instead of plugin diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs index 767af5b90c..f281df8a32 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs @@ -347,6 +347,16 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Configuration.Settings.Order.GiftCards_Deactivated") .Value("de", "Geschenkgutschein wird deaktiviert, wenn Auftragsstatus..."); + + builder.AddOrUpdate("Admin.Configuration.Languages.Fields.UniqueSeoCode.Required", + "Please select a SEO language code.", + "Bitte legen Sie einen SEO Sprach-Code fest."); + + builder.AddOrUpdate("Admin.Configuration.Languages.Fields.FlagImageFileName", + "Flag image", + "Flaggenbild", + "Specifies the flag image. The files for the flag images must be stored in /Content/Images/flags/.", + "Legt das Flaggenbild fest. Die Dateien der Flaggenbilder mssen in /Content/Images/flags/ liegen."); } } } diff --git a/src/Presentation/SmartStore.Web/Administration/Content/admin.less b/src/Presentation/SmartStore.Web/Administration/Content/admin.less index eff6584861..26c3604e2a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/admin.less +++ b/src/Presentation/SmartStore.Web/Administration/Content/admin.less @@ -432,6 +432,21 @@ td.adminData > input[type="checkbox"] { min-width: 314px; /* ensures more clean look & feel */ } +.select2-image-item { + img { + margin-bottom: 3px; + } + span:not(.select2-icon) { + padding-left: 5px; + } + .icon-container { + float: left; + display: inline-block; + width: 16px; + min-width: 16px; + max-width: 16px; + } +} /* select2 with large, templated items -------------------------------------------------------------- */ diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs index 02587df34f..20824005a4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs @@ -1,15 +1,17 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Web.Mvc; using SmartStore.Admin.Models.Localization; -using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Plugins; +using SmartStore.Services; +using SmartStore.Services.Directory; using SmartStore.Services.Localization; using SmartStore.Services.Security; using SmartStore.Services.Stores; @@ -28,74 +30,95 @@ public partial class LanguageController : AdminControllerBase #region Fields private readonly ILanguageService _languageService; - private readonly ILocalizationService _localizationService; - private readonly IStoreService _storeService; private readonly IStoreMappingService _storeMappingService; - private readonly IPermissionService _permissionService; - private readonly IWebHelper _webHelper; private readonly AdminAreaSettings _adminAreaSettings; private readonly IPluginFinder _pluginFinder; + private readonly ICountryService _countryService; + private readonly ICommonServices _services; #endregion #region Constructors public LanguageController(ILanguageService languageService, - ILocalizationService localizationService, - IStoreService storeService, IStoreMappingService storeMappingService, - IPermissionService permissionService, - IWebHelper webHelper, AdminAreaSettings adminAreaSettings, - IPluginFinder pluginFinder) + IPluginFinder pluginFinder, + ICountryService countryService, + ICommonServices services) { - this._localizationService = localizationService; this._languageService = languageService; - this._storeService = storeService; this._storeMappingService = storeMappingService; - this._permissionService = permissionService; - this._webHelper = webHelper; this._adminAreaSettings = adminAreaSettings; this._pluginFinder = pluginFinder; + this._countryService = countryService; + this._services = services; } - #endregion - + #endregion + #region Utilities - [NonAction] - private void PrepareFlagsModel(LanguageModel model) - { - if (model == null) - throw new ArgumentNullException("model"); + private void PrepareLanguageModel(LanguageModel model, Language language, bool excludeProperties) + { + var languageId = _services.WorkContext.WorkingLanguage.Id; - model.FlagFileNames = System.IO.Directory - .EnumerateFiles(_webHelper.MapPath("~/Content/Images/flags/"), "*.png", SearchOption.TopDirectoryOnly) - .Select(System.IO.Path.GetFileName) - .ToList(); - } + var allCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures) + .OrderBy(x => x.DisplayName) + .ToList(); - [NonAction] - private void PrepareStoresMappingModel(LanguageModel model, Language language, bool excludeProperties) - { - if (model == null) - throw new ArgumentNullException("model"); + var allCountryNames = _countryService.GetAllCountries(true) + .ToDictionary(x => x.TwoLetterIsoCode.EmptyNull().ToLower(), x => x.GetLocalized(y => y.Name, languageId, true, false)); - model.AvailableStores = _storeService - .GetAllStores() - .Select(s => s.ToModel()) + model.AvailableCultures = allCultures + .Select(x => new SelectListItem { Text = "{0} [{1}]".FormatInvariant(x.DisplayName, x.IetfLanguageTag), Value = x.IetfLanguageTag }) .ToList(); - if (!excludeProperties) + + model.AvailableTwoLetterLanguageCodes = new List(); + model.AvailableFlags = new List(); + + foreach (var item in allCultures) { - if (language != null) - { - model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(language); - } - else + if (!model.AvailableTwoLetterLanguageCodes.Any(x => x.Value.IsCaseInsensitiveEqual(item.TwoLetterISOLanguageName))) { - model.SelectedStoreIds = new int[0]; + // display language name is not provided by net framework + var index = item.DisplayName.EmptyNull().IndexOf(" ("); + + if (index == -1) + index = item.DisplayName.EmptyNull().IndexOf(" ["); + + var displayName = "{0} [{1}]".FormatInvariant( + index == -1 ? item.DisplayName : item.DisplayName.Substring(0, index), + item.TwoLetterISOLanguageName); + + model.AvailableTwoLetterLanguageCodes.Add(new SelectListItem { Text = displayName, Value = item.TwoLetterISOLanguageName }); } } + + foreach (var path in Directory.EnumerateFiles(_services.WebHelper.MapPath("~/Content/Images/flags/"), "*.png", SearchOption.TopDirectoryOnly)) + { + var name = Path.GetFileNameWithoutExtension(path).EmptyNull().ToLower(); + string countryDescription = null; + + if (allCountryNames.ContainsKey(name)) + countryDescription = "{0} [{1}]".FormatInvariant(allCountryNames[name], name); + + if (countryDescription.IsEmpty()) + countryDescription = name; + + model.AvailableFlags.Add(new SelectListItem { Text = countryDescription, Value = Path.GetFileName(path) }); + } + + model.AvailableFlags = model.AvailableFlags.OrderBy(x => x.Text).ToList(); + + model.AvailableStores = _services.StoreService.GetAllStores() + .Select(s => s.ToModel()) + .ToList(); + + if (!excludeProperties) + { + model.SelectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(language); + } } #endregion @@ -109,15 +132,17 @@ public ActionResult Index() public ActionResult List() { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var languages = _languageService.GetAllLanguages(true); + var gridModel = new GridModel { Data = languages.Select(x => x.ToModel()), Total = languages.Count() }; + return View(gridModel); } @@ -126,7 +151,7 @@ public ActionResult List(GridCommand command) { var model = new GridModel(); - if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { var languages = _languageService.GetAllLanguages(true); @@ -148,26 +173,20 @@ public ActionResult List(GridCommand command) public ActionResult Create() { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var model = new LanguageModel(); - //flags - PrepareFlagsModel(model); - - //Stores - PrepareStoresMappingModel(model, null, false); + PrepareLanguageModel(model, null, false); - //default values - //model.Published = true; return View(model); } [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Create(LanguageModel model, bool continueEditing) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); if (ModelState.IsValid) @@ -183,18 +202,14 @@ public ActionResult Create(LanguageModel model, bool continueEditing) foreach (var plugin in plugins) { - _localizationService.ImportPluginResourcesFromXml(plugin, null, false, filterLanguages); + _services.Localization.ImportPluginResourcesFromXml(plugin, null, false, filterLanguages); } - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Languages.Added")); + NotifySuccess(T("Admin.Configuration.Languages.Added")); return continueEditing ? RedirectToAction("Edit", new { id = language.Id }) : RedirectToAction("List"); } - //flags - PrepareFlagsModel(model); - - //Stores - PrepareStoresMappingModel(model, null, true); + PrepareLanguageModel(model, null, true); //If we got this far, something failed, redisplay form return View(model); @@ -202,7 +217,7 @@ public ActionResult Create(LanguageModel model, bool continueEditing) public ActionResult Edit(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var language = _languageService.GetLanguageById(id); @@ -214,11 +229,7 @@ public ActionResult Edit(int id) var model = language.ToModel(); - //flags - PrepareFlagsModel(model); - - //Stores - PrepareStoresMappingModel(model, language, false); + PrepareLanguageModel(model, language, false); return View(model); } @@ -226,7 +237,7 @@ public ActionResult Edit(int id) [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] public ActionResult Edit(LanguageModel model, bool continueEditing) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var language = _languageService.GetLanguageById(model.Id); @@ -252,15 +263,11 @@ public ActionResult Edit(LanguageModel model, bool continueEditing) _storeMappingService.SaveStoreMappings(language, model.SelectedStoreIds); //notification - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Languages.Updated")); + NotifySuccess(T("Admin.Configuration.Languages.Updated")); return continueEditing ? RedirectToAction("Edit", new { id = language.Id }) : RedirectToAction("List"); } - //flags - PrepareFlagsModel(model); - - //Stores - PrepareStoresMappingModel(model, language, true); + PrepareLanguageModel(model, language, true); //If we got this far, something failed, redisplay form return View(model); @@ -269,7 +276,7 @@ public ActionResult Edit(LanguageModel model, bool continueEditing) [HttpPost] public ActionResult Delete(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var language = _languageService.GetLanguageById(id); @@ -288,7 +295,7 @@ public ActionResult Delete(int id) _languageService.DeleteLanguage(language); //notification - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Languages.Deleted")); + NotifySuccess(T("Admin.Configuration.Languages.Deleted")); return RedirectToAction("List"); } @@ -298,7 +305,7 @@ public ActionResult Delete(int id) public ActionResult Resources(int languageId) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); ViewBag.AllLanguages = _languageService.GetAllLanguages(true) @@ -312,7 +319,7 @@ public ActionResult Resources(int languageId) ViewBag.LanguageId = languageId; ViewBag.LanguageName = language.Name; - var resources = _localizationService + var resources = _services.Localization .GetResourceValues(languageId, true) .Where(x => x.Key != "!!___EOF___!!" && x.Value != null) .OrderBy(x => x.Key) @@ -339,11 +346,11 @@ public ActionResult Resources(int languageId, GridCommand command) { var model = new GridModel(); - if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { var language = _languageService.GetLanguageById(languageId); - var resources = _localizationService + var resources = _services.Localization .GetResourceValues(languageId, true) .OrderBy(x => x.Key) .Where(x => x.Key != "!!___EOF___!!" && x.Value != null) @@ -377,7 +384,7 @@ public ActionResult Resources(int languageId, GridCommand command) [GridAction(EnableCustomBinding = true)] public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand command) { - if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { if (model.Name != null) model.Name = model.Name.Trim(); @@ -390,11 +397,11 @@ public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand comm return Content(modelStateErrors.FirstOrDefault()); } - var resource = _localizationService.GetLocaleStringResourceById(model.Id); + var resource = _services.Localization.GetLocaleStringResourceById(model.Id); // if the resourceName changed, ensure it isn't being used by another resource if (!resource.ResourceName.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase)) { - var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); + var res = _services.Localization.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); if (res != null && res.Id != resource.Id) { return Content(T("Admin.Configuration.Languages.Resources.NameAlreadyExists", res.ResourceName)); @@ -405,7 +412,7 @@ public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand comm resource.ResourceValue = model.Value; resource.IsTouched = true; - _localizationService.UpdateLocaleStringResource(resource); + _services.Localization.UpdateLocaleStringResource(resource); } return Resources(model.LanguageId, command); @@ -414,7 +421,7 @@ public ActionResult ResourceUpdate(LanguageResourceModel model, GridCommand comm [GridAction(EnableCustomBinding = true)] public ActionResult ResourceAdd(int id, LanguageResourceModel model, GridCommand command) { - if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { if (model.Name != null) model.Name = model.Name.Trim(); @@ -427,7 +434,7 @@ public ActionResult ResourceAdd(int id, LanguageResourceModel model, GridCommand return Content(modelStateErrors.FirstOrDefault()); } - var res = _localizationService.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); + var res = _services.Localization.GetLocaleStringResourceByName(model.Name, model.LanguageId, false); if (res == null) { var resource = new LocaleStringResource { LanguageId = id }; @@ -435,7 +442,7 @@ public ActionResult ResourceAdd(int id, LanguageResourceModel model, GridCommand resource.ResourceValue = model.Value; resource.IsTouched = true; - _localizationService.InsertLocaleStringResource(resource); + _services.Localization.InsertLocaleStringResource(resource); } else { @@ -449,11 +456,11 @@ public ActionResult ResourceAdd(int id, LanguageResourceModel model, GridCommand [GridAction(EnableCustomBinding = true)] public ActionResult ResourceDelete(int id, int languageId, GridCommand command) { - if (_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) { - var resource = _localizationService.GetLocaleStringResourceById(id); + var resource = _services.Localization.GetLocaleStringResourceById(id); - _localizationService.DeleteLocaleStringResource(resource); + _services.Localization.DeleteLocaleStringResource(resource); } return Resources(languageId, command); @@ -465,7 +472,7 @@ public ActionResult ResourceDelete(int id, int languageId, GridCommand command) public ActionResult ExportXml(int id) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var language = _languageService.GetLanguageById(id); @@ -474,7 +481,7 @@ public ActionResult ExportXml(int id) try { - var xml = _localizationService.ExportResourcesToXml(language); + var xml = _services.Localization.ExportResourcesToXml(language); return new XmlDownloadResult(xml, "language-pack-{0}.xml".FormatInvariant(language.UniqueSeoCode)); } catch (Exception exc) @@ -487,7 +494,7 @@ public ActionResult ExportXml(int id) [HttpPost] public ActionResult ImportXml(int id, FormCollection form, ImportModeFlags mode, bool updateTouched) { - if (!_permissionService.Authorize(StandardPermissionProvider.ManageLanguages)) + if (!_services.Permissions.Authorize(StandardPermissionProvider.ManageLanguages)) return AccessDeniedView(); var language = _languageService.GetLanguageById(id); @@ -502,15 +509,15 @@ public ActionResult ImportXml(int id, FormCollection form, ImportModeFlags mode, var file = Request.Files["importxmlfile"]; if (file != null && file.ContentLength > 0) { - _localizationService.ImportResourcesFromXml(language, file.InputStream.AsString(), mode: mode, updateTouchedResources: updateTouched); + _services.Localization.ImportResourcesFromXml(language, file.InputStream.AsString(), mode: mode, updateTouchedResources: updateTouched); } else { - NotifyError(_localizationService.GetResource("Admin.Common.UploadFile")); + NotifyError(T("Admin.Common.UploadFile")); return RedirectToAction("Edit", new { id = language.Id }); } - NotifySuccess(_localizationService.GetResource("Admin.Configuration.Languages.Imported")); + NotifySuccess(T("Admin.Configuration.Languages.Imported")); return RedirectToAction("Edit", new { id = language.Id }); } catch (Exception exc) diff --git a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs index 3cbb5afe21..cf6652c228 100644 --- a/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs +++ b/src/Presentation/SmartStore.Web/Administration/Infrastructure/AutoMapperStartupTask.cs @@ -129,7 +129,9 @@ public void Execute() .ForMember(dest => dest.Country, mo => mo.Ignore()); //language Mapper.CreateMap() - .ForMember(dest => dest.FlagFileNames, mo => mo.Ignore()) + .ForMember(dest => dest.AvailableFlags, mo => mo.Ignore()) + .ForMember(dest => dest.AvailableCultures, mo => mo.Ignore()) + .ForMember(dest => dest.AvailableTwoLetterLanguageCodes, mo => mo.Ignore()) .ForMember(dest => dest.AvailableStores, mo => mo.Ignore()) .ForMember(dest => dest.SelectedStoreIds, mo => mo.Ignore()); Mapper.CreateMap() diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Localization/LanguageModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Localization/LanguageModel.cs index b720c69d5f..28d3dfaf30 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Localization/LanguageModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Localization/LanguageModel.cs @@ -8,7 +8,7 @@ namespace SmartStore.Admin.Models.Localization { - [Validator(typeof(LanguageValidator))] + [Validator(typeof(LanguageValidator))] public class LanguageModel : EntityModelBase { public LanguageModel() @@ -23,18 +23,21 @@ public LanguageModel() [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.LanguageCulture")] [AllowHtml] public string LanguageCulture { get; set; } + public List AvailableCultures { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.UniqueSeoCode")] + [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.UniqueSeoCode")] [AllowHtml] public string UniqueSeoCode { get; set; } + public List AvailableTwoLetterLanguageCodes { get; set; } - //flags - [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.FlagImageFileName")] + //flags + [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.FlagImageFileName")] [AllowHtml] public string FlagImageFileName { get; set; } public IList FlagFileNames { get; set; } + public List AvailableFlags { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.Rtl")] + [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.Rtl")] public bool Rtl { get; set; } [SmartResourceDisplayName("Admin.Configuration.Languages.Fields.Published")] diff --git a/src/Presentation/SmartStore.Web/Administration/Validators/Localization/LanguageValidator.cs b/src/Presentation/SmartStore.Web/Administration/Validators/Localization/LanguageValidator.cs index ea6ba1314e..891c093efc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Validators/Localization/LanguageValidator.cs +++ b/src/Presentation/SmartStore.Web/Administration/Validators/Localization/LanguageValidator.cs @@ -9,29 +9,32 @@ public partial class LanguageValidator : AbstractValidator { public LanguageValidator(ILocalizationService localizationService) { - RuleFor(x => x.Name).NotEmpty().WithMessage(localizationService.GetResource("Admin.Configuration.Languages.Fields.Name.Required")); + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage(localizationService.GetResource("Admin.Configuration.Languages.Fields.Name.Required")); + RuleFor(x => x.LanguageCulture) .Must(x => - { - try - { - var culture = new CultureInfo(x); - return culture != null; - } - catch - { - return false; - } - }) + { + try + { + var culture = new CultureInfo(x); + return culture != null; + } + catch + { + return false; + } + }) .WithMessage(localizationService.GetResource("Admin.Configuration.Languages.Fields.LanguageCulture.Validation")); RuleFor(x => x.UniqueSeoCode) .NotNull() .WithMessage(localizationService.GetResource("Admin.Configuration.Languages.Fields.UniqueSeoCode.Required")); + RuleFor(x => x.UniqueSeoCode) .Length(2) .WithMessage(localizationService.GetResource("Admin.Configuration.Languages.Fields.UniqueSeoCode.Length")); - } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml index 362dd5f5a6..91c1fe8140 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml @@ -1,7 +1,6 @@ @model Telerik.Web.Mvc.GridModel @using Telerik.Web.Mvc.UI @{ - //page title ViewBag.Title = T("Admin.Configuration.Languages").Text; }
@@ -24,7 +23,7 @@ columns.Bound(x => x.Name) .Template(x => Html.ActionLink(x.Name, "Edit", new { id = x.Id })); columns.Bound(x => x.LanguageCulture) - .Width(150) + .Width(200) .Centered(); columns.Template( @
@@ -34,10 +33,10 @@ .Centered() .Title(T("Admin.Configuration.Languages.Resources.View").Text); columns.Bound(x => x.DisplayOrder) - .Width(100) + .Width(200) .Centered(); columns.Bound(x => x.Published) - .Width(100) + .Width(200) .Template(item => @Html.SymbolForBool(item.Published)) .ClientTemplate(@Html.SymbolForBool("Published")) .Centered(); diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Language/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Language/_CreateOrUpdate.cshtml index 621e963481..7f393dd8c5 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Language/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Language/_CreateOrUpdate.cshtml @@ -1,9 +1,7 @@ -@model LanguageModel +@using SmartStore.Web.Framework.UI; +@model LanguageModel -@using Telerik.Web.Mvc.UI; -@using SmartStore.Web.Framework.UI; - -@Html.ValidationSummary(true) +@Html.ValidationSummary(false) @Html.HiddenFor(model => model.Id) @Html.SmartStore().TabStrip().Name("language-edit").Style(TabsStyle.Tabs).Position(TabsPosition.Top).Items(x => { @@ -16,20 +14,7 @@ @helper TabInfo() { - var cultures = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.SpecificCultures).ToList(); - - - +
@@ -54,7 +38,7 @@ @Html.SmartLabelFor(model => model.UniqueSeoCode) @@ -63,20 +47,15 @@ @Html.SmartLabelFor(model => model.FlagImageFileName) @@ -118,9 +97,40 @@ toggleStoreMapping(); - $("#pnlAllFlags a.flag").click(function () { - $("#FlagImageFileName").val($(this).data("filename")); + // init flags select + $('#LanguageEditTable').find('select[name=FlagImageFileName]').select2({ + allowClear: true, + minimumResultsForSearch: 16, + formatResult: flagSelectItemFormatting, + formatSelection: flagSelectItemFormatting }); + + function flagSelectItemFormatting(item) { + var flagUrl = '@Url.Content("~/Content/Images/flags/")'; + + try { + if (item.text.length > 0) { + var option = $(item.element), + html = '
'; + + // item.element is undefined for selection formatting + if (option.length === 0) { + option = $('select[name="FlagImageFileName"]').find('option[value="' + item.id + '"]'); + } + + if (option.length !== 0) { + html += '
'; + } + + html += '' + item.text + '
'; + + return html; + } + } + catch (e) { } + + return item.text; + } }); @@ -132,8 +142,8 @@ $('#pnl-available-stores').hide(); } } - +
@Html.SmartLabelFor(model => model.Name) @@ -44,8 +29,7 @@ @Html.SmartLabelFor(model => model.LanguageCulture) - @Html.DropDownListFor(x => x.LanguageCulture, cultures.Select(x => - new SelectListItem { Value = x.IetfLanguageTag, Text = string.Format("{0} [{1}]", x.DisplayName, x.IetfLanguageTag) })) + @Html.DropDownListFor(x => x.LanguageCulture, Model.AvailableCultures) @Html.ValidationMessageFor(model => model.LanguageCulture)
- @Html.EditorFor(model => model.UniqueSeoCode) + @Html.DropDownListFor(x => x.UniqueSeoCode, Model.AvailableTwoLetterLanguageCodes) @Html.ValidationMessageFor(model => model.UniqueSeoCode)
- @Html.EditorFor(model => model.FlagImageFileName) + @Html.ValidationMessageFor(model => model.FlagImageFileName) -
- @T("Admin.Common.Show") -
From c1a189e89bb48c296704eaecd1b2380f2a472385 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 11 Jan 2016 14:54:42 +0100 Subject: [PATCH 251/732] Suppresses opening of a non existing link in a new window according to forum post 47526 --- .../SmartStore.Web/Views/Checkout/Confirm.cshtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Presentation/SmartStore.Web/Views/Checkout/Confirm.cshtml b/src/Presentation/SmartStore.Web/Views/Checkout/Confirm.cshtml index fed6c42b8a..30d4231846 100644 --- a/src/Presentation/SmartStore.Web/Views/Checkout/Confirm.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Checkout/Confirm.cshtml @@ -8,8 +8,8 @@ //title Html.AddTitleParts(T("PageTitle.Checkout").Text); - string termsLink = ""; - string disclaimerLink = ""; + string termsLink = ""; + string disclaimerLink = ""; string terms = string.Format(T("Checkout.TermsOfService.IAccept"), termsLink, "", disclaimerLink); } @section orderProgress{ @@ -38,7 +38,7 @@ $("#terms-of-service-modal .modal-body").html('