Creating A Custom Feed Provider

From AbleCommerce Wiki
Revision as of 10:09, 27 August 2008 by Sohaib (Talk | contribs)

Jump to: navigation, search

IFeedProvider

To create a custom Feed Provider you need to implement CommerceBuilder.DataFeeds.IFeedProvider interface. This interface has been defined as follows.

    public interface IFeedProvider
    {
        bool CreateFeed(FeedOptions options, ref List<string> messages);

        bool CompressFeed(FeedOptions options, ref List<string> messages);

        bool UploadUncompressedFeed(FeedOptions options, ref List<string> messages);

        bool UploadCompressedFeed(FeedOptions options, ref List<string> messages);
    }

You will notice that each method has two arguments. FeedOptions and a List<string> passed by reference. FeedOptions object contains common options for the feed. The List<string> object passed by reference is used to get the error messages or warnings during the process.


FeedOptions

FeedOptions class is defined as follows

    public class FeedOptions
    {
        private string _FeedFileName = "";
        private bool _OverwriteFeedFile = true;
        private bool _IncludeAllProducts = false;
        private string _CompressedFeedFileName = "";
        private bool _OverwriteCompressedFile = true;
        private string _FtpHost = "";
        private string _FtpUser = "";
        private string _FtpPassword = "";
        private string _RemoteFileName = "";
        private string _FeedDataPath = "";

        private bool IsDirty = false;
        
        public string FeedFileName
        {
            get { return _FeedFileName; }
            set
            {
                if (!_FeedFileName.Equals(value, StringComparison.InvariantCultureIgnoreCase))
                {
                    _FeedFileName = value;
                    IsDirty = true;
                }
            }
        }

        public bool OverwriteFeedFile
        {
            get { return _OverwriteFeedFile; }
            set
            {
                if (!_OverwriteFeedFile.Equals(value))
                {
                    _OverwriteFeedFile = value;
                    IsDirty = true;
                }
            }
        }

        public bool IncludeAllProducts
        {
            get { return _IncludeAllProducts; }
            set
            {
                if (!_IncludeAllProducts.Equals(value))
                {
                    _IncludeAllProducts = value;
                    IsDirty = true;
                }
            }
        }

        public string CompressedFeedFileName
        {
            get { return _CompressedFeedFileName; }
            set
            {
                if (!_CompressedFeedFileName.Equals(value, StringComparison.InvariantCultureIgnoreCase))
                {
                    _CompressedFeedFileName = value;
                    IsDirty = true;
                }
            }
        }

        public bool OverwriteCompressedFile
        {
            get { return _OverwriteCompressedFile; }
            set
            {
                if (!_OverwriteCompressedFile.Equals(value))
                {
                    _OverwriteCompressedFile = value;
                    IsDirty = true;
                }
            }
        }

        public string FtpHost
        {
            get { return _FtpHost; }
            set
            {
                if (!_FtpHost.Equals(value, StringComparison.InvariantCultureIgnoreCase))
                {
                    _FtpHost = value;
                    IsDirty = true;
                }
            }
        }

        public string FtpUser
        {
            get { return _FtpUser; }
            set
            {
                if (!_FtpUser.Equals(value))
                {
                    _FtpUser = value;
                    IsDirty = true;
                }
            }
        }

        public string FtpPassword
        {
            get { return _FtpPassword; }
            set
            {
                if (!_FtpPassword.Equals(value))
                {
                    _FtpPassword = value;
                    IsDirty = true;
                }
            }
        }

        public string RemoteFileName
        {
            get { return _RemoteFileName; }
            set
            {
                if (!_RemoteFileName.Equals(value))
                {
                    _RemoteFileName = value;
                    IsDirty = true;
                }
            }
        }


        public string FeedDataPath
        {
            get { return _FeedDataPath; }
            set
            {
                if (!_FeedDataPath.Equals(value, StringComparison.InvariantCultureIgnoreCase))
                {
                    _FeedDataPath = value;
                    IsDirty = true;
                }
            }
        }

        /// <summary>
        /// Load feed options using the given option keys
        /// </summary>
        /// <param name="settingKeys">Feed Option Setting Keys</param>
        public void Load(IFeedOptionKeys settingKeys)
        {
            StoreSettingCollection settings = Token.Instance.Store.Settings;
            _CompressedFeedFileName = settings.GetValueByKey(settingKeys.CompressedFeedFileName);
            _FeedDataPath = settings.GetValueByKey(settingKeys.FeedDataPath);
            _FeedFileName = settings.GetValueByKey(settingKeys.FeedFileName);
            _FtpHost = settings.GetValueByKey(settingKeys.FtpHost);
            _FtpPassword = settings.GetValueByKey(settingKeys.FtpPassword);
            _FtpUser = settings.GetValueByKey(settingKeys.FtpUser);
            _IncludeAllProducts = AlwaysConvert.ToBool(settings.GetValueByKey(settingKeys.IncludeAllProducts), false);
            _OverwriteCompressedFile = AlwaysConvert.ToBool(settings.GetValueByKey(settingKeys.OverwriteCompressedFile), true);
            _OverwriteFeedFile = AlwaysConvert.ToBool(settings.GetValueByKey(settingKeys.OverwriteFeedFile), true);
            _RemoteFileName = settings.GetValueByKey(settingKeys.RemoteFileName);
            IsDirty = false;
        }

        /// <summary>
        /// Save the feed options using the given feed option keys
        /// </summary>
        /// <param name="settingKeys">Feed Option Setting Keys</param>
        public void Save(IFeedOptionKeys settingKeys)
        {
            if (IsDirty)
            {
                StoreSettingCollection settings = Token.Instance.Store.Settings;
                settings.SetValueByKey(settingKeys.CompressedFeedFileName, CompressedFeedFileName);
                settings.SetValueByKey(settingKeys.FeedDataPath, FeedDataPath);
                settings.SetValueByKey(settingKeys.FeedFileName, FeedFileName);
                settings.SetValueByKey(settingKeys.FtpHost, FtpHost);
                settings.SetValueByKey(settingKeys.FtpPassword, FtpPassword);
                settings.SetValueByKey(settingKeys.FtpUser, FtpUser);
                settings.SetValueByKey(settingKeys.IncludeAllProducts, IncludeAllProducts.ToString());
                settings.SetValueByKey(settingKeys.OverwriteCompressedFile, OverwriteCompressedFile.ToString());
                settings.SetValueByKey(settingKeys.OverwriteFeedFile, OverwriteFeedFile.ToString());
                settings.SetValueByKey(settingKeys.RemoteFileName, RemoteFileName);

                settings.Save();
            }
        }
    }


IFeedOptionKeys

You will notice that the feed option setting values are stored in the store settings table. This means that if there are more than one feed providers we need to distinguish the settings for each of them by some means. In order to ensure that each feed provider has its own settings stored with its own distinct keys each feed provider must also provide an implementation of IFeedOptionKeys interface.

IFeedOptionKeys is defined as follows

    public interface IFeedOptionKeys
    {
        string FeedFileName { get; }        
        string OverwriteFeedFile { get; }
        string IncludeAllProducts { get; }
        string CompressedFeedFileName { get; }
        string OverwriteCompressedFile { get; }
        string FtpHost { get; }
        string FtpUser { get; }
        string FtpPassword { get; }
        string FeedDataPath { get; }
        string RemoteFileName { get; }
    }

The implementation of IFeedOptionKeys for a particular feed provider must return unique keys for that provider. For example implementation of IFeedOptionKeys for GoogleBase feed is like this

    public class GoogleBaseOptionKeys : IFeedOptionKeys
    {        
        public string FeedFileName { get{ return "GoogleBase_FeedFileName";}}
        public string OverwriteFeedFile { get { return "GoogleBase_OverwriteFeedFile"; } }
        public string IncludeAllProducts { get { return "GoogleBase_IncludeAllProducts"; } }
        public string CompressedFeedFileName { get { return "GoogleBase_CompressedFeedFileName"; } }
        public string OverwriteCompressedFile { get { return "GoogleBase_OverwriteCompressedFile"; } }
        public string FtpHost { get { return "GoogleBase_FtpHost"; } }
        public string FtpUser { get { return "GoogleBase_FtpUser"; } }
        public string FtpPassword { get { return "GoogleBase_FtpPassword"; } }
        public string FeedDataPath { get { return "GoogleBase_FeedDataPath"; } }
        public string RemoteFileName { get { return "GoogleBase_RemoteFileName"; } }
    }


FeedProviderBase

There is a helper class to facilitate implementation of feed providers. This is FeedProviderBase. The actual implementation can simply extend this class instead of implementing all the methods by itself. In fact it is recomended that you extend FeedProviderBase class instead of implementing the IFeedProvider interface directly. If you extend FeedProviderBase class you just need to implement one method

	protected abstract string GetFeedData(ProductCollection products);

The GetFeedData method takes a collection or products and creates a feed for them and returns the resulting feed data as string back to the caller.

GoogleBase Reference Implementation

Here is a complete implementation of Google Base feed provider.

    public class GoogleBase : FeedProviderBase
    {
        private static GoogleBase _Instance = null;
        private GoogleBase()   { }

        //Singleton Instance of the GoogleBase feed provider implementation
        public static GoogleBase Instance { 
            get{
                if (_Instance == null) _Instance = new GoogleBase();                
                return _Instance;
            }
        }

        protected override string GetFeedData(ProductCollection products)
        {
            StringWriter feedWriter = new StringWriter();

            //Required
            //	description, id, link, price, title 
            StringBuilder feedLine = new StringBuilder();
            feedLine.Append("description\t");
            feedLine.Append("id\t");
            feedLine.Append("link\t");
            feedLine.Append("price\t");
            feedLine.Append("title\t");

            //Recommended
            //	brand, condition, image_link, isbn, mpn, upc 
            feedLine.Append("brand\t");
            feedLine.Append("condition\t");
            feedLine.Append("image_link\t");
            //feedLine.Append("isbn\t");
            //feedLine.Append("mpn\t");
            feedLine.Append("upc");
            
            //Optional
            //	color, expiration_date, height,length,model_number,payment_accepted,payment_notes 
            //	price_type,product_type,quantity,size,weight,width,year

            //write header row
            feedWriter.WriteLine(feedLine.ToString());

            string storeUrl = Token.Instance.Store.StoreUrl;
            storeUrl = storeUrl + (storeUrl.EndsWith("/") ? "" : "/");

            string url, name, desc, price, imgurl, id, brand, condition, mpn, upc;
            foreach (Product product in products)
            {
                url = product.NavigateUrl;
                if (url.StartsWith("~/"))
                {
                    url = url.Substring(2);
                }
                url = storeUrl + url;

                name = StringHelper.CleanupSpecialChars(product.Name);
                desc = StringHelper.CleanupSpecialChars(product.Summary);
                
                price = string.Format("{0:F2}", product.Price);

                imgurl = product.ImageUrl;
                if (!string.IsNullOrEmpty(imgurl) && !IsAbsoluteURL(imgurl))
                {
                    if (imgurl.StartsWith("~/"))
                    {
                        imgurl = imgurl.Substring(2);
                    }
                    imgurl = storeUrl + imgurl;
                }
                
                id = product.ProductId.ToString();
                brand = product.Manufacturer != null ? product.Manufacturer.Name : "";
                condition = "new";
                //mpn = product.ModelNumber;
                upc = string.IsNullOrEmpty(product.Sku) ? product.ProductId.ToString() : product.Sku;

                feedLine = new StringBuilder();
                feedLine.Append(desc + "\t" + id + "\t" + url + "\t" + price + "\t" + name + "\t");
                feedLine.Append(brand + "\t" + condition + "\t" + imgurl + "\t" + upc);

                feedWriter.WriteLine(feedLine.ToString());
            }

            string returnData = feedWriter.ToString();
            feedWriter.Close();

            return returnData;
        }

    }

    public class GoogleBaseOptionKeys : IFeedOptionKeys
    {        
        public string FeedFileName { get{ return "GoogleBase_FeedFileName";}}
        public string OverwriteFeedFile { get { return "GoogleBase_OverwriteFeedFile"; } }
        public string IncludeAllProducts { get { return "GoogleBase_IncludeAllProducts"; } }
        public string CompressedFeedFileName { get { return "GoogleBase_CompressedFeedFileName"; } }
        public string OverwriteCompressedFile { get { return "GoogleBase_OverwriteCompressedFile"; } }
        public string FtpHost { get { return "GoogleBase_FtpHost"; } }
        public string FtpUser { get { return "GoogleBase_FtpUser"; } }
        public string FtpPassword { get { return "GoogleBase_FtpPassword"; } }
        public string FeedDataPath { get { return "GoogleBase_FeedDataPath"; } }
        public string RemoteFileName { get { return "GoogleBase_RemoteFileName"; } }
    }


Deployment

Once your implementation is complete you can compile it an put the DLL in Website/Bin folder. That is all you need to do to implement a feed provider. However, unlike payment and shipping providers, feed providers will not be automatically detected and made available in the merchant Admin. You will have to create merchant admin page(s) for the new feed provider yourself. An example and good starting point is Website\Admin\Marketing\Feeds\GoogleBase.aspx page.