示例#1
0
		/// <summary>
		/// Writes a single product
		/// </summary>
		/// <param name="writer">The XML writer</param>
		/// <param name="product">The product</param>
		/// <param name="context">Context objects</param>
		public virtual void WriteProductToXml(XmlWriter writer, Product product, XmlExportContext context)
		{
			var culture = CultureInfo.InvariantCulture;
			var productTemplate = context.ProductTemplates.FirstOrDefault(x => x.Id == product.ProductTemplateId);

			writer.Write("Id", product.Id.ToString());
			writer.Write("Name", product.Name);
			writer.Write("SeName", product.GetSeName(0, true, false));

			writer.Write("ShortDescription", product.ShortDescription, null, true);
			writer.Write("FullDescription", product.FullDescription, null, true);

			writer.Write("AdminComment", product.AdminComment);
			writer.Write("ProductTemplateId", product.ProductTemplateId.ToString());
			writer.Write("ProductTemplateViewPath", productTemplate == null ? "" : productTemplate.ViewPath);
			writer.Write("ShowOnHomePage", product.ShowOnHomePage.ToString());
			writer.Write("HomePageDisplayOrder", product.HomePageDisplayOrder.ToString());
			writer.Write("MetaKeywords", product.MetaKeywords);
			writer.Write("MetaDescription", product.MetaDescription);
			writer.Write("MetaTitle", product.MetaTitle);
			writer.Write("AllowCustomerReviews", product.AllowCustomerReviews.ToString());
			writer.Write("ApprovedRatingSum", product.ApprovedRatingSum.ToString());
			writer.Write("NotApprovedRatingSum", product.NotApprovedRatingSum.ToString());
			writer.Write("ApprovedTotalReviews", product.ApprovedTotalReviews.ToString());
			writer.Write("NotApprovedTotalReviews", product.NotApprovedTotalReviews.ToString());
			writer.Write("Published", product.Published.ToString());
			writer.Write("CreatedOnUtc", product.CreatedOnUtc.ToString(culture));
			writer.Write("UpdatedOnUtc", product.UpdatedOnUtc.ToString(culture));
			writer.Write("SubjectToAcl", product.SubjectToAcl.ToString());
			writer.Write("LimitedToStores", product.LimitedToStores.ToString());
			writer.Write("ProductTypeId", product.ProductTypeId.ToString());
			writer.Write("ParentGroupedProductId", product.ParentGroupedProductId.ToString());
			writer.Write("Sku", product.Sku);
			writer.Write("ManufacturerPartNumber", product.ManufacturerPartNumber);
			writer.Write("Gtin", product.Gtin);
			writer.Write("IsGiftCard", product.IsGiftCard.ToString());
			writer.Write("GiftCardTypeId", product.GiftCardTypeId.ToString());
			writer.Write("RequireOtherProducts", product.RequireOtherProducts.ToString());
			writer.Write("RequiredProductIds", product.RequiredProductIds);
			writer.Write("AutomaticallyAddRequiredProducts", product.AutomaticallyAddRequiredProducts.ToString());
			writer.Write("IsDownload", product.IsDownload.ToString());
			writer.Write("DownloadId", product.DownloadId.ToString());
			writer.Write("UnlimitedDownloads", product.UnlimitedDownloads.ToString());
			writer.Write("MaxNumberOfDownloads", product.MaxNumberOfDownloads.ToString());
			writer.Write("DownloadExpirationDays", product.DownloadExpirationDays.HasValue ? product.DownloadExpirationDays.ToString() : "");
			writer.Write("DownloadActivationType", product.DownloadActivationType.ToString());
			writer.Write("HasSampleDownload", product.HasSampleDownload.ToString());
			writer.Write("SampleDownloadId", product.SampleDownloadId.ToString());
			writer.Write("HasUserAgreement", product.HasUserAgreement.ToString());
			writer.Write("UserAgreementText", product.UserAgreementText);
			writer.Write("IsRecurring", product.IsRecurring.ToString());
			writer.Write("RecurringCycleLength", product.RecurringCycleLength.ToString());
			writer.Write("RecurringCyclePeriodId", product.RecurringCyclePeriodId.ToString());
			writer.Write("RecurringTotalCycles", product.RecurringTotalCycles.ToString());
			writer.Write("IsShipEnabled", product.IsShipEnabled.ToString());
			writer.Write("IsFreeShipping", product.IsFreeShipping.ToString());
			writer.Write("AdditionalShippingCharge", product.AdditionalShippingCharge.ToString(culture));
			writer.Write("IsTaxExempt", product.IsTaxExempt.ToString());
			writer.Write("TaxCategoryId", product.TaxCategoryId.ToString());
			writer.Write("ManageInventoryMethodId", product.ManageInventoryMethodId.ToString());
			writer.Write("StockQuantity", product.StockQuantity.ToString());
			writer.Write("DisplayStockAvailability", product.DisplayStockAvailability.ToString());
			writer.Write("DisplayStockQuantity", product.DisplayStockQuantity.ToString());
			writer.Write("MinStockQuantity", product.MinStockQuantity.ToString());
			writer.Write("LowStockActivityId", product.LowStockActivityId.ToString());
			writer.Write("NotifyAdminForQuantityBelow", product.NotifyAdminForQuantityBelow.ToString());
			writer.Write("BackorderModeId", product.BackorderModeId.ToString());
			writer.Write("AllowBackInStockSubscriptions", product.AllowBackInStockSubscriptions.ToString());
			writer.Write("OrderMinimumQuantity", product.OrderMinimumQuantity.ToString());
			writer.Write("OrderMaximumQuantity", product.OrderMaximumQuantity.ToString());
			writer.Write("AllowedQuantities", product.AllowedQuantities);
			writer.Write("DisableBuyButton", product.DisableBuyButton.ToString());
			writer.Write("DisableWishlistButton", product.DisableWishlistButton.ToString());
			writer.Write("AvailableForPreOrder", product.AvailableForPreOrder.ToString());
			writer.Write("CallForPrice", product.CallForPrice.ToString());
			writer.Write("Price", product.Price.ToString(culture));
			writer.Write("OldPrice", product.OldPrice.ToString(culture));
			writer.Write("ProductCost", product.ProductCost.ToString(culture));
			writer.Write("SpecialPrice", product.SpecialPrice.HasValue ? product.SpecialPrice.Value.ToString(culture) : "");
			writer.Write("SpecialPriceStartDateTimeUtc", product.SpecialPriceStartDateTimeUtc.HasValue ? product.SpecialPriceStartDateTimeUtc.Value.ToString(culture) : "");
			writer.Write("SpecialPriceEndDateTimeUtc", product.SpecialPriceEndDateTimeUtc.HasValue ? product.SpecialPriceEndDateTimeUtc.Value.ToString(culture) : "");
			writer.Write("CustomerEntersPrice", product.CustomerEntersPrice.ToString());
			writer.Write("MinimumCustomerEnteredPrice", product.MinimumCustomerEnteredPrice.ToString(culture));
			writer.Write("MaximumCustomerEnteredPrice", product.MaximumCustomerEnteredPrice.ToString(culture));
			writer.Write("HasTierPrices", product.HasTierPrices.ToString());
			writer.Write("HasDiscountsApplied", product.HasDiscountsApplied.ToString());
			writer.Write("Weight", product.Weight.ToString(culture));
			writer.Write("Length", product.Length.ToString(culture));
			writer.Write("Width", product.Width.ToString(culture));
			writer.Write("Height", product.Height.ToString(culture));
			writer.Write("AvailableStartDateTimeUtc", product.AvailableStartDateTimeUtc.HasValue ? product.AvailableStartDateTimeUtc.Value.ToString(culture) : "");
			writer.Write("AvailableEndDateTimeUtc", product.AvailableEndDateTimeUtc.HasValue ? product.AvailableEndDateTimeUtc.Value.ToString(culture) : "");
			writer.Write("BasePriceEnabled", product.BasePriceEnabled.ToString());
			writer.Write("BasePriceMeasureUnit", product.BasePriceMeasureUnit);
			writer.Write("BasePriceAmount", product.BasePriceAmount.HasValue ? product.BasePriceAmount.Value.ToString(culture) : "");
			writer.Write("BasePriceBaseAmount", product.BasePriceBaseAmount.HasValue ? product.BasePriceBaseAmount.Value.ToString() : "");
			writer.Write("VisibleIndividually", product.VisibleIndividually.ToString());
			writer.Write("DisplayOrder", product.DisplayOrder.ToString());
			writer.Write("BundleTitleText", product.BundleTitleText);
			writer.Write("BundlePerItemPricing", product.BundlePerItemPricing.ToString());
			writer.Write("BundlePerItemShipping", product.BundlePerItemShipping.ToString());
			writer.Write("BundlePerItemShoppingCart", product.BundlePerItemShoppingCart.ToString());
			writer.Write("LowestAttributeCombinationPrice", product.LowestAttributeCombinationPrice.HasValue ? product.LowestAttributeCombinationPrice.Value.ToString(culture) : "");
			writer.Write("IsEsd", product.IsEsd.ToString());

			WriteLocalized(writer, context, lang =>
			{
				writer.Write("Name", product.GetLocalized(x => x.Name, lang.Id, false, false), lang);
				writer.Write("SeName", product.GetSeName(lang.Id, false, false), lang);
				writer.Write("ShortDescription", product.GetLocalized(x => x.ShortDescription, lang.Id, false, false), lang, true);
				writer.Write("FullDescription", product.GetLocalized(x => x.FullDescription, lang.Id, false, false), lang, true);
				writer.Write("MetaKeywords", product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false), lang);
				writer.Write("MetaDescription", product.GetLocalized(x => x.MetaDescription, lang.Id, false, false), lang);
				writer.Write("MetaTitle", product.GetLocalized(x => x.MetaTitle, lang.Id, false, false), lang);
				writer.Write("BundleTitleText", product.GetLocalized(x => x.BundleTitleText, lang.Id, false, false), lang);
			});

			if (product.DeliveryTime != null)
			{
				writer.WriteStartElement("DeliveryTime");
				writer.Write("Id", product.DeliveryTime.Id.ToString());
				writer.Write("Name", product.DeliveryTime.Name);
				writer.Write("DisplayLocale", product.DeliveryTime.DisplayLocale);
				writer.Write("ColorHexValue", product.DeliveryTime.ColorHexValue);
				writer.Write("DisplayOrder", product.DeliveryTime.DisplayOrder.ToString());
				WriteLocalized(writer, context, lang =>
				{
					writer.Write("Name", product.DeliveryTime.GetLocalized(x => x.Name, lang.Id, false, false), lang);
				});
				writer.WriteEndElement();
			}

			WriteQuantityUnit(writer, context, product.QuantityUnit);

			writer.WriteStartElement("ProductTags");
			foreach (var tag in product.ProductTags)
			{
				writer.WriteStartElement("ProductTag");
				writer.Write("Id", tag.Id.ToString());
				writer.Write("Name", tag.Name);

				WriteLocalized(writer, context, lang =>
				{
					writer.Write("Name", tag.GetLocalized(x => x.Name, lang.Id, false, false), lang);
				});

				writer.WriteEndElement();
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductDiscounts");
			foreach (var discount in product.AppliedDiscounts)
			{
				writer.WriteStartElement("ProductDiscount");
				writer.Write("DiscountId", discount.Id.ToString());
				writer.WriteEndElement();
			}
			writer.WriteEndElement();

			writer.WriteStartElement("TierPrices");
			foreach (var tierPrice in product.TierPrices)
			{
				writer.WriteStartElement("TierPrice");
				writer.Write("Id", tierPrice.Id.ToString());
				writer.Write("StoreId", tierPrice.StoreId.ToString());
				writer.Write("CustomerRoleId", tierPrice.CustomerRoleId.HasValue ? tierPrice.CustomerRoleId.ToString() : "0");
				writer.Write("Quantity", tierPrice.Quantity.ToString());
				writer.Write("Price", tierPrice.Price.ToString(culture));
				writer.WriteEndElement();
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductAttributes");
			foreach (var pva in product.ProductVariantAttributes.OrderBy(x => x.DisplayOrder))
			{
				writer.WriteStartElement("ProductAttribute");

				writer.Write("Id", pva.Id.ToString());
				writer.Write("TextPrompt", pva.TextPrompt);
				writer.Write("IsRequired", pva.IsRequired.ToString());
				writer.Write("AttributeControlTypeId", pva.AttributeControlTypeId.ToString());
				writer.Write("DisplayOrder", pva.DisplayOrder.ToString());

				writer.WriteStartElement("Attribute");
				writer.Write("Id", pva.ProductAttribute.Id.ToString());
				writer.Write("Alias", pva.ProductAttribute.Alias);
				writer.Write("Name", pva.ProductAttribute.Name);
				writer.Write("Description", pva.ProductAttribute.Description);
				WriteLocalized(writer, context, lang =>
				{
					writer.Write("Name", pva.ProductAttribute.GetLocalized(x => x.Name, lang.Id, false, false), lang);
					writer.Write("Description", pva.ProductAttribute.GetLocalized(x => x.Description, lang.Id, false, false), lang);
				});
				writer.WriteEndElement();	// Attribute

				writer.WriteStartElement("AttributeValues");
				foreach (var value in pva.ProductVariantAttributeValues.OrderBy(x => x.DisplayOrder))
				{
					writer.WriteStartElement("AttributeValue");
					writer.Write("Id", value.Id.ToString());
					writer.Write("Alias", value.Alias);
					writer.Write("Name", value.Name);
					writer.Write("ColorSquaresRgb", value.ColorSquaresRgb);
					writer.Write("PriceAdjustment", value.PriceAdjustment.ToString(culture));
					writer.Write("WeightAdjustment", value.WeightAdjustment.ToString(culture));
					writer.Write("IsPreSelected", value.IsPreSelected.ToString());
					writer.Write("DisplayOrder", value.DisplayOrder.ToString());
					writer.Write("ValueTypeId", value.ValueTypeId.ToString());
					writer.Write("LinkedProductId", value.LinkedProductId.ToString());
					writer.Write("Quantity", value.Quantity.ToString());
					WriteLocalized(writer, context, lang =>
					{
						writer.Write("Name", value.GetLocalized(x => x.Name, lang.Id, false, false), lang);
					});
					writer.WriteEndElement();	// AttributeValue
				}
				writer.WriteEndElement();	// AttributeValues

				writer.WriteEndElement();	// ProductAttribute
			}
			writer.WriteEndElement();   // ProductAttributes

			using (var scope = new DbContextScope(proxyCreation: false, forceNoTracking: true))
			{
				var allCombinations = product.ProductVariantAttributeCombinations;

				writer.WriteStartElement("ProductAttributeCombinations");
				foreach (var combination in allCombinations)
				{
					writer.WriteStartElement("ProductAttributeCombination");

					writer.Write("Id", combination.Id.ToString());
					writer.Write("StockQuantity", combination.StockQuantity.ToString());
					writer.Write("AllowOutOfStockOrders", combination.AllowOutOfStockOrders.ToString());
					writer.Write("AttributesXml", combination.AttributesXml, null, true);
					writer.Write("Sku", combination.Sku);
					writer.Write("Gtin", combination.Gtin);
					writer.Write("ManufacturerPartNumber", combination.ManufacturerPartNumber);
					writer.Write("Price", combination.Price.HasValue ? combination.Price.Value.ToString(culture) : "");
					writer.Write("Length", combination.Length.HasValue ? combination.Length.Value.ToString(culture) : "");
					writer.Write("Width", combination.Width.HasValue ? combination.Width.Value.ToString(culture) : "");
					writer.Write("Height", combination.Height.HasValue ? combination.Height.Value.ToString(culture) : "");
					writer.Write("BasePriceAmount", combination.BasePriceAmount.HasValue ? combination.BasePriceAmount.Value.ToString(culture) : "");
					writer.Write("BasePriceBaseAmount", combination.BasePriceBaseAmount.HasValue ? combination.BasePriceBaseAmount.Value.ToString() : "");
					writer.Write("DeliveryTimeId", combination.DeliveryTimeId.HasValue ? combination.DeliveryTimeId.Value.ToString() : "");
					writer.Write("IsActive", combination.IsActive.ToString());

					WriteQuantityUnit(writer, context, combination.QuantityUnit);

					writer.WriteStartElement("Pictures");
					foreach (int pictureId in combination.GetAssignedPictureIds())
					{
						WritePicture(writer, context, _pictureService.GetPictureById(pictureId), _mediaSettings.ProductThumbPictureSize, _mediaSettings.ProductDetailsPictureSize);
					}
					writer.WriteEndElement();   // Pictures

					writer.WriteEndElement();   // ProductAttributeCombination
				}
				writer.WriteEndElement(); // ProductAttributeCombinations
			}

			writer.WriteStartElement("ProductPictures");
			foreach (var productPicture in product.ProductPictures.OrderBy(x => x.DisplayOrder))
			{
				writer.WriteStartElement("ProductPicture");
				writer.Write("Id", productPicture.Id.ToString());
				writer.Write("DisplayOrder", productPicture.DisplayOrder.ToString());

				WritePicture(writer, context, productPicture.Picture, _mediaSettings.ProductThumbPictureSize, _mediaSettings.ProductDetailsPictureSize);

				writer.WriteEndElement();
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductCategories");
			var productCategories = _categoryService.GetProductCategoriesByProductId(product.Id);
			if (productCategories != null)
			{
				foreach (var productCategory in productCategories.OrderBy(x => x.DisplayOrder))
				{
					var category = productCategory.Category;
					writer.WriteStartElement("ProductCategory");
					writer.Write("IsFeaturedProduct", productCategory.IsFeaturedProduct.ToString());
					writer.Write("DisplayOrder", productCategory.DisplayOrder.ToString());
					
					writer.WriteStartElement("Category");
					writer.Write("Id", category.Id.ToString());
					writer.Write("Name", category.Name);
					writer.Write("FullName", category.FullName);
					writer.Write("Description", category.Description);
					writer.Write("BottomDescription", category.BottomDescription);
					writer.Write("CategoryTemplateId", category.CategoryTemplateId.ToString());
					writer.Write("MetaKeywords", category.MetaKeywords);
					writer.Write("MetaDescription", category.MetaDescription);
					writer.Write("MetaTitle", category.MetaTitle);
					writer.Write("SeName", category.GetSeName(0));
					writer.Write("ParentCategoryId", category.ParentCategoryId.ToString());
					writer.Write("PageSize", category.PageSize.ToString());
					writer.Write("AllowCustomersToSelectPageSize", category.AllowCustomersToSelectPageSize.ToString());
					writer.Write("PageSizeOptions", category.PageSizeOptions);
					writer.Write("PriceRanges", category.PriceRanges);
					writer.Write("ShowOnHomePage", category.ShowOnHomePage.ToString());
					writer.Write("HasDiscountsApplied", category.HasDiscountsApplied.ToString());
					writer.Write("Published", category.Published.ToString());
					writer.Write("Deleted", category.Deleted.ToString());
					writer.Write("DisplayOrder", category.DisplayOrder.ToString());
					writer.Write("CreatedOnUtc", category.CreatedOnUtc.ToString(culture));
					writer.Write("UpdatedOnUtc", category.UpdatedOnUtc.ToString(culture));
					writer.Write("SubjectToAcl", category.SubjectToAcl.ToString());
					writer.Write("LimitedToStores", category.LimitedToStores.ToString());
					writer.Write("Alias", category.Alias);
					writer.Write("DefaultViewMode", category.DefaultViewMode);

					WritePicture(writer, context, category.Picture, _mediaSettings.CategoryThumbPictureSize, _mediaSettings.CategoryThumbPictureSize);

					WriteLocalized(writer, context, lang =>
					{
						writer.Write("Name", category.GetLocalized(x => x.Name, lang.Id, false, false), lang);
						writer.Write("FullName", category.GetLocalized(x => x.FullName, lang.Id, false, false), lang);
						writer.Write("Description", category.GetLocalized(x => x.Description, lang.Id, false, false), lang);
						writer.Write("BottomDescription", category.GetLocalized(x => x.BottomDescription, lang.Id, false, false), lang);
						writer.Write("MetaKeywords", category.GetLocalized(x => x.MetaKeywords, lang.Id, false, false), lang);
						writer.Write("MetaDescription", category.GetLocalized(x => x.MetaDescription, lang.Id, false, false), lang);
						writer.Write("MetaTitle", category.GetLocalized(x => x.MetaTitle, lang.Id, false, false), lang);
						writer.Write("SeName", category.GetSeName(lang.Id, false, false));
					});

					writer.WriteEndElement();
					
					writer.WriteEndElement();
				}
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductManufacturers");
			var productManufacturers = _manufacturerService.GetProductManufacturersByProductId(product.Id);
			if (productManufacturers != null)
			{
				foreach (var productManufacturer in productManufacturers.OrderBy(x => x.DisplayOrder))
				{
					var manu = productManufacturer.Manufacturer;
					writer.WriteStartElement("ProductManufacturer");

					writer.Write("Id", productManufacturer.Id.ToString());
					writer.Write("IsFeaturedProduct", productManufacturer.IsFeaturedProduct.ToString());
					writer.Write("DisplayOrder", productManufacturer.DisplayOrder.ToString());

					writer.WriteStartElement("Manufacturer");
					writer.Write("Id", manu.Id.ToString());
					writer.Write("Name", manu.Name);
					writer.Write("SeName", manu.GetSeName(0, true, false));
					writer.Write("Description", manu.Description);
					writer.Write("MetaKeywords", manu.MetaKeywords);
					writer.Write("MetaDescription", manu.MetaDescription);
					writer.Write("MetaTitle", manu.MetaTitle);

					WritePicture(writer, context, manu.Picture, _mediaSettings.ManufacturerThumbPictureSize, _mediaSettings.ManufacturerThumbPictureSize);

					WriteLocalized(writer, context, lang =>
					{
						writer.Write("Name", manu.GetLocalized(x => x.Name, lang.Id, false, false), lang);
						writer.Write("SeName", manu.GetSeName(lang.Id, false, false), lang);
						writer.Write("Description", manu.GetLocalized(x => x.Description, lang.Id, false, false), lang);
						writer.Write("MetaKeywords", manu.GetLocalized(x => x.MetaKeywords, lang.Id, false, false), lang);
						writer.Write("MetaDescription", manu.GetLocalized(x => x.MetaDescription, lang.Id, false, false), lang);
						writer.Write("MetaTitle", manu.GetLocalized(x => x.MetaTitle, lang.Id, false, false), lang);
					});

					writer.WriteEndElement();

					writer.WriteEndElement();
				}
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductSpecificationAttributes");
			foreach (var pca in product.ProductSpecificationAttributes.OrderBy(x => x.DisplayOrder))
			{
				writer.WriteStartElement("ProductSpecificationAttribute");
				writer.Write("Id", pca.Id.ToString());
				writer.Write("AllowFiltering", pca.AllowFiltering.ToString());
				writer.Write("ShowOnProductPage", pca.ShowOnProductPage.ToString());
				writer.Write("DisplayOrder", pca.DisplayOrder.ToString());

				writer.WriteStartElement("SpecificationAttributeOption");
				writer.Write("Id", pca.SpecificationAttributeOption.Id.ToString());
				writer.Write("DisplayOrder", pca.SpecificationAttributeOption.DisplayOrder.ToString());
				writer.Write("Name", pca.SpecificationAttributeOption.Name);
				WriteLocalized(writer, context, lang =>
				{
					writer.Write("Name", pca.SpecificationAttributeOption.GetLocalized(x => x.Name, lang.Id, false, false), lang);
				});

				writer.WriteStartElement("SpecificationAttribute");
				writer.Write("Id", pca.SpecificationAttributeOption.SpecificationAttribute.Id.ToString());
				writer.Write("DisplayOrder", pca.SpecificationAttributeOption.SpecificationAttribute.DisplayOrder.ToString());
				writer.Write("Name", pca.SpecificationAttributeOption.SpecificationAttribute.Name);
				WriteLocalized(writer, context, lang =>
				{
					writer.Write("Name", pca.SpecificationAttributeOption.SpecificationAttribute.GetLocalized(x => x.Name, lang.Id, false, false), lang);
				});
				writer.WriteEndElement();	// SpecificationAttribute

				writer.WriteEndElement();	// SpecificationAttributeOption

				writer.WriteEndElement();	// ProductSpecificationAttribute
			}
			writer.WriteEndElement();

			writer.WriteStartElement("ProductBundleItems");
			var bundleItems = _productService.GetBundleItems(product.Id, true);
			foreach (var bundleItem in bundleItems.Select(x => x.Item).OrderBy(x => x.DisplayOrder))
			{
				writer.WriteStartElement("ProductBundleItem");
				writer.Write("ProductId", bundleItem.ProductId.ToString());
				writer.Write("BundleProductId", bundleItem.BundleProductId.ToString());
				writer.Write("Quantity", bundleItem.Quantity.ToString());
				writer.Write("Discount", bundleItem.Discount.HasValue ? bundleItem.Discount.Value.ToString(culture) : "");
				writer.Write("DiscountPercentage", bundleItem.DiscountPercentage.ToString());
				writer.Write("Name", bundleItem.GetLocalizedName());
				writer.Write("ShortDescription", bundleItem.ShortDescription);
				writer.Write("FilterAttributes", bundleItem.FilterAttributes.ToString());
				writer.Write("HideThumbnail", bundleItem.HideThumbnail.ToString());
				writer.Write("Visible", bundleItem.Visible.ToString());
				writer.Write("Published", bundleItem.Published.ToString());
				writer.Write("DisplayOrder", bundleItem.DisplayOrder.ToString());
				writer.Write("CreatedOnUtc", bundleItem.CreatedOnUtc.ToString(culture));
				writer.Write("UpdatedOnUtc", bundleItem.UpdatedOnUtc.ToString(culture));
				writer.WriteEndElement();
			}
			writer.WriteEndElement();
		}
        public virtual void AddProductTokens(IList<Token> tokens, Product product, int languageId)
        {
            tokens.Add(new Token("Product.ID", product.Id.ToString()));
            tokens.Add(new Token("Product.Sku", product.Sku));
            tokens.Add(new Token("Product.Name", product.GetLocalized(x => x.Name, languageId)));
            tokens.Add(new Token("Product.ShortDescription", product.GetLocalized(x => x.ShortDescription, languageId), true));
            tokens.Add(new Token("Product.StockQuantity", product.StockQuantity.ToString()));

            // TODO: add a method for getting URL (use routing because it handles all SEO friendly URLs)
            var productUrl = string.Format("{0}{1}", _webHelper.GetStoreLocation(false), product.GetSeName());
            tokens.Add(new Token("Product.ProductURLForCustomer", productUrl, true));

            //event notification
            _eventPublisher.EntityTokensAdded(product, tokens);
        }
        /// <param name="selectedAttributes">Attributes explicitly selected by user or by query string.</param>
        public ProductDetailsModel PrepareProductDetailModel(
			ProductDetailsModel model,
			Product product,
			bool isAssociatedProduct = false,
			ProductBundleItemData productBundleItem = null,
			IList<ProductBundleItemData> productBundleItems = null,
			NameValueCollection selectedAttributes = null,
			int selectedQuantity = 1)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            if (model == null)
                throw new ArgumentNullException("model");

            if (selectedAttributes == null)
                selectedAttributes = new NameValueCollection();

            var store = _services.StoreContext.CurrentStore;
            var customer = _services.WorkContext.CurrentCustomer;
            var currency = _services.WorkContext.WorkingCurrency;

            decimal preSelectedPriceAdjustmentBase = decimal.Zero;
            decimal preSelectedWeightAdjustment = decimal.Zero;
            bool displayPrices = _services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices);
            bool isBundle = (product.ProductType == ProductType.BundledProduct);
            bool isBundleItemPricing = (productBundleItem != null && productBundleItem.Item.BundleProduct.BundlePerItemPricing);
            bool isBundlePricing = (productBundleItem != null && !productBundleItem.Item.BundleProduct.BundlePerItemPricing);
            int bundleItemId = (productBundleItem == null ? 0 : productBundleItem.Item.Id);

            bool hasSelectedAttributesValues = false;
            bool hasSelectedAttributes = (selectedAttributes.Count > 0);
            List<ProductVariantAttributeValue> selectedAttributeValues = null;

            var variantAttributes = (isBundle ? new List<ProductVariantAttribute>() : _productAttributeService.GetProductVariantAttributesByProductId(product.Id));

            model.ProductPrice.DynamicPriceUpdate = _catalogSettings.EnableDynamicPriceUpdate;
            model.ProductPrice.BundleItemShowBasePrice = _catalogSettings.BundleItemShowBasePrice;

            if (!model.ProductPrice.DynamicPriceUpdate)
                selectedQuantity = 1;

            #region Product attributes

            if (!isBundle)		// bundles doesn't have attributes
            {
                foreach (var attribute in variantAttributes)
                {
                    var pvaModel = new ProductDetailsModel.ProductVariantAttributeModel
                    {
                        Id = attribute.Id,
                        ProductId = attribute.ProductId,
                        BundleItemId = bundleItemId,
                        ProductAttributeId = attribute.ProductAttributeId,
                        Alias = attribute.ProductAttribute.Alias,
                        Name = attribute.ProductAttribute.GetLocalized(x => x.Name),
                        Description = attribute.ProductAttribute.GetLocalized(x => x.Description),
                        TextPrompt = attribute.TextPrompt,
                        IsRequired = attribute.IsRequired,
                        AttributeControlType = attribute.AttributeControlType,
                        AllowedFileExtensions = _catalogSettings.FileUploadAllowedExtensions
                    };

                    if (attribute.AttributeControlType == AttributeControlType.Datepicker)
                    {
                        if (pvaModel.Alias.HasValue() && RegularExpressions.IsYearRange.IsMatch(pvaModel.Alias))
                        {
                            var match = RegularExpressions.IsYearRange.Match(pvaModel.Alias);
                            pvaModel.BeginYear = match.Groups[1].Value.ToInt();
                            pvaModel.EndYear = match.Groups[2].Value.ToInt();
                        }

                        if (hasSelectedAttributes)
                        {
                            var attributeKey = "product_attribute_{0}_{1}_{2}_{3}".FormatInvariant(product.Id, bundleItemId, attribute.ProductAttributeId, attribute.Id);
                            var day = selectedAttributes[attributeKey + "_day"].ToInt();
                            var month = selectedAttributes[attributeKey + "_month"].ToInt();
                            var year = selectedAttributes[attributeKey + "_year"].ToInt();
                            if (day > 0 && month > 0 && year > 0)
                            {
                                pvaModel.SelectedDay = day;
                                pvaModel.SelectedMonth = month;
                                pvaModel.SelectedYear = year;
                            }
                        }
                    }
                    else if (attribute.AttributeControlType == AttributeControlType.TextBox || attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
                    {
                        if (hasSelectedAttributes)
                        {
                            var attributeKey = "product_attribute_{0}_{1}_{2}_{3}".FormatInvariant(product.Id, bundleItemId, attribute.ProductAttributeId, attribute.Id);
                            pvaModel.TextValue = selectedAttributes[attributeKey];
                        }
                    }

                    var preSelectedValueId = 0;
                    var pvaValues = (attribute.ShouldHaveValues() ? _productAttributeService.GetProductVariantAttributeValues(attribute.Id) : new List<ProductVariantAttributeValue>());

                    foreach (var pvaValue in pvaValues)
                    {
                        ProductBundleItemAttributeFilter attributeFilter = null;

                        if (productBundleItem.FilterOut(pvaValue, out attributeFilter))
                            continue;

                        if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected)
                            preSelectedValueId = attributeFilter.AttributeValueId;

                        var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId);

                        var pvaValueModel = new ProductDetailsModel.ProductVariantAttributeValueModel();
                        pvaValueModel.Id = pvaValue.Id;
                        pvaValueModel.Name = pvaValue.GetLocalized(x => x.Name);
                        pvaValueModel.Alias = pvaValue.Alias;
                        pvaValueModel.ColorSquaresRgb = pvaValue.ColorSquaresRgb; //used with "Color squares" attribute type
                        pvaValueModel.IsPreSelected = pvaValue.IsPreSelected;

                        if (linkedProduct != null && linkedProduct.VisibleIndividually)
                            pvaValueModel.SeName = linkedProduct.GetSeName();

                        if (hasSelectedAttributes)
                            pvaValueModel.IsPreSelected = false;	// explicitly selected always discards pre-selected by merchant

                        // display price if allowed
                        if (displayPrices && !isBundlePricing)
                        {
                            decimal taxRate = decimal.Zero;
                            decimal attributeValuePriceAdjustment = _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(pvaValue);
                            decimal priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, out taxRate);
                            decimal priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, currency);

                            if (priceAdjustmentBase > decimal.Zero)
                                pvaValueModel.PriceAdjustment = "+" + _priceFormatter.FormatPrice(priceAdjustment, true, false);
                            else if (priceAdjustmentBase < decimal.Zero)
                                pvaValueModel.PriceAdjustment = "-" + _priceFormatter.FormatPrice(-priceAdjustment, true, false);

                            if (pvaValueModel.IsPreSelected)
                            {
                                preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase);
                                preSelectedWeightAdjustment = decimal.Add(preSelectedWeightAdjustment, pvaValue.WeightAdjustment);
                            }

                            if (_catalogSettings.ShowLinkedAttributeValueQuantity && pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage)
                            {
                                pvaValueModel.QuantityInfo = pvaValue.Quantity;
                            }

                            pvaValueModel.PriceAdjustmentValue = priceAdjustment;
                        }

                        if (!_catalogSettings.ShowVariantCombinationPriceAdjustment)
                        {
                            pvaValueModel.PriceAdjustment = "";
                        }

                        if (_catalogSettings.ShowLinkedAttributeValueImage && pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage)
                        {
                            var linkagePicture = _pictureService.GetPicturesByProductId(pvaValue.LinkedProductId, 1).FirstOrDefault();
                            if (linkagePicture != null)
                                pvaValueModel.ImageUrl = _pictureService.GetPictureUrl(linkagePicture, _mediaSettings.VariantValueThumbPictureSize, false);
                        }

                        pvaModel.Values.Add(pvaValueModel);
                    }

                    // we need selected attributes to get initially displayed combination images
                    if (!hasSelectedAttributes)
                    {
                        ProductDetailsModel.ProductVariantAttributeValueModel defaultValue = null;

                        if (preSelectedValueId != 0)	// value pre-selected by a bundle item filter discards the default pre-selection
                        {
                            pvaModel.Values.Each(x => x.IsPreSelected = false);

                            if ((defaultValue = pvaModel.Values.FirstOrDefault(v => v.Id == preSelectedValueId)) != null)
                            {
                                defaultValue.IsPreSelected = true;
                                selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, defaultValue.Id, product.Id, bundleItemId);
                            }
                        }

                        if (defaultValue == null)
                        {
                            foreach (var value in pvaModel.Values.Where(x => x.IsPreSelected))
                            {
                                selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, value.Id, product.Id, bundleItemId);
                            }
                        }

                        //if (defaultValue == null)
                        //	defaultValue = pvaModel.Values.FirstOrDefault(v => v.IsPreSelected);

                        //if (defaultValue != null)
                        //	selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, defaultValue.Id, product.Id, bundleItemId);
                    }

                    model.ProductVariantAttributes.Add(pvaModel);
                }
            }

            #endregion

            #region Attribute combinations

            if (!isBundle)
            {
                if (selectedAttributes.Count > 0)
                {
                    // merge with combination data if there's a match
                    var warnings = new List<string>();
                    string attributeXml = selectedAttributes.CreateSelectedAttributesXml(product.Id, variantAttributes, _productAttributeParser, _localizationService,
                        _downloadService, _catalogSettings, _httpRequest, warnings, true, bundleItemId);

                    selectedAttributeValues = _productAttributeParser.ParseProductVariantAttributeValues(attributeXml).ToList();
                    hasSelectedAttributesValues = (selectedAttributeValues.Count > 0);

                    if (isBundlePricing)
                    {
                        model.AttributeInfo = _productAttributeFormatter.FormatAttributes(product, attributeXml, customer,
                            renderPrices: false, renderGiftCardAttributes: false, allowHyperlinks: false);
                    }

                    model.SelectedCombination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, attributeXml);

                    if (model.SelectedCombination != null && model.SelectedCombination.IsActive == false)
                    {
                        model.IsAvailable = false;
                        model.StockAvailability = T("Products.Availability.IsNotActive");
                    }

                    product.MergeWithCombination(model.SelectedCombination);

                    // mark explicitly selected as pre-selected
                    foreach (var attribute in model.ProductVariantAttributes)
                    {
                        foreach (var value in attribute.Values)
                        {
                            if (selectedAttributeValues.FirstOrDefault(v => v.Id == value.Id) != null)
                                value.IsPreSelected = true;

                            if (!_catalogSettings.ShowVariantCombinationPriceAdjustment)
                                value.PriceAdjustment = "";
                        }
                    }
                }
            }

            #endregion

            #region Properties

            if ((productBundleItem != null && !productBundleItem.Item.BundleProduct.BundlePerItemShoppingCart) ||
                (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes && !hasSelectedAttributesValues))
            {
                // cases where stock inventory is not functional. determined by what ShoppingCartService.GetStandardWarnings and ProductService.AdjustInventory is not handling.
                model.IsAvailable = true;
                var hasAttributeCombinations = _services.DbContext.QueryForCollection(product, (Product p) => p.ProductVariantAttributeCombinations).Any();
                model.StockAvailability = !hasAttributeCombinations ? product.FormatStockMessage(_localizationService) : "";
            }
            else if (model.IsAvailable)
            {
                model.IsAvailable = product.IsAvailableByStock();
                model.StockAvailability = product.FormatStockMessage(_localizationService);
            }

            model.Id = product.Id;
            model.Name = product.GetLocalized(x => x.Name);
            model.ShowSku = _catalogSettings.ShowProductSku;
            model.Sku = product.Sku;
            model.ShortDescription = product.GetLocalized(x => x.ShortDescription);
            model.FullDescription = product.GetLocalized(x => x.FullDescription);
            model.MetaKeywords = product.GetLocalized(x => x.MetaKeywords);
            model.MetaDescription = product.GetLocalized(x => x.MetaDescription);
            model.MetaTitle = product.GetLocalized(x => x.MetaTitle);
            model.SeName = product.GetSeName();
            model.ShowManufacturerPartNumber = _catalogSettings.ShowManufacturerPartNumber;
            model.ManufacturerPartNumber = product.ManufacturerPartNumber;
            model.ShowDimensions = _catalogSettings.ShowDimensions;
            model.ShowWeight = _catalogSettings.ShowWeight;
            model.ShowGtin = _catalogSettings.ShowGtin;
            model.Gtin = product.Gtin;
            model.HasSampleDownload = product.IsDownload && product.HasSampleDownload;
            model.IsCurrentCustomerRegistered = customer.IsRegistered();
            model.IsBasePriceEnabled = product.BasePriceEnabled;
            model.BasePriceInfo = product.GetBasePriceInfo(_localizationService, _priceFormatter, _currencyService, _taxService, _priceCalculationService, currency);
            model.ShowLegalInfo = _taxSettings.ShowLegalHintsInProductDetails;
            model.BundleTitleText = product.GetLocalized(x => x.BundleTitleText);
            model.BundlePerItemPricing = product.BundlePerItemPricing;
            model.BundlePerItemShipping = product.BundlePerItemShipping;
            model.BundlePerItemShoppingCart = product.BundlePerItemShoppingCart;

            //_taxSettings.TaxDisplayType == TaxDisplayType.ExcludingTax;

            var taxDisplayType = _services.WorkContext.GetTaxDisplayTypeFor(customer, store.Id);
            string taxInfo = T(taxDisplayType == TaxDisplayType.IncludingTax ? "Tax.InclVAT" : "Tax.ExclVAT");

            var defaultTaxRate = "";
            var taxrate = Convert.ToString(_taxService.GetTaxRate(product, customer));
            if (_taxSettings.DisplayTaxRates && !taxrate.Equals("0", StringComparison.InvariantCultureIgnoreCase))
            {
                defaultTaxRate = "({0}%)".FormatWith(taxrate);
            }

            var additionalShippingCosts = String.Empty;
            var addShippingPrice = _currencyService.ConvertFromPrimaryStoreCurrency(product.AdditionalShippingCharge, currency);

            if (addShippingPrice > 0)
            {
                additionalShippingCosts = T("Common.AdditionalShippingSurcharge").Text.FormatInvariant(_priceFormatter.FormatPrice(addShippingPrice, true, false)) + ", ";
            }

            if (!product.IsShipEnabled || (addShippingPrice == 0 && product.IsFreeShipping))
            {
                model.LegalInfo += "{0} {1}, {2}".FormatInvariant(
                    product.IsTaxExempt ? "" : taxInfo,
                    product.IsTaxExempt ? "" : defaultTaxRate,
                    T("Common.FreeShipping"));
            }
            else
            {
                var topic = _topicService.Value.GetTopicBySystemName("ShippingInfo", store.Id);

                if (topic == null)
                {
                    model.LegalInfo = T("Tax.LegalInfoProductDetail2",
                        product.IsTaxExempt ? "" : taxInfo,
                        product.IsTaxExempt ? "" : defaultTaxRate,
                        additionalShippingCosts);
                }
                else
                {
                    model.LegalInfo = T("Tax.LegalInfoProductDetail",
                        product.IsTaxExempt ? "" : taxInfo,
                        product.IsTaxExempt ? "" : defaultTaxRate,
                        additionalShippingCosts,
                        _urlHelper.RouteUrl("Topic", new { SystemName = "shippinginfo" }));
                }
            }

            var dimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).Name;

            model.WeightValue = product.Weight;
            if (!isBundle)
            {
                if (selectedAttributeValues != null)
                {
                    foreach (var attributeValue in selectedAttributeValues)
                        model.WeightValue = decimal.Add(model.WeightValue, attributeValue.WeightAdjustment);
                }
                else
                {
                    model.WeightValue = decimal.Add(model.WeightValue, preSelectedWeightAdjustment);
                }
            }

            model.Weight = (model.WeightValue > 0) ? "{0} {1}".FormatCurrent(model.WeightValue.ToString("F2"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name) : "";
            model.Height = (product.Height > 0) ? "{0} {1}".FormatCurrent(product.Height.ToString("F2"), dimension) : "";
            model.Length = (product.Length > 0) ? "{0} {1}".FormatCurrent(product.Length.ToString("F2"), dimension) : "";
            model.Width = (product.Width > 0) ? "{0} {1}".FormatCurrent(product.Width.ToString("F2"), dimension) : "";

            if (productBundleItem != null)
                model.ThumbDimensions = _mediaSettings.BundledProductPictureSize;
            else if (isAssociatedProduct)
                model.ThumbDimensions = _mediaSettings.AssociatedProductPictureSize;

            if (model.IsAvailable)
            {
                var deliveryTime = _deliveryTimeService.GetDeliveryTime(product);
                if (deliveryTime != null)
                {
                    model.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name);
                    model.DeliveryTimeHexValue = deliveryTime.ColorHexValue;
                }
            }

            model.DisplayDeliveryTime = _catalogSettings.ShowDeliveryTimesInProductDetail;
            model.IsShipEnabled = product.IsShipEnabled;
            model.DisplayDeliveryTimeAccordingToStock = product.DisplayDeliveryTimeAccordingToStock(_catalogSettings);

            if (model.DeliveryTimeName.IsEmpty() && model.DisplayDeliveryTime)
            {
                model.DeliveryTimeName = T("ShoppingCart.NotAvailable");
            }

            var quantityUnit = _quantityUnitService.GetQuantityUnit(product);
            if (quantityUnit != null)
            {
                model.QuantityUnitName = quantityUnit.GetLocalized(x => x.Name);
            }

            //back in stock subscriptions)
            if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock &&
                product.BackorderMode == BackorderMode.NoBackorders &&
                product.AllowBackInStockSubscriptions &&
                product.StockQuantity <= 0)
            {
                //out of stock
                model.DisplayBackInStockSubscription = true;
                model.BackInStockAlreadySubscribed = _backInStockSubscriptionService.FindSubscription(customer.Id, product.Id, store.Id) != null;
            }

            #endregion

            #region Product price

            model.ProductPrice.ProductId = product.Id;

            if (displayPrices)
            {
                model.ProductPrice.HidePrices = false;

                if (product.CustomerEntersPrice && !isBundleItemPricing)
                {
                    model.ProductPrice.CustomerEntersPrice = true;
                }
                else
                {
                    if (product.CallForPrice && !isBundleItemPricing)
                    {
                        model.ProductPrice.CallForPrice = true;
                    }
                    else
                    {
                        decimal taxRate = decimal.Zero;
                        decimal oldPrice = decimal.Zero;
                        decimal finalPriceWithoutDiscountBase = decimal.Zero;
                        decimal finalPriceWithDiscountBase = decimal.Zero;
                        decimal attributesTotalPriceBase = decimal.Zero;
                        decimal finalPriceWithoutDiscount = decimal.Zero;
                        decimal finalPriceWithDiscount = decimal.Zero;

                        decimal oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate);

                        if (model.ProductPrice.DynamicPriceUpdate && !isBundlePricing)
                        {
                            if (selectedAttributeValues != null)
                            {
                                selectedAttributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(x));
                            }
                            else
                            {
                                attributesTotalPriceBase = preSelectedPriceAdjustmentBase;
                            }
                        }

                        if (productBundleItem != null)
                        {
                            productBundleItem.AdditionalCharge = attributesTotalPriceBase;
                        }

                        finalPriceWithoutDiscountBase = _priceCalculationService.GetFinalPrice(product, productBundleItems,
                            customer, attributesTotalPriceBase, false, selectedQuantity, productBundleItem);

                        finalPriceWithDiscountBase = _priceCalculationService.GetFinalPrice(product, productBundleItems,
                            customer, attributesTotalPriceBase, true, selectedQuantity, productBundleItem);

                        finalPriceWithoutDiscountBase = _taxService.GetProductPrice(product, finalPriceWithoutDiscountBase, out taxRate);
                        finalPriceWithDiscountBase = _taxService.GetProductPrice(product, finalPriceWithDiscountBase, out taxRate);

                        oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, _services.WorkContext.WorkingCurrency);

                        finalPriceWithoutDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithoutDiscountBase, currency);
                        finalPriceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscountBase, currency);

                        if (productBundleItem == null || isBundleItemPricing)
                        {
                            if (finalPriceWithoutDiscountBase != oldPriceBase && oldPriceBase > decimal.Zero)
                                model.ProductPrice.OldPrice = _priceFormatter.FormatPrice(oldPrice);

                            model.ProductPrice.Price = _priceFormatter.FormatPrice(finalPriceWithoutDiscount);

                            if (finalPriceWithoutDiscountBase != finalPriceWithDiscountBase)
                                model.ProductPrice.PriceWithDiscount = _priceFormatter.FormatPrice(finalPriceWithDiscount);
                        }

                        model.ProductPrice.PriceValue = finalPriceWithoutDiscount;
                        model.ProductPrice.PriceWithDiscountValue = finalPriceWithDiscount;
                        model.BasePriceInfo = product.GetBasePriceInfo(
                            _localizationService,
                            _priceFormatter,
                            _currencyService,
                            _taxService,
                            _priceCalculationService,
                            currency,
                            attributesTotalPriceBase);

                        if (!string.IsNullOrWhiteSpace(model.ProductPrice.OldPrice) || !string.IsNullOrWhiteSpace(model.ProductPrice.PriceWithDiscount))
                        {
                            model.ProductPrice.NoteWithoutDiscount = T(isBundle && product.BundlePerItemPricing ? "Products.Bundle.PriceWithoutDiscount.Note" : "Products.Price");
                        }

                        if ((isBundle && product.BundlePerItemPricing && !string.IsNullOrWhiteSpace(model.ProductPrice.PriceWithDiscount)) || product.HasTierPrices)
                        {
                            if (!product.HasTierPrices)
                            {
                                model.ProductPrice.NoteWithDiscount = T("Products.Bundle.PriceWithDiscount.Note");
                            }

                            model.BasePriceInfo = product.GetBasePriceInfo(
                                _localizationService,
                                _priceFormatter,
                                _currencyService,
                                _taxService,
                                _priceCalculationService,
                                currency,
                                (product.Price - finalPriceWithDiscount) * (-1));
                        }
                    }
                }
            }
            else
            {
                model.ProductPrice.HidePrices = true;
                model.ProductPrice.OldPrice = null;
                model.ProductPrice.Price = null;
            }
            #endregion

            #region 'Add to cart' model

            model.AddToCart.ProductId = product.Id;

            //quantity
            model.AddToCart.EnteredQuantity = product.OrderMinimumQuantity;

            //'add to cart', 'add to wishlist' buttons
            model.AddToCart.DisableBuyButton = product.DisableBuyButton || !_services.Permissions.Authorize(StandardPermissionProvider.EnableShoppingCart);
            model.AddToCart.DisableWishlistButton = product.DisableWishlistButton || !_services.Permissions.Authorize(StandardPermissionProvider.EnableWishlist);
            if (!displayPrices)
            {
                model.AddToCart.DisableBuyButton = true;
                model.AddToCart.DisableWishlistButton = true;
            }
            //pre-order
            model.AddToCart.AvailableForPreOrder = product.AvailableForPreOrder;

            //customer entered price
            model.AddToCart.CustomerEntersPrice = product.CustomerEntersPrice;
            if (model.AddToCart.CustomerEntersPrice)
            {
                var minimumCustomerEnteredPrice = _currencyService.ConvertFromPrimaryStoreCurrency(product.MinimumCustomerEnteredPrice, currency);
                var maximumCustomerEnteredPrice = _currencyService.ConvertFromPrimaryStoreCurrency(product.MaximumCustomerEnteredPrice, currency);

                model.AddToCart.CustomerEnteredPrice = minimumCustomerEnteredPrice;

                model.AddToCart.CustomerEnteredPriceRange = string.Format(T("Products.EnterProductPrice.Range"),
                    _priceFormatter.FormatPrice(minimumCustomerEnteredPrice, true, false),
                    _priceFormatter.FormatPrice(maximumCustomerEnteredPrice, true, false));
            }

            //allowed quantities
            var allowedQuantities = product.ParseAllowedQuatities();
            foreach (var qty in allowedQuantities)
            {
                model.AddToCart.AllowedQuantities.Add(new SelectListItem
                {
                    Text = qty.ToString(),
                    Value = qty.ToString()
                });
            }

            #endregion

            #region Gift card

            model.GiftCard.IsGiftCard = product.IsGiftCard;
            if (model.GiftCard.IsGiftCard)
            {
                model.GiftCard.GiftCardType = product.GiftCardType;
                model.GiftCard.SenderName = customer.GetFullName();
                model.GiftCard.SenderEmail = customer.Email;
            }

            #endregion

            return model;
        }
示例#4
0
        private void WriteItem(FeedFileCreationContext fileCreation, XmlWriter writer, Product product, Currency currency, string measureWeightSystemKey)
        {
            GoogleProductRecord googleProduct = null;

            try
            {
                googleProduct = GetGoogleProductRecord(product.Id);

                if (googleProduct != null && !googleProduct.Export)
                    return;
            }
            catch (Exception exc)
            {
                fileCreation.Logger.Error(exc.Message, exc);
            }

            writer.WriteStartElement("item");

            try
            {
                var manu = _manufacturerService.GetProductManufacturersByProductId(product.Id).FirstOrDefault();
                var mainImageUrl = Helper.GetMainProductImageUrl(fileCreation.Store, product);
                var category = ProductCategory(googleProduct);

                if (category.IsEmpty())
                    fileCreation.ErrorMessage = Helper.GetResource("MissingDefaultCategory");

                string manuName = (manu != null ? manu.Manufacturer.GetLocalized(x => x.Name, Settings.LanguageId, true, false) : null);
                string productName = product.GetLocalized(x => x.Name, Settings.LanguageId, true, false);
                string shortDescription = product.GetLocalized(x => x.ShortDescription, Settings.LanguageId, true, false);
                string fullDescription = product.GetLocalized(x => x.FullDescription, Settings.LanguageId, true, false);

                var brand = (manuName ?? Settings.Brand);
                var mpn = Helper.GetManufacturerPartNumber(product);

                bool identifierExists = product.Gtin.HasValue() || brand.HasValue() || mpn.HasValue();

                writer.WriteElementString("g", "id", _googleNamespace, product.Id.ToString());

                writer.WriteStartElement("title");
                writer.WriteCData(productName.Truncate(70));
                writer.WriteEndElement();

                var description = Helper.BuildProductDescription(productName, shortDescription, fullDescription, manuName, d =>
                {
                    if (fullDescription.IsEmpty() && shortDescription.IsEmpty())
                    {
                        var rnd = new Random();

                        switch (rnd.Next(1, 5))
                        {
                            case 1: return d.Grow(Settings.AppendDescriptionText1, " ");
                            case 2: return d.Grow(Settings.AppendDescriptionText2, " ");
                            case 3: return d.Grow(Settings.AppendDescriptionText3, " ");
                            case 4: return d.Grow(Settings.AppendDescriptionText4, " ");
                            case 5: return d.Grow(Settings.AppendDescriptionText5, " ");
                        }
                    }
                    return d;
                });

                writer.WriteStartElement("description");
                writer.WriteCData(description.RemoveInvalidXmlChars());
                writer.WriteEndElement();

                writer.WriteStartElement("g", "google_product_category", _googleNamespace);
                writer.WriteCData(category);
                writer.WriteFullEndElement();

                string productType = Helper.GetCategoryPath(product);
                if (productType.HasValue())
                {
                    writer.WriteStartElement("g", "product_type", _googleNamespace);
                    writer.WriteCData(productType);
                    writer.WriteFullEndElement();
                }

                writer.WriteElementString("link", Helper.GetProductDetailUrl(fileCreation.Store, product));
                writer.WriteElementString("g", "image_link", _googleNamespace, mainImageUrl);

                foreach (string additionalImageUrl in Helper.GetAdditionalProductImages(fileCreation.Store, product, mainImageUrl))
                {
                    writer.WriteElementString("g", "additional_image_link", _googleNamespace, additionalImageUrl);
                }

                writer.WriteElementString("g", "condition", _googleNamespace, Condition());
                writer.WriteElementString("g", "availability", _googleNamespace, Availability(product));

                decimal price = Helper.GetProductPrice(product, currency, fileCreation.Store);
                string specialPriceDate;

                if (SpecialPrice(product, out specialPriceDate))
                {
                    writer.WriteElementString("g", "sale_price", _googleNamespace, price.FormatInvariant() + " " + currency.CurrencyCode);
                    writer.WriteElementString("g", "sale_price_effective_date", _googleNamespace, specialPriceDate);

                    // get regular price ignoring any special price
                    decimal specialPrice = product.SpecialPrice.Value;
                    product.SpecialPrice = null;
                    price = Helper.GetProductPrice(product, currency, fileCreation.Store);
                    product.SpecialPrice = specialPrice;

                    _dbContext.SetToUnchanged<Product>(product);
                }

                writer.WriteElementString("g", "price", _googleNamespace, price.FormatInvariant() + " " + currency.CurrencyCode);

                writer.WriteCData("gtin", product.Gtin, "g", _googleNamespace);
                writer.WriteCData("brand", brand, "g", _googleNamespace);
                writer.WriteCData("mpn", mpn, "g", _googleNamespace);

                writer.WriteCData("gender", Gender(googleProduct), "g", _googleNamespace);
                writer.WriteCData("age_group", AgeGroup(googleProduct), "g", _googleNamespace);
                writer.WriteCData("color", Color(googleProduct), "g", _googleNamespace);
                writer.WriteCData("size", Size(googleProduct), "g", _googleNamespace);
                writer.WriteCData("material", Material(googleProduct), "g", _googleNamespace);
                writer.WriteCData("pattern", Pattern(googleProduct), "g", _googleNamespace);
                writer.WriteCData("item_group_id", ItemGroupId(googleProduct), "g", _googleNamespace);

                writer.WriteElementString("g", "online_only", _googleNamespace, Settings.OnlineOnly ? "y" : "n");
                writer.WriteElementString("g", "identifier_exists", _googleNamespace, identifierExists ? "TRUE" : "FALSE");

                if (Settings.ExpirationDays > 0)
                {
                    writer.WriteElementString("g", "expiration_date", _googleNamespace, DateTime.UtcNow.AddDays(Settings.ExpirationDays).ToString("yyyy-MM-dd"));
                }

                if (Settings.ExportShipping)
                {
                    string weightInfo, weight = product.Weight.FormatInvariant();

                    if (measureWeightSystemKey.IsCaseInsensitiveEqual("gram"))
                        weightInfo = weight + " g";
                    else if (measureWeightSystemKey.IsCaseInsensitiveEqual("lb"))
                        weightInfo = weight + " lb";
                    else if (measureWeightSystemKey.IsCaseInsensitiveEqual("ounce"))
                        weightInfo = weight + " oz";
                    else
                        weightInfo = weight + " kg";

                    writer.WriteElementString("g", "shipping_weight", _googleNamespace, weightInfo);
                }

                if (Settings.ExportBasePrice && product.BasePriceHasValue)
                {
                    string measureUnit = BasePriceUnits(product.BasePriceMeasureUnit);

                    if (BasePriceSupported(product.BasePriceBaseAmount ?? 0, measureUnit))
                    {
                        string basePriceMeasure = "{0} {1}".FormatWith((product.BasePriceAmount ?? decimal.Zero).FormatInvariant(), measureUnit);
                        string basePriceBaseMeasure = "{0} {1}".FormatWith(product.BasePriceBaseAmount, measureUnit);

                        writer.WriteElementString("g", "unit_pricing_measure", _googleNamespace, basePriceMeasure);
                        writer.WriteElementString("g", "unit_pricing_base_measure", _googleNamespace, basePriceBaseMeasure);
                    }
                }
            }
            catch (Exception exc)
            {
                fileCreation.Logger.Error(exc.Message, exc);
            }

            writer.WriteEndElement(); // item
        }
        public void PrepareProductReviewsModel(ProductReviewsModel model, Product product)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            if (model == null)
                throw new ArgumentNullException("model");

            model.ProductId = product.Id;
            model.ProductName = product.GetLocalized(x => x.Name);
            model.ProductSeName = product.GetSeName();

            var productReviews = product.ProductReviews.Where(pr => pr.IsApproved).OrderBy(pr => pr.CreatedOnUtc);
            foreach (var pr in productReviews)
            {
                model.Items.Add(new ProductReviewModel()
                {
                    Id = pr.Id,
                    CustomerId = pr.CustomerId,
                    CustomerName = pr.Customer.FormatUserName(),
                    AllowViewingProfiles = _customerSettings.AllowViewingProfiles && pr.Customer != null && !pr.Customer.IsGuest(),
                    Title = pr.Title,
                    ReviewText = pr.ReviewText,
                    Rating = pr.Rating,
                    Helpfulness = new ProductReviewHelpfulnessModel()
                    {
                        ProductReviewId = pr.Id,
                        HelpfulYesTotal = pr.HelpfulYesTotal,
                        HelpfulNoTotal = pr.HelpfulNoTotal,
                    },
                    WrittenOnStr = _dateTimeHelper.ConvertToUserTime(pr.CreatedOnUtc, DateTimeKind.Utc).ToString("g"),
                });
            }

            model.AddProductReview.CanCurrentCustomerLeaveReview = _catalogSettings.AllowAnonymousUsersToReviewProduct || !_services.WorkContext.CurrentCustomer.IsGuest();
            model.AddProductReview.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnProductReviewPage;
        }
        public ProductDetailsModel PrepareProductDetailsPageModel(
			Product product, 
			bool isAssociatedProduct = false,
			ProductBundleItemData productBundleItem = null, 
			IList<ProductBundleItemData> productBundleItems = null, 
			NameValueCollection selectedAttributes = null,
			NameValueCollection queryData = null)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            var model = new ProductDetailsModel
            {
                Id = product.Id,
                Name = product.GetLocalized(x => x.Name),
                ShortDescription = product.GetLocalized(x => x.ShortDescription),
                FullDescription = product.GetLocalized(x => x.FullDescription),
                MetaKeywords = product.GetLocalized(x => x.MetaKeywords),
                MetaDescription = product.GetLocalized(x => x.MetaDescription),
                MetaTitle = product.GetLocalized(x => x.MetaTitle),
                SeName = product.GetSeName(),
                ProductType = product.ProductType,
                VisibleIndividually = product.VisibleIndividually,
                //Manufacturers = _manufacturerService.GetProductManufacturersByProductId(product.Id),
                Manufacturers = PrepareManufacturersOverviewModel(_manufacturerService.GetProductManufacturersByProductId(product.Id), null, true),
                ReviewCount = product.ApprovedTotalReviews,
                DisplayAdminLink = _services.Permissions.Authorize(StandardPermissionProvider.AccessAdminPanel),
                //EnableHtmlTextCollapser = Convert.ToBoolean(_settingService.GetSettingByKey<string>("CatalogSettings.EnableHtmlTextCollapser")),
                //HtmlTextCollapsedHeight = Convert.ToString(_settingService.GetSettingByKey<string>("CatalogSettings.HtmlTextCollapsedHeight")),
                ShowSku = _catalogSettings.ShowProductSku,
                Sku = product.Sku,
                ShowManufacturerPartNumber = _catalogSettings.ShowManufacturerPartNumber,
                DisplayProductReviews = _catalogSettings.ShowProductReviewsInProductDetail,
                ManufacturerPartNumber = product.ManufacturerPartNumber,
                ShowGtin = _catalogSettings.ShowGtin,
                Gtin = product.Gtin,
                StockAvailability = product.FormatStockMessage(_localizationService),
                HasSampleDownload = product.IsDownload && product.HasSampleDownload,
                IsCurrentCustomerRegistered = _services.WorkContext.CurrentCustomer.IsRegistered()
            };

            // get gift card values from query string
            if (queryData != null && queryData.Count > 0)
            {
                var giftCardItems = queryData.AllKeys
                    .Where(x => x.EmptyNull().StartsWith("giftcard_"))
                    .SelectMany(queryData.GetValues, (k, v) => new { key = k, value = v.TrimSafe() });

                foreach (var item in giftCardItems)
                {
                    var key = item.key.EmptyNull().ToLower();

                    if (key.EndsWith("recipientname"))
                        model.GiftCard.RecipientName = item.value;
                    else if (key.EndsWith("recipientemail"))
                        model.GiftCard.RecipientEmail = item.value;
                    else if (key.EndsWith("sendername"))
                        model.GiftCard.SenderName = item.value;
                    else if (key.EndsWith("senderemail"))
                        model.GiftCard.SenderEmail = item.value;
                    else if (key.EndsWith("message"))
                        model.GiftCard.Message = item.value;
                }
            }

            // Back in stock subscriptions
            if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock &&
                 product.BackorderMode == BackorderMode.NoBackorders &&
                 product.AllowBackInStockSubscriptions &&
                 product.StockQuantity <= 0)
            {
                //out of stock
                model.DisplayBackInStockSubscription = true;
                model.BackInStockAlreadySubscribed = _backInStockSubscriptionService
                    .FindSubscription(_services.WorkContext.CurrentCustomer.Id, product.Id, _services.StoreContext.CurrentStore.Id) != null;
            }

            //template
            var templateCacheKey = string.Format(ModelCacheEventConsumer.PRODUCT_TEMPLATE_MODEL_KEY, product.ProductTemplateId);
            model.ProductTemplateViewPath = _services.Cache.Get(templateCacheKey, () =>
            {
                var template = _productTemplateService.GetProductTemplateById(product.ProductTemplateId);
                if (template == null)
                    template = _productTemplateService.GetAllProductTemplates().FirstOrDefault();
                return template.ViewPath;
            });

            IList<ProductBundleItemData> bundleItems = null;
            ProductVariantAttributeCombination combination = null;

            if (product.ProductType == ProductType.GroupedProduct && !isAssociatedProduct)	// associated products
            {
                var searchContext = new ProductSearchContext
                {
                    OrderBy = ProductSortingEnum.Position,
                    StoreId = _services.StoreContext.CurrentStore.Id,
                    ParentGroupedProductId = product.Id,
                    PageSize = int.MaxValue,
                    VisibleIndividuallyOnly = false
                };

                var associatedProducts = _productService.SearchProducts(searchContext);

                foreach (var associatedProduct in associatedProducts)
                    model.AssociatedProducts.Add(PrepareProductDetailsPageModel(associatedProduct, true));
            }
            else if (product.ProductType == ProductType.BundledProduct && productBundleItem == null)		// bundled items
            {
                bundleItems = _productService.GetBundleItems(product.Id);

                foreach (var itemData in bundleItems.Where(x => x.Item.Product.CanBeBundleItem()))
                {
                    var item = itemData.Item;
                    var bundleItemAttributes = new NameValueCollection();

                    if (selectedAttributes != null)
                    {
                        var keyPrefix = "product_attribute_{0}_{1}".FormatInvariant(item.ProductId, item.Id);

                        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;
                    bundledProductModel.BundleItem.HideThumbnail = item.HideThumbnail;
                    bundledProductModel.BundleItem.Visible = item.Visible;
                    bundledProductModel.BundleItem.IsBundleItemPricing = item.BundleProduct.BundlePerItemPricing;

                    var bundleItemName = item.GetLocalized(x => x.Name);
                    if (bundleItemName.HasValue())
                        bundledProductModel.Name = bundleItemName;

                    var bundleItemShortDescription = item.GetLocalized(x => x.ShortDescription);
                    if (bundleItemShortDescription.HasValue())
                        bundledProductModel.ShortDescription = bundleItemShortDescription;

                    model.BundledItems.Add(bundledProductModel);
                }
            }

            model = PrepareProductDetailModel(model, product, isAssociatedProduct, productBundleItem, bundleItems, selectedAttributes);

            IList<int> combinationPictureIds = null;

            if (productBundleItem == null)
            {
                combinationPictureIds = _productAttributeService.GetAllProductVariantAttributeCombinationPictureIds(product.Id);
                if (combination == null && model.SelectedCombination != null)
                    combination = model.SelectedCombination;
            }

            // pictures
            var pictures = _pictureService.GetPicturesByProductId(product.Id);
            PrepareProductDetailsPictureModel(model.DetailsPictureModel, pictures, model.Name, combinationPictureIds, isAssociatedProduct, productBundleItem, combination);

            return model;
        }
        /// <summary>
        /// Create a copy of product with all depended data
        /// </summary>
        /// <param name="product">The product</param>
        /// <param name="newName">The name of product duplicate</param>
        /// <param name="isPublished">A value indicating whether the product duplicate should be published</param>
        /// <param name="copyImages">A value indicating whether the product images should be copied</param>
        /// <param name="copyAssociatedProducts">A value indicating whether the copy associated products</param>
        /// <returns>Product entity</returns>
        public virtual Product CopyProduct(Product product, string newName, bool isPublished, bool copyImages, bool copyAssociatedProducts = true)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            if (String.IsNullOrEmpty(newName))
                throw new ArgumentException("Product name is required");

            Product productCopy = null;
            var utcNow = DateTime.UtcNow;

            // product download & sample download
            int downloadId = 0;
            int? sampleDownloadId = null;

            if (product.IsDownload)
            {
                var download = _downloadService.GetDownloadById(product.DownloadId);
                if (download != null)
                {
                    var downloadCopy = new Download
                    {
                        DownloadGuid = Guid.NewGuid(),
                        UseDownloadUrl = download.UseDownloadUrl,
                        DownloadUrl = download.DownloadUrl,
                        DownloadBinary = download.DownloadBinary,
                        ContentType = download.ContentType,
                        Filename = download.Filename,
                        Extension = download.Extension,
                        IsNew = download.IsNew,
                    };

                    _downloadService.InsertDownload(downloadCopy);
                    downloadId = downloadCopy.Id;
                }

                if (product.HasSampleDownload)
                {
                    var sampleDownload = _downloadService.GetDownloadById(product.SampleDownloadId.GetValueOrDefault());
                    if (sampleDownload != null)
                    {
                        var sampleDownloadCopy = new Download
                        {
                            DownloadGuid = Guid.NewGuid(),
                            UseDownloadUrl = sampleDownload.UseDownloadUrl,
                            DownloadUrl = sampleDownload.DownloadUrl,
                            DownloadBinary = sampleDownload.DownloadBinary,
                            ContentType = sampleDownload.ContentType,
                            Filename = sampleDownload.Filename,
                            Extension = sampleDownload.Extension,
                            IsNew = sampleDownload.IsNew
                        };

                        _downloadService.InsertDownload(sampleDownloadCopy);
                        sampleDownloadId = sampleDownloadCopy.Id;
                    }
                }
            }

            // product
            productCopy = new Product
            {
                ProductTypeId = product.ProductTypeId,
                ParentGroupedProductId = product.ParentGroupedProductId,
                VisibleIndividually = product.VisibleIndividually,
                Name = newName,
                ShortDescription = product.ShortDescription,
                FullDescription = product.FullDescription,
                ProductTemplateId = product.ProductTemplateId,
                AdminComment = product.AdminComment,
                ShowOnHomePage = product.ShowOnHomePage,
                HomePageDisplayOrder = product.HomePageDisplayOrder,
                MetaKeywords = product.MetaKeywords,
                MetaDescription = product.MetaDescription,
                MetaTitle = product.MetaTitle,
                AllowCustomerReviews = product.AllowCustomerReviews,
                LimitedToStores = product.LimitedToStores,
                Sku = product.Sku,
                ManufacturerPartNumber = product.ManufacturerPartNumber,
                Gtin = product.Gtin,
                IsGiftCard = product.IsGiftCard,
                GiftCardType = product.GiftCardType,
                RequireOtherProducts = product.RequireOtherProducts,
                RequiredProductIds = product.RequiredProductIds,
                AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts,
                IsDownload = product.IsDownload,
                DownloadId = downloadId,
                UnlimitedDownloads = product.UnlimitedDownloads,
                MaxNumberOfDownloads = product.MaxNumberOfDownloads,
                DownloadExpirationDays = product.DownloadExpirationDays,
                DownloadActivationType = product.DownloadActivationType,
                HasSampleDownload = product.HasSampleDownload,
                SampleDownloadId = sampleDownloadId,
                HasUserAgreement = product.HasUserAgreement,
                UserAgreementText = product.UserAgreementText,
                IsRecurring = product.IsRecurring,
                RecurringCycleLength = product.RecurringCycleLength,
                RecurringCyclePeriod = product.RecurringCyclePeriod,
                RecurringTotalCycles = product.RecurringTotalCycles,
                IsShipEnabled = product.IsShipEnabled,
                IsFreeShipping = product.IsFreeShipping,
                AdditionalShippingCharge = product.AdditionalShippingCharge,
                IsEsd = product.IsEsd,
                IsTaxExempt = product.IsTaxExempt,
                TaxCategoryId = product.TaxCategoryId,
                ManageInventoryMethod = product.ManageInventoryMethod,
                StockQuantity = product.StockQuantity,
                DisplayStockAvailability = product.DisplayStockAvailability,
                DisplayStockQuantity = product.DisplayStockQuantity,
                MinStockQuantity = product.MinStockQuantity,
                LowStockActivityId = product.LowStockActivityId,
                NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow,
                BackorderMode = product.BackorderMode,
                AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions,
                OrderMinimumQuantity = product.OrderMinimumQuantity,
                OrderMaximumQuantity = product.OrderMaximumQuantity,
                AllowedQuantities = product.AllowedQuantities,
                DisableBuyButton = product.DisableBuyButton,
                DisableWishlistButton = product.DisableWishlistButton,
                AvailableForPreOrder = product.AvailableForPreOrder,
                CallForPrice = product.CallForPrice,
                Price = product.Price,
                OldPrice = product.OldPrice,
                ProductCost = product.ProductCost,
                SpecialPrice = product.SpecialPrice,
                SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc,
                SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc,
                CustomerEntersPrice = product.CustomerEntersPrice,
                MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice,
                MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice,
                LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice,
                Weight = product.Weight,
                Length = product.Length,
                Width = product.Width,
                Height = product.Height,
                AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc,
                AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc,
                DisplayOrder = product.DisplayOrder,
                Published = isPublished,
                Deleted = product.Deleted,
                CreatedOnUtc = utcNow,
                UpdatedOnUtc = utcNow,
                DeliveryTimeId = product.DeliveryTimeId,
                QuantityUnitId = product.QuantityUnitId,
                BasePriceEnabled = product.BasePriceEnabled,
                BasePriceMeasureUnit = product.BasePriceMeasureUnit,
                BasePriceAmount = product.BasePriceAmount,
                BasePriceBaseAmount = product.BasePriceBaseAmount,
                BundleTitleText = product.BundleTitleText,
                BundlePerItemShipping = product.BundlePerItemShipping,
                BundlePerItemPricing = product.BundlePerItemPricing,
                BundlePerItemShoppingCart = product.BundlePerItemShoppingCart
            };

            _productService.InsertProduct(productCopy);

            //search engine name
            _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", productCopy.Name, true), 0);

            var languages = _languageService.GetAllLanguages(true);

            //localization
            foreach (var lang in languages)
            {
                var name = product.GetLocalized(x => x.Name, lang.Id, false, false);
                if (!String.IsNullOrEmpty(name))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.Name, name, lang.Id);

                var shortDescription = product.GetLocalized(x => x.ShortDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(shortDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.ShortDescription, shortDescription, lang.Id);

                var fullDescription = product.GetLocalized(x => x.FullDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(fullDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.FullDescription, fullDescription, lang.Id);

                var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaKeywords))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaKeywords, metaKeywords, lang.Id);

                var metaDescription = product.GetLocalized(x => x.MetaDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaDescription, metaDescription, lang.Id);

                var metaTitle = product.GetLocalized(x => x.MetaTitle, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaTitle))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaTitle, metaTitle, lang.Id);

                var bundleTitleText = product.GetLocalized(x => x.BundleTitleText, lang.Id, false, false);
                if (!String.IsNullOrEmpty(bundleTitleText))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.BundleTitleText, bundleTitleText, lang.Id);

                //search engine name
                _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", name, false), lang.Id);

            }

            // product pictures
            if (copyImages)
            {
                foreach (var productPicture in product.ProductPictures)
                {
                    var picture = productPicture.Picture;
                    var pictureCopy = _pictureService.InsertPicture(
                        _pictureService.LoadPictureBinary(picture),
                        picture.MimeType,
                        _pictureService.GetPictureSeName(newName),
                        true,
                        false,
                        false);

                    _productService.InsertProductPicture(new ProductPicture
                    {
                        ProductId = productCopy.Id,
                        PictureId = pictureCopy.Id,
                        DisplayOrder = productPicture.DisplayOrder
                    });
                }
            }

            // product <-> categories mappings
            foreach (var productCategory in product.ProductCategories)
            {
                var productCategoryCopy = new ProductCategory
                {
                    ProductId = productCopy.Id,
                    CategoryId = productCategory.CategoryId,
                    IsFeaturedProduct = productCategory.IsFeaturedProduct,
                    DisplayOrder = productCategory.DisplayOrder
                };

                _categoryService.InsertProductCategory(productCategoryCopy);
            }

            // product <-> manufacturers mappings
            foreach (var productManufacturers in product.ProductManufacturers)
            {
                var productManufacturerCopy = new ProductManufacturer
                {
                    ProductId = productCopy.Id,
                    ManufacturerId = productManufacturers.ManufacturerId,
                    IsFeaturedProduct = productManufacturers.IsFeaturedProduct,
                    DisplayOrder = productManufacturers.DisplayOrder
                };

                _manufacturerService.InsertProductManufacturer(productManufacturerCopy);
            }

            // 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
                });
            }

            // product <-> cross sells mappings
            foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true))
            {
                _productService.InsertCrossSellProduct(new CrossSellProduct
                {
                    ProductId1 = productCopy.Id,
                    ProductId2 = csProduct.ProductId2,
                });
            }

            // product specifications
            foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes)
            {
                var psaCopy = new ProductSpecificationAttribute
                {
                    ProductId = productCopy.Id,
                    SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId,
                    AllowFiltering = productSpecificationAttribute.AllowFiltering,
                    ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage,
                    DisplayOrder = productSpecificationAttribute.DisplayOrder
                };

                _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy);
            }

            //store mapping
            var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product);
            foreach (var id in selectedStoreIds)
            {
                _storeMappingService.InsertStoreMapping(productCopy, id);
            }

            // product <-> attributes mappings
            var associatedAttributes = new Dictionary<int, int>();
            var associatedAttributeValues = new Dictionary<int, int>();

            foreach (var productVariantAttribute in _productAttributeService.GetProductVariantAttributesByProductId(product.Id))
            {
                var productVariantAttributeCopy = new ProductVariantAttribute
                {
                    ProductId = productCopy.Id,
                    ProductAttributeId = productVariantAttribute.ProductAttributeId,
                    TextPrompt = productVariantAttribute.TextPrompt,
                    IsRequired = productVariantAttribute.IsRequired,
                    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
                    {
                        ProductVariantAttributeId = productVariantAttributeCopy.Id,
                        Name = productVariantAttributeValue.Name,
                        ColorSquaresRgb = productVariantAttributeValue.ColorSquaresRgb,
                        PriceAdjustment = productVariantAttributeValue.PriceAdjustment,
                        WeightAdjustment = productVariantAttributeValue.WeightAdjustment,
                        IsPreSelected = productVariantAttributeValue.IsPreSelected,
                        DisplayOrder = productVariantAttributeValue.DisplayOrder,
                        ValueTypeId = productVariantAttributeValue.ValueTypeId,
                        LinkedProductId = productVariantAttributeValue.LinkedProductId,
                        Quantity = productVariantAttributeValue.Quantity,
                    };

                    _productAttributeService.InsertProductVariantAttributeValue(pvavCopy);

                    //save associated value (used for combinations copying)
                    associatedAttributeValues.Add(productVariantAttributeValue.Id, pvavCopy.Id);

                    //localization
                    foreach (var lang in languages)
                    {
                        var name = productVariantAttributeValue.GetLocalized(x => x.Name, lang.Id, false, false);
                        if (!String.IsNullOrEmpty(name))
                            _localizedEntityService.SaveLocalizedValue(pvavCopy, x => x.Name, name, lang.Id);
                    }
                }
            }

            // attribute combinations
            using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: false))
            {
                scope.LoadCollection(product, (Product p) => p.ProductVariantAttributeCombinations);
            }

            foreach (var combination in product.ProductVariantAttributeCombinations)
            {
                //generate new AttributesXml according to new value IDs
                string newAttributesXml = "";
                var parsedProductVariantAttributes = _productAttributeParser.ParseProductVariantAttributes(combination.AttributesXml);
                foreach (var oldPva in parsedProductVariantAttributes)
                {
                    if (associatedAttributes.ContainsKey(oldPva.Id))
                    {
                        int newPvaId = associatedAttributes[oldPva.Id];
                        var newPva = _productAttributeService.GetProductVariantAttributeById(newPvaId);
                        if (newPva != null)
                        {
                            var oldPvaValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldPva.Id);
                            foreach (var oldPvaValueStr in oldPvaValuesStr)
                            {
                                if (newPva.ShouldHaveValues())
                                {
                                    //attribute values
                                    int oldPvaValue = int.Parse(oldPvaValueStr);
                                    if (associatedAttributeValues.ContainsKey(oldPvaValue))
                                    {
                                        int newPvavId = associatedAttributeValues[oldPvaValue];
                                        var newPvav = _productAttributeService.GetProductVariantAttributeValueById(newPvavId);
                                        if (newPvav != null)
                                        {
                                            newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, newPvav.Id.ToString());
                                        }
                                    }
                                }
                                else
                                {
                                    //just a text
                                    newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, oldPvaValueStr);
                                }
                            }
                        }
                    }
                }
                var combinationCopy = new ProductVariantAttributeCombination
                {
                    ProductId = productCopy.Id,
                    AttributesXml = newAttributesXml,
                    StockQuantity = combination.StockQuantity,
                    AllowOutOfStockOrders = combination.AllowOutOfStockOrders,

                    // SmartStore extension
                    Sku = combination.Sku,
                    Gtin = combination.Gtin,
                    ManufacturerPartNumber = combination.ManufacturerPartNumber,
                    Price = combination.Price,
                    AssignedPictureIds = copyImages ? combination.AssignedPictureIds : null,
                    Length = combination.Length,
                    Width = combination.Width,
                    Height = combination.Height,
                    BasePriceAmount = combination.BasePriceAmount,
                    BasePriceBaseAmount = combination.BasePriceBaseAmount,
                    DeliveryTimeId = combination.DeliveryTimeId,
                    QuantityUnitId = combination.QuantityUnitId,
                    IsActive = combination.IsActive
                    //IsDefaultCombination = combination.IsDefaultCombination
                };
                _productAttributeService.InsertProductVariantAttributeCombination(combinationCopy);
            }

            // 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
                });
            }

            // product <-> discounts mapping
            foreach (var discount in product.AppliedDiscounts)
            {
                productCopy.AppliedDiscounts.Add(discount);
                _productService.UpdateProduct(productCopy);
            }

            // update "HasTierPrices" and "HasDiscountsApplied" properties
            _productService.UpdateHasTierPricesProperty(productCopy);
            _productService.UpdateLowestAttributeCombinationPriceProperty(productCopy);
            _productService.UpdateHasDiscountsApplied(productCopy);

            // associated products
            if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct)
            {
                var searchContext = new ProductSearchContext
                {
                    OrderBy = ProductSortingEnum.Position,
                    ParentGroupedProductId = product.Id,
                    PageSize = int.MaxValue,
                    ShowHidden = true
                };

                string copyOf = _localizationService.GetResource("Admin.Common.CopyOf");
                var associatedProducts = _productService.SearchProducts(searchContext);

                foreach (var associatedProduct in associatedProducts)
                {
                    var associatedProductCopy = CopyProduct(associatedProduct, string.Format("{0} {1}", copyOf, associatedProduct.Name), isPublished, copyImages, false);
                    associatedProductCopy.ParentGroupedProductId = productCopy.Id;

                    _productService.UpdateProduct(productCopy);
                }
            }

            // bundled products
            var bundledItems = _productService.GetBundleItems(product.Id, true);

            foreach (var bundleItem in bundledItems)
            {
                var newBundleItem = bundleItem.Item.Clone();
                newBundleItem.BundleProductId = productCopy.Id;
                newBundleItem.CreatedOnUtc = utcNow;
                newBundleItem.UpdatedOnUtc = utcNow;

                _productService.InsertBundleItem(newBundleItem);

                foreach (var itemFilter in bundleItem.Item.AttributeFilters)
                {
                    var newItemFilter = itemFilter.Clone();
                    newItemFilter.BundleItemId = newBundleItem.Id;

                    _productAttributeService.InsertProductBundleItemAttributeFilter(newItemFilter);
                }
            }

            return productCopy;
        }
		protected ProductDetailsModel PrepareProductDetailsPageModel(Product product, bool isAssociatedProduct = false,
			ProductBundleItemData productBundleItem = null, IList<ProductBundleItemData> productBundleItems = null, FormCollection selectedAttributes = null)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            var model = new ProductDetailsModel()
            {
                Id = product.Id,
                Name = product.GetLocalized(x => x.Name),
                ShortDescription = product.GetLocalized(x => x.ShortDescription),
                FullDescription = product.GetLocalized(x => x.FullDescription),
                MetaKeywords = product.GetLocalized(x => x.MetaKeywords),
                MetaDescription = product.GetLocalized(x => x.MetaDescription),
                MetaTitle = product.GetLocalized(x => x.MetaTitle),
                SeName = product.GetSeName(),
				ProductType = product.ProductType,
				VisibleIndividually = product.VisibleIndividually,
                //Manufacturers = _manufacturerService.GetProductManufacturersByProductId(product.Id),  /* codehint: sm-edit */
                Manufacturers = PrepareManufacturersOverviewModel(_manufacturerService.GetProductManufacturersByProductId(product.Id)),
                ReviewCount = product.ApprovedTotalReviews,                     /* codehint: sm-add */
                DisplayAdminLink = _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel),
                EnableHtmlTextCollapser = Convert.ToBoolean(_settingService.GetSettingByKey<string>("CatalogSettings.EnableHtmlTextCollapser")),
                HtmlTextCollapsedHeight = Convert.ToString(_settingService.GetSettingByKey<string>("CatalogSettings.HtmlTextCollapsedHeight")),
				ShowSku = _catalogSettings.ShowProductSku,
				Sku = product.Sku,
				ShowManufacturerPartNumber = _catalogSettings.ShowManufacturerPartNumber,
				ManufacturerPartNumber = product.ManufacturerPartNumber,
				ShowGtin = _catalogSettings.ShowGtin,
				Gtin = product.Gtin,
				StockAvailability = product.FormatStockMessage(_localizationService),
				HasSampleDownload = product.IsDownload && product.HasSampleDownload,
				IsCurrentCustomerRegistered = _workContext.CurrentCustomer.IsRegistered()
            };

			// Back in stock subscriptions
			if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock &&
				 product.BackorderMode == BackorderMode.NoBackorders &&
				 product.AllowBackInStockSubscriptions &&
				 product.StockQuantity <= 0)
			{
				//out of stock
				model.DisplayBackInStockSubscription = true;
				model.BackInStockAlreadySubscribed = _backInStockSubscriptionService
					.FindSubscription(_workContext.CurrentCustomer.Id, product.Id, _storeContext.CurrentStore.Id) != null;
			}

            //template
            var templateCacheKey = string.Format(ModelCacheEventConsumer.PRODUCT_TEMPLATE_MODEL_KEY, product.ProductTemplateId);
            model.ProductTemplateViewPath = _cacheManager.Get(templateCacheKey, () =>
            {
                var template = _productTemplateService.GetProductTemplateById(product.ProductTemplateId);
                if (template == null)
                    template = _productTemplateService.GetAllProductTemplates().FirstOrDefault();
                return template.ViewPath;
            });

			IList<ProductBundleItemData> bundleItems = null;
            ProductVariantAttributeCombination combination = null;
            var combinationImageIds = new List<int>();

			if (product.ProductType == ProductType.GroupedProduct && !isAssociatedProduct)	// associated products
			{
				var searchContext = new ProductSearchContext()
				{
					StoreId = _storeContext.CurrentStore.Id,
					ParentGroupedProductId = product.Id,
					VisibleIndividuallyOnly = false
				};

				var associatedProducts = _productService.SearchProducts(searchContext);

				foreach (var associatedProduct in associatedProducts)
					model.AssociatedProducts.Add(PrepareProductDetailsPageModel(associatedProduct, true));
			}
			else if (product.ProductType == ProductType.BundledProduct && productBundleItem == null)		// bundled items
			{
				bundleItems = _productService.GetBundleItems(product.Id);

				foreach (var itemData in bundleItems.Where(x => x.Item.Product.CanBeBundleItem()))
				{
					var item = itemData.Item;
					var bundledProductModel = PrepareProductDetailsPageModel(item.Product, false, itemData);

					bundledProductModel.BundleItem.Id = item.Id;
					bundledProductModel.BundleItem.Quantity = item.Quantity;
					bundledProductModel.BundleItem.HideThumbnail = item.HideThumbnail;
					bundledProductModel.BundleItem.Visible = item.Visible;
					bundledProductModel.BundleItem.IsBundleItemPricing = item.BundleProduct.BundlePerItemPricing;

					string bundleItemName = item.GetLocalized(x => x.Name);
					if (bundleItemName.HasValue())
						bundledProductModel.Name = bundleItemName;

					string bundleItemShortDescription = item.GetLocalized(x => x.ShortDescription);
					if (bundleItemShortDescription.HasValue())
						bundledProductModel.ShortDescription = bundleItemShortDescription;

					model.BundledItems.Add(bundledProductModel);
				}
			}

			model = PrepareProductDetailModel(model, product, isAssociatedProduct, productBundleItem, bundleItems, selectedAttributes);

			if (productBundleItem == null)
			{
				model.Combinations.GetAllCombinationImageIds(combinationImageIds);

				if (combination == null && model.CombinationSelected != null)
					combination = model.CombinationSelected;
			}

            // pictures
            var pictures = _pictureService.GetPicturesByProductId(product.Id);
			PrepareProductDetailsPictureModel(model.DetailsPictureModel, pictures, model.Name, combinationImageIds, isAssociatedProduct, productBundleItem, combination);

            return model;
        }
        public virtual void AddProductTokens(IList<Token> tokens, Product product, Language language)
        {
            // TODO: add a method for getting URL (use routing because it handles all SEO friendly URLs)
            var storeLocation = _webHelper.GetStoreLocation(false);
            var productUrl = storeLocation + product.GetSeName();
            var productName = product.GetLocalized(x => x.Name, language.Id);

            tokens.Add(new Token("Product.ID", product.Id.ToString()));
            tokens.Add(new Token("Product.Sku", product.Sku));
            tokens.Add(new Token("Product.Name", productName));
            tokens.Add(new Token("Product.ShortDescription", product.GetLocalized(x => x.ShortDescription, language.Id), true));
            tokens.Add(new Token("Product.StockQuantity", product.StockQuantity.ToString()));
            tokens.Add(new Token("Product.ProductURLForCustomer", productUrl, true));

            var currency = _workContext.WorkingCurrency;

            var additionalShippingCharge = _currencyService.ConvertFromPrimaryStoreCurrency(product.AdditionalShippingCharge, currency);
            var additionalShippingChargeFormatted = _priceFormatter.FormatPrice(additionalShippingCharge, false, currency.CurrencyCode, false, language);

            tokens.Add(new Token("Product.AdditionalShippingCharge", additionalShippingChargeFormatted));

            if (_mediaSettings.MessageProductThumbPictureSize > 0)
            {
                var pictureHtml = ProductPictureToHtml(GetPictureFor(product, null), language, productName, productUrl, storeLocation);

                tokens.Add(new Token("Product.Thumbnail", pictureHtml, true));
            }

            //event notification
            _eventPublisher.EntityTokensAdded(product, tokens);
        }