код класса:
//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.nopCommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------
using System;
using System.Xml;
using System.IO;
using System.Text;
using NopSolutions.NopCommerce.BusinessLogic.Caching;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Configuration.Settings;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Media;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.SEO;
using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;
using NopSolutions.NopCommerce.BusinessLogic.Tasks;
using Ionic.Zip;
namespace NopSolutions.NopCommerce.BusinessLogic.Utils
{
/// <summary>
///
/// </summary>
public partial class GenerateYMLTask : ITask
{
private const string DateFormat = @"yyyy-MM-dd HH:mm";
private const string ALSOGENERATEYML = "nop.AlsoGenerateYML";
private TimeSpan timeGenerate;
private ICacheManager cacheManager;
private readonly object l_state = new object();
int languageId = 0;
/// <summary>
/// Executes a task
/// </summary>
/// <param name="node">Xml node that represents a task description</param>
public void Execute(XmlNode node)
{
lock (l_state)
{
cacheManager = new NopStaticCache();
var attribute1 = node.Attributes["timeGenerate"];
if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
{
timeGenerate = TimeSpan.Parse(attribute1.Value);
}
bool alsoGenerate = false;
object obj = cacheManager.Get(ALSOGENERATEYML);
if (obj != null)
{
alsoGenerate = Convert.ToBoolean(obj);
}
DateTime nowTime = DateTime.Now;
DateTime nextGenerateTime = DateTime.Now;
if (alsoGenerate && (nowTime.Date + timeGenerate) <= nowTime)
{
nextGenerateTime = (nowTime.Date + new TimeSpan(1, 0, 0, 0)) + timeGenerate;
}
else
{
nextGenerateTime = nowTime.Date + timeGenerate;
}
if (nowTime > (nextGenerateTime - (new TimeSpan(3, 0, 0))) && nowTime < (nextGenerateTime - (new TimeSpan(1, 0, 0))))
{
cacheManager.Remove(ALSOGENERATEYML);
}
if (!alsoGenerate && (nowTime > nextGenerateTime && nowTime < (nextGenerateTime + new TimeSpan(2, 0, 0))))
{
string filename = AppDomain.CurrentDomain.BaseDirectory + "\\files\\yml\\priceyml.zip";
if (File.Exists(filename))
File.Delete(filename);
languageId = IoC.Resolve<ISettingManager>().GetSettingValueInteger("YMLPrices.LanguageId", 0);
ZipFile zipFile = new ZipFile(filename, Encoding.UTF8);
zipFile.AddEntry("priceyml.xml", (name, stream) =>
{
XmlTextWriter xtw = new XmlTextWriter(stream, Encoding.UTF8);
xtw.Formatting = Formatting.Indented;
xtw.WriteStartDocument();
Generate(xtw);
xtw.Flush();
xtw.Close();
});
zipFile.Save();
cacheManager.Add(ALSOGENERATEYML, true);
}
}
}
string EncodingString(string value)
{
//return win1251.GetString(Encoding.UTF8.GetBytes(value));
return value;
}
/// <summary>
/// This will build an xml sitemap for better index with search engines.
/// See http://en.wikipedia.org/wiki/Sitemaps for more information.
/// </summary>
/// <param name="stream">Stream of sitemap.</param>
public void Generate(XmlTextWriter writer)
{
//Write the DocumentType node.
writer.WriteDocType("yml_catalog", null, "shops.dtd", null);
writer.WriteStartElement("yml_catalog");
writer.WriteAttributeString("date", DateTime.Now.ToString(DateFormat));
writer.WriteStartElement("shop");
writer.WriteElementString("name", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.StoreName")));
writer.WriteElementString("company", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("StoreOwner.ShortName")));
writer.WriteElementString("url", IoC.Resolve<ISettingManager>().StoreUrl);
GenerateCurrencies(writer);
GenerateCategories(writer);
GenerateProducts(writer);
writer.WriteEndElement();
writer.WriteEndElement();
}
void GenerateCurrencies(XmlTextWriter writer)
{
writer.WriteStartElement("currencies");
writer.WriteStartElement("currency");
writer.WriteAttributeString("id", "RUR");
writer.WriteAttributeString("rate", "1");
writer.WriteEndElement();
writer.WriteEndElement();
}
void GenerateCategories(XmlTextWriter writer)
{
writer.WriteStartElement("categories");
var categories = IoC.Resolve<ICategoryService>().GetAllCategories(false);
foreach (var category in categories)
{
writer.WriteStartElement("category");
writer.WriteAttributeString("id", category.CategoryId.ToString());
if (category.ParentCategoryId != 0)
writer.WriteAttributeString("parentId", category.ParentCategoryId.ToString());
writer.WriteString(EncodingString(category.GetLocalizedName(languageId)));
writer.WriteEndElement();
}
writer.WriteEndElement();
}
void GenerateProducts(XmlTextWriter writer)
{
writer.WriteStartElement("offers");
var products = IoC.Resolve<IProductService>().GetAllProducts(false);
foreach (var product in products)
{
writer.WriteStartElement("offer");
//_writer.WriteAttributeString("type", "vendor.model");
writer.WriteAttributeString("available", "false");
writer.WriteAttributeString("id", product.ProductId.ToString());
if (product == null)
throw new ArgumentNullException("product");
string seName = SEOHelper.GetProductSEName(product);
string url2 = SEOHelper.EnableUrlRewriting ? IoC.Resolve<ISettingManager>().GetSettingValue("SEO.Product.UrlRewriteFormat") : "{0}Product.aspx?ProductID={1}";
string url = string.Format(url2, IoC.Resolve<ISettingManager>().StoreUrl, product.ProductId, seName).ToLowerInvariant();
writer.WriteElementString("url", url);
writer.WriteElementString("price", PriceHelper.GetFinalPrice(product.ProductVariants[0], false).ToString().Replace(",", "."));
writer.WriteElementString("currencyId", "RUR");
string prefixName = "";
if (product.ProductCategories.Count > 0)
{
writer.WriteElementString("categoryId", product.ProductCategories[0].CategoryId.ToString());
prefixName = IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.Prefix" + product.ProductCategories[0].Category.GetLocalizedSEName(languageId));
}
writer.WriteElementString("picture", IoC.Resolve<IPictureService>().GetPictureUrl(product.DefaultPicture, IoC.Resolve<ISettingManager>().GetSettingValueInteger("Media.Product.ThumbnailImageSize", 125), true));
writer.WriteElementString("delivery", "true");
writer.WriteElementString("local_delivery_cost", "0");
string man = "";
if (product.ProductManufacturers.Count > 0)
{
man = product.ProductManufacturers[0].Manufacturer.GetLocalizedName(languageId);
}
string model = "";
model = product.GetLocalizedName(languageId);
string name = "";
if (!string.IsNullOrEmpty(prefixName))
prefixName = prefixName + " ";
name = prefixName + man + " - " + model;
writer.WriteElementString("name", EncodingString(name));
if (!string.IsNullOrEmpty(man))
{
writer.WriteElementString("vendor", EncodingString(man));
}
string specDescription = "";
var productSpecificationAttributes = IoC.Resolve<ISpecificationAttributeService>().GetProductSpecificationAttributesByProductId(product.ProductId, null, true);
foreach (var productSpecificationAttribute in productSpecificationAttributes)
{
specDescription = specDescription + productSpecificationAttribute.SpecificationAttribute.GetLocalizedName(languageId) + " - " + productSpecificationAttribute.SpecificationAttributeOption.GetLocalizedName(languageId) + "; ";
}
writer.WriteElementString("description", EncodingString(product.GetLocalizedShortDescription(languageId) + " " + specDescription));
writer.WriteElementString("sales_notes", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.SalesNote")));
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
}
файл сохраняется в архив в папке \files\yml\priceyml.zip
здесь берется допущение, что каждому товару соответствует одна категория и один производитель
в название товара подставляется префикс - имя категории в единственном числе - он находится в настройках магазина, название настройки для префикса - YMLPrices.Prefix+название категории
описание товара в прайсе - короткое описание товара в базе+ характеристики товара
также используются другие дополнительные настройки магазина - посмотрите код, увидите сами
следует также внести изменения в файл Libraries\Nop.BusinessLogic\Utils\PictureService.cs
в процедуре public string GetPictureUrl(Picture picture, int targetSize, bool showDefaultPicture)
поменять
url = CommonHelper.GetStoreLocation() + "images/thumbs/" + localFilename;
return url;
на
if (HttpContext.Current == null)
url = IoC.Resolve<ISettingManager>().StoreUrl + "images/thumbs/" + localFilename;
else
url = CommonHelper.GetStoreLocation() + "images/thumbs/" + localFilename;
return url;
в процедуре public string GetDefaultPictureUrl(PictureTypeEnum defaultPictureType, int targetSize)
return CommonHelper.GetStoreLocation() + "images/thumbs/" + fname;
на
if (HttpContext.Current == null)
return IoC.Resolve<ISettingManager>().StoreUrl + "images/thumbs/" + fname;
else
return CommonHelper.GetStoreLocation() + "images/thumbs/" + fname;
в свойстве public string LocalThumbImagePath получение значения заменить на
string path = "";
if (HttpContext.Current == null)
path = AppDomain.CurrentDomain.BaseDirectory + "images\\thumbs";
else
path = HttpContext.Current.Request.PhysicalApplicationPath + "images\\thumbs";
return path;
и в свойстве public string LocalImagePath получение значения заменить на
string path = "";
if (HttpContext.Current == null)
path = AppDomain.CurrentDomain.BaseDirectory + "images";
else
path = HttpContext.Current.Request.PhysicalApplicationPath + "images";
return path;
в процедуре public string GetDefaultPictureUrl(PictureTypeEnum defaultPictureType, int targetSize)
string relPath = CommonHelper.GetStoreLocation() + "images/" + defaultImageName;
заменить на
string relPath = "";
if (HttpContext.Current == null)
relPath = IoC.Resolve<ISettingManager>().StoreUrl + "images/" + defaultImageName;
else
relPath = CommonHelper.GetStoreLocation() + "images/" + defaultImageName;
ну и самое последнее - в web.config добавить строку в раздел <ScheduleTasks>
<Thread seconds="60">
<task name="GeneratYML" type="NopSolutions.NopCommerce.BusinessLogic.Utils.GenerateYMLTask, Nop.BusinessLogic" enabled="true" stopOnError="false" timeGenerate="14:00:00"/>
где timeGenerate - время запуска генерирования прайса
ps: забыл добавить - это все делалось в версии 1.9
pps: исправление описания -
в название товара подставляется префикс - имя категории в единственном числе - он находится в настройках магазина, название настройки для префикса - YMLPrices.Prefix+название страницы с категорией в поле SEName