Making the web work. Together.

Better Google Base [Taxonomy] in nopCommerce 1.9

Posted by Ed on 25 May 2011 | 0 Comments

Tags: , , , , , , , , , ,

The time has come for me to add the taxomomy details to a nopCommerce 1.9 build. It wasn't in 1.9 by default, but [on the face of it] it seems pretty easy to add stuff in 1.8/1.9 with the introduction of the Entity Framework library, which makes naming convention important, but makes new objects and entities easier, once you figure out a few things.

This isn't a tutorial on ASP.NET or c#.

This isn't a how-to or turorial on Entity Frameworks.

This isn't a discussion of Google Merchant Centre, Taxonomy or why it's important.

This isn't a 'how to extend nopCommerce' tutorial per se, but you could use this example to extend it yourself for your own objects.

If you aren't familiar with c#, Entity Framework stuff and the workings of a .NET application, you probably need to take your time with this. I'm not going to hold your hand through things that aren't related to the broad strokes of this process. And remember, don't break what you can't fix, so back everything up first, so if it goes awry [and it probably will if you are new to all this] you can restore and start again.

This will be followed [hopefully next week] with a 1.9 version of the popup gallery, which will also be extended to allow 2 options for the gallery. One will be to show in a slimbox2 [LightBox] style, the other will be to display as an easySlider1.7 'scrolling' affair that I've used in nopCommerce 1.* with great effect. But more on that next week.

This article will show you how to add Taxonomy to products in nopCommerce 1.9 [and 1.8 with a few extra requirements for EF].

Here are a few notes on how we want the implementation to work:

We will download and create a 'taxonomy' table to store all out items. We'll grab the data itself from Google and do an import (or in my case, a copy from another project that I have it in).

Once we have the data, we want to be able to add our own categories/entries to it, so we want a new menu item [I'm putting it in products] to link to the list/add/edit/delete pages for taxonomy.

In the earlier version, I had set it to have a single selectable taxonomy with an additional textbox to manually add the extra text, but this is a bit restrictive, so I'm making it so you can select as many entries as you like for the product, and also as mentioned above, to add new taxonomy details fo your own to select from a drop down.

So we also need a custom property adding to product and a table for ProductID <-> GoogleTaxonomyID linking. This just means we can have as many Taxonomy categories per product and the pivot/link table stores them all, so we have less messing around in the core model. We just add an entry with ProductID and GoogleTaxonomyID, then retrieve any List<> by ProductID in the main product object.

So, first things first. Get the Google Taxonomy list and create a table from it in your nopCommerce database. See [url=http://www.n-webdesign.co.uk/better-google-base-in-nopcommerce/]this[/url] post for how to download and set up the taxonomy table.

Ok, now we need to add our objects to the solution. I'm adding a sub-folder in products, called GoogleProductTypes. It kinda makes sense being in there.

Once added, you can add a Class called GoogleProduct.cs and add the ID(int) and Name(string) fields to it, using the same names as the corresponding fields in the Taxonomy table. This is fairly important, as the Entity Framework uses the call and datatable names to match objects. You can call one apples and one oranges, but it just means you have to edit the EF diagram manually, which is a bit pointless if you just name stuff the same from the off.

Your class should look something like:

//######################################

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes
{
    public partial class GoogleProduct : BaseEntity
    {

        #region Properties

        public int GoogleProductTypeID { get; set; }
        public string GoogleProductType { get; set; }

        #endregion

    }
}

//#######################################

Next, open the Entity Framework diagram, and right click, then select update from the database. This will add the new taxonomy table to the model. You will need to edit the imported object, as it will come in called nop_GoogleProductType or similar, and we want it to be called 'GoogleProduct' to match our class name.

Once we have this set, we can now add out Service [manager] class and the interface. So, create a new interface in our GoogleProductTypes folder, next to our GooglePRoduct.cs and call it IGoogleProductService.cs.

We will be building this Interface up as we add new metthods but for now, we only need all the Taxonomy items in a list, so add: List<GoogleProduct> GetAllGoogleProducts();, so the Interface looks like...

//#######################################

using System;
using System.Collections.Generic;

namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes
{
    public partial interface IGoogleProductService
    {
        #region Methods

        List<GoogleProduct> GetAllGoogleProducts();

        #endregion
    }
}

//#######################################

Ok, now the actual class, called GoogleProductService as follows...

//#######################################

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NopSolutions.NopCommerce.BusinessLogic.Caching;
using NopSolutions.NopCommerce.BusinessLogic.Data;

namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes
{
    public partial class GoogleProductService : IGoogleProductService
    {

        #region Fields

        /// <summary>
        /// Object context
        /// </summary>
        private readonly NopObjectContext _context;

        /// <summary>
        /// Cache manager
        /// </summary>
        private readonly ICacheManager _cacheManager;

        #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="context">Object context</param>
        public GoogleProductService(NopObjectContext context)
        {
            this._context = context;
            this._cacheManager = new NopRequestCache();
        }

        #endregion

        #region Methods

        public List<GoogleProduct> GetAllGoogleProducts()
        {
            var query = from p in _context.GoogleProduct
                        orderby p.GoogleProductType
                        select p;
            var products = query.ToList();
            return products;
        }

        #endregion
    }
}

//#######################################

For now, we have a single method, GetAllGoogleProducts() that just does that. We are keeping it simple and building up from this slowly, so we know it all works as we go.

Now we want to add a new set of pages to admin, so we can manage these Taxonomy entries. We do this by adding a menu to admin.sitemap and then a set of pages, based on the current structure, to show, add, amend and delete items.

Open the admin sitemap, and in the catalog section under Manufacturers, add a line for Google Product Types. Just copy the manufacturers one and edit it.

I've pointed mine at ~/administration/googleproducttypes.aspx, so we now need to add the page. This is just a normal admin page, so go and add it now. make sure it uses the admin master page, then add a user control called GoogleProductTypes.ascx and add it to the page.

Now these pages need to use all the same base classes as the other pages and controls, so just copy any inheritance from other pages. I normally just pick a set of pages that is closest to the structure of what I' adding, so in this case I've copied the logic from the Manufacturers.aspx/ascx to give me my method prototypes and inheritance.

Once you've created the page and user control, just add the GoogleProductTypes.ascx control to the page. We'll now plumb in all the data and stuff.

First, add 'using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;' to BaseNopUserControl.cs, then add the new service. Like:


        public IGoogleProductService GoogleProductService
        {
            get { return IoC.Resolve<IGoogleProductService>(); }
        }

This goes in with the other services. It just allows us to use tons of built-in libraries and methods without invocating the classes ourselves, as they are already in the scope.

Once this is done, we can reference our GoogleProductService directly from the user control class. The ascx file will look like this:

//##############################################

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GoogleProductTypes.ascx.cs" Inherits="NopSolutions.NopCommerce.Web.Administration.Modules.GoogleProductTypes" %>

<div>
    <div>
        <img src="Common/ico-catalog.png" alt="<%=GetLocaleResourceString("Admin.Manufacturers.Title")%>" />
        <%=GetLocaleResourceString("Admin.GoogleProductTypes.Title")%>
    </div>
    <div>
        <input type="button" value="<%=GetLocaleResourceString("Admin.GoogleProductTypes.AddButton.Text")%>"
            id="btnAddNew" title="<%=GetLocaleResourceString("Admin.GoogleProductTypes.AddButton.Tooltip")%>" />
    </div>
</div>
<table>
    <asp:GridView ID="gvGoogleProductTypes" runat="server" AutoGenerateColumns="False" Width="100%"
    AllowPaging="true" PageSize="15">
        <Columns>
            <asp:TemplateField HeaderText="<% $NopResources:Admin.GoogleProductTypes.Name %>" ItemStyle-Width="65%">
                <ItemTemplate>
                    <%#Server.HtmlEncode(Eval("GoogleProductType").ToString())%>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="<% $NopResources:Admin.GoogleProductTypes.Edit %>" HeaderStyle-HorizontalAlign="Center"
                ItemStyle-Width="20%" ItemStyle-HorizontalAlign="Center">
                <ItemTemplate>
                    <a href="GoogleProductTypesDetails.aspx GoogleProductTypeID=<%#Eval("GoogleProductTypeId")%>" title="<%#GetLocaleResourceString("Admin.GoogleProductTypes.Edit.Tooltip")%>">
                        <%#GetLocaleResourceString("Admin.GoogleProductTypes.Edit")%>
                    </a>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    <PagerSettings PageButtonCount="50" Position="TopAndBottom" />
    </asp:GridView>
</table>

//##############################################

And the code behind (.cs) file will look like this:

//##############################################

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using NopSolutions.NopCommerce.BusinessLogic.ExportImport;
using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;
using NopSolutions.NopCommerce.Common.Utils;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;

namespace NopSolutions.NopCommerce.Web.Administration.Modules
{
    public partial class GoogleProductTypes : BaseNopAdministrationUserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                BindGrid();
            }
        }

        protected void BindGrid()
        {
            var googleproducttypes = this.GoogleProductService.GetAllGoogleProducts();
            gvGoogleProductTypes.DataSource = googleproducttypes;
            gvGoogleProductTypes.DataBind();
        }

        protected void gvGoogleProductTypes_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            gvGoogleProductTypes.PageIndex = e.NewPageIndex;
            BindGrid();
        }
    }
}

//##############################################

Now if you F5, the app will run (this is good) but you'll get an error when you try to load the GoogleProductTypes page in admin. This is because you need to add an entry in the UnityDependancyResolver.cs file. This is in BusinessLogic/Infrastructure. Add the following line 'using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;' to the using directive and add this to the page in the obvious place:

container.RegisterType<IGoogleProductService, GoogleProductService>(new UnityPerExecutionContextLifetimeManager());

Sorry about that. Please bear in mind, for speed rather than clarity, I'm adding this to a build as I write it up, so please bear with me. I'll provide all code in a file at the end, so don't panic about missing anything.

Ok, now F5 and navigate to administration, then the new section in Products, you'll see a big list of taxonomy details, ready to edit. You'll need to add localisation tokens etc, but the basics are there.

So what now

Well, we need an add, an edit and a update for this bit, and then a way of adding a list of taxonomy names to products.

Ok, so the add facility. We need a page called 'GoogleProductTypesAdd.aspx'. Add this and a corresponding 'GoogleProductTypesInfo.ascx' user control. Do the same as we did with the main page and control, using a reference page. Again, I'm going to use Manufacturers, as that is a simple(ish) object. The only difference is, I'm adding an info control directly for add/edit, rather than as a control within the ad control, as on the Manufacturers usage. This is because we only want a really simple single textbox, no tabs or complex content.

GoogleProductTypesAdd.aspx is pretty simple. It just has the  GoogleProductTypesInfo.ascx user control in it. The GoogleProductTypesInfo.ascx control looks as follows:

//#######################################

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GoogleProductTypesInfo.ascx.cs" Inherits="NopSolutions.NopCommerce.Web.Administration.Modules.GoogleProductTypesInfo" %>
<%@ Register TagPrefix="nopCommerce" TagName="SimpleTextBox" Src="SimpleTextBox.ascx" %>
<%@ Register TagPrefix="nopCommerce" TagName="ToolTipLabel" Src="ToolTipLabelControl.ascx" %>
<div>
    <div>
        <img src="Common/ico-catalog.png" alt="<%=GetLocaleResourceString("Admin.GoogleProductTypesAdd.Title")%>" />
        <%=GetLocaleResourceString("Admin.GoogleProductTypesAdd.Title")%><a href="Manufacturers.aspx"
            title="<%=GetLocaleResourceString("Admin.ManufacturerAdd.BackToManufacturers")%>">
            (<%=GetLocaleResourceString("Admin.GoogleProductTypesAdd.BackToManufacturers")%>)</a>
    </div>
    <div>
        <asp:Button ID="SaveButton" runat="server" Text="<% $NopResources:Admin.ManufacturerAdd.SaveButton.Text %>"
            CssClass="adminButtonBlue" ToolTip="<% $NopResources:Admin.ManufacturerAdd.SaveButton.Tooltip %>" />

        <asp:Button ID="SaveAndStayButton" runat="server" CssClass="adminButtonBlue" Text="<% $NopResources:Admin.ManufacturerAdd.SaveAndStayButton.Text %>"
            />
    </div>
</div>
<table>
    <tr>
        <td>
            <nopCommerce:ToolTipLabel runat="server" ID="lblName" Text="<% $NopResources:Admin.GoogleProductTypesInfo.Name %>"
                ToolTip="<% $NopResources:Admin.GoogleProductTypesInfo.Name.Tooltip %>" ToolTipImage="~/Administration/Common/ico-help.gif" />
        </td>
        <td>
            <nopCommerce:SimpleTextBox runat="server" ID="txtName" CssClass="adminInput" ErrorMessage="<% $NopResources:Admin.GoogleProductTypesInfo.Name.ErrorMessage %>">
            </nopCommerce:SimpleTextBox>
        </td>
    </tr>
</table>

//#####################################

And the GoogleProductTypesInfo.ascx.cs code behind is like:

//#####################################

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;
using NopSolutions.NopCommerce.BusinessLogic.Media;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.Templates;
using NopSolutions.NopCommerce.Common.Utils;
using NopSolutions.NopCommerce.Web.Administration.Modules;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;

namespace NopSolutions.NopCommerce.Web.Administration.Modules
{
    public partial class GoogleProductTypesInfo : BaseNopAdministrationUserControl
    {
        private void BindData()
        {
            var googleproducttype = this.GoogleProductService.GetGoogleProduct(this.GoogleProductTypeId);

            if (googleproducttype != null)
            {
                this.txtName.Text = googleproducttype.GoogleProductType;
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                this.BindData();
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            BindJQuery();
            BindJQueryIdTabs();

            base.OnPreRender(e);
        }

        protected void SaveButton_Click(object sender, EventArgs e)
        {
            if (Page.IsValid)
            {
                try
                {
                    GoogleProduct googleproduct = SaveInfo();
                    Response.Redirect("googleproducttypes.aspx");
                }
                catch (Exception exc)
                {
                    ProcessException(exc);
                }
            }
        }

        protected void SaveAndStayButton_Click(object sender, EventArgs e)
        {
            if (Page.IsValid)
            {
                try
                {
                    GoogleProduct googleproduct = SaveInfo();
                    Response.Redirect("GoogleProductTypesAdd.aspx GoogleProductTypeID=" + googleproduct.GoogleProductTypeID);
                }
                catch (Exception exc)
                {
                    ProcessException(exc);
                }
            }
        }

        public GoogleProduct SaveInfo()
        {
            var googleproducttype = this.GoogleProductService.GetGoogleProduct(this.GoogleProductTypeId);

            if (googleproducttype != null)
            {

                googleproducttype.GoogleProductType = txtName.Text;
                this.GoogleProductService.UpdateGoogleProductType(googleproducttype);
            }
            else
            {
                googleproducttype = new GoogleProduct()
                {
                    GoogleProductType = txtName.Text,
                };
                this.GoogleProductService.AddGoogleProductType(googleproducttype);
            }

            return googleproducttype;
        }

        public int GoogleProductTypeId
        {
            get
            {
                return CommonHelper.QueryStringInt("GoogleProductTypeId");
            }
        }
    }
}

//#####################################

So now, if we F5 and check it all out, we can add and edit new taxonomy entries here.

We might need to re-visit this bit to add better filtering and searching (like the options available in Localisation) but we'll worry about that later.

At this moment, we have about half of the structure we need for the complete implementation. Hopefully you've got here error free or have been able to fix anything that has arisen. It's pretty likely that you'll have some errors with the entity framework and other bits of the model process, but as a tip, make sure all your data tables have a primary key and an identity field, that the naming convention between the database and the classes we are adding in consistent and that you take small steps, especially when you are learning new stuff like Entity Framework models.

Ok, on to stage 2. Adding the mappings for google product types to our products.

We are going to add a mapping table in the database, with a ProductID and a GoogleProductTypeID, a new tab to the 'Products' section and some nifty selection options to add/remove entries with ease.

I considered either a dropdown selector with an add new, or a checkbox list stylee thing as in the categories tab, but aas there are nearly 4000 taxonomies, it was a no brainer. We will be adding a list of already available items, a dropdown for adding new ones and a linkbutton to delete existing ones.

This should be [in theory] fairly painless with the EF stuff, but here goes. (I've had a lot of trouble with it so far, as I tend to fight stuff like this, rather than accept it knws best :)).

So first, add the table to the database.

//################################

USE [my-database]
GO

/****** Object:  Table [dbo].[Nop_GoogleProductTypeMapping]    Script Date: 05/26/2011 16:38:38 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Nop_GoogleProductTypeMapping](
    [GoogleProductTypeMappingID] [int] IDENTITY(1,1) NOT NULL,
    [GoogleProductTypeID] [int] NULL,
    [ProductID] [int] NULL,
 CONSTRAINT [PK_GoogleProductTypeMapping] PRIMARY KEY CLUSTERED
(
    [GoogleProductTypeMappingID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

//########################################  

Next, add a GoogleProductTypeMapping.cs file to our GoogleProductTypes folder, like so:

//########################################

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes
{
    public partial class GoogleProductTypeMapping : BaseEntity
    {

        #region Properties

        public int GoogleProductTypeMappingID { get; set; }
        public int GoogleProductTypeID { get; set; }
        public int ProductID { get; set; }

        #endregion

    }
}

//#####################################

...then add these new methods to the IGoogleProductService.cs file:

        void AddGoogleProductToProduct(GoogleProductTypeMapping googleproducttypemapping);

        void RemoveGoogleProductFromProduct(int ProductID, int GoogleProductTypeID);

        GoogleProductTypeMapping GetGoogleProductTypeMapping(int ProductID, int GoogleProductTypeID);

        GoogleProductTypeMapping GetGoogleProductMapping(int GoogleProductTypeMappingID);

        List<GoogleProductTypeMapping> GetGoogleProductTypeMapping(int ProductID);

...and also add them to the GoogleProductService.cs file as follows...

//######################################


        public GoogleProductTypeMapping GetGoogleProductTypeMapping(int ProductID, int GoogleProductTypeID)
        {
            var query = from m in _context.GoogleProductTypeMapping
                        where m.ProductID == ProductID && m.GoogleProductTypeID == GoogleProductTypeID
                        select m;
            var mapping = query.SingleOrDefault();

            return mapping;
        }

        public GoogleProductTypeMapping GetGoogleProductMapping(int GoogleProductTypeMappingID)
        {
            var query = from m in _context.GoogleProductTypeMapping
                        where m.GoogleProductTypeMappingID == GoogleProductTypeMappingID
                        select m;
            var mapping = query.SingleOrDefault();

            return mapping;
        }

        public void AddGoogleProductToProduct(GoogleProductTypeMapping googleproducttypemapping)
        {

            _context.GoogleProductTypeMapping.AddObject(googleproducttypemapping);
            _context.SaveChanges();

        }

        public void RemoveGoogleProductFromProduct(int ProductID, int GoogleProductTypeID)
        {
            var googlemapping = GetGoogleProductTypeMapping(ProductID, GoogleProductTypeID);
            if (googlemapping == null)
                return;

            if (!_context.IsAttached(googlemapping))
                _context.GoogleProductTypeMapping.Attach(googlemapping);
            _context.DeleteObject(googlemapping);
            _context.SaveChanges();
        }

        public List<GoogleProductTypeMapping> GetGoogleProductTypeMapping(int ProductID)
        {
            var query = from m in _context.GoogleProductTypeMapping
                        where m.ProductID == ProductID
                        select m;
            var mapping = query.ToList();
            return mapping;
        }

//#####################################

Finally, do the 'update from database' on the NopModel.edmx file. Remember to rename the new mapping table to match the class we've just added.

We also need to expose the list in our Products.cs class, so in our extensions folder in Nop.BusinessLogic [make one if you haven't go one], extend our partial products class like so:

//#####################################

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;

namespace NopSolutions.NopCommerce.BusinessLogic.Products
{
    public partial class Product : BaseEntity
    {
        public List<GoogleProduct> GoogleProducts
        {
            get
            {
                return IoC.Resolve<IGoogleProductService>().GetAllGoogleProductsByProduct(this.ProductId);
            }
        }
    }
}

//#####################################

This just adds a property of type List of GoogleProducts, so we can reference them in the code later on.

Now we can add the section to the 'Products' tabs in the admin section.

In administration/modules, add a user control called 'ProductGoogleMappings.ascx'. It looks something like:

//#####################################

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProductGoogleMappings.ascx.cs" Inherits="NopSolutions.NopCommerce.Web.Administration.Modules.ProductGoogleMappings" %>
<%@ Register TagPrefix="nopCommerce" TagName="ToolTipLabel" Src="ToolTipLabelControl.ascx" %>

<asp:Panel runat="server" ID="pnlData">
    <asp:GridView ID="gvwGoogleMappings" runat="server" AutoGenerateColumns="false" DataKeyNames="GoogleProductTypeMappingID"
        Width="100%">
        <Columns>
            <asp:TemplateField HeaderText="<% $NopResources:Admin.ProductGoogleMappings.GoogleMapping %>">
                <ItemTemplate>
                    <%#Server.HtmlEncode(Eval("GoogleProduct.GoogleProductType").ToString())%>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="<% $NopResources:Admin.ProductGoogleMappings.Delete %>"
                HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
                <ItemTemplate>
                    <asp:Button ID="btnDeleteGoogleMappings" runat="server" CssClass="adminButton" Text="<% $NopResources:Admin.ProductGoogleMappings.Delete %>"
                        CausesValidation="false" CommandName="Delete" CommandArgument='<%# Bind("GoogleProductTypeMappingID") %>' ToolTip="<% $NopResources:Admin.ProductGoogleMappings.Delete.Tooltip %>" />
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
    <p>
        <strong>
            <%=GetLocaleResourceString("Admin.ProductGoogleMappings.AddNewGoogleMapping")%>
        </strong>
    </p>
    <table>
        <tr>
            <td>
                <nopCommerce:ToolTipLabel runat="server" ID="lblSelectPicture1" Text="<% $NopResources:Admin.ProductGoogleMappingss.SelectMappings %>"
                    ToolTip="<% $NopResources:Admin.ProductGoogleMappings.SelectGoogleMappings.Tooltip %>" ToolTipImage="~/Administration/Common/ico-help.gif" />
            </td>
            <td>
                <asp:DropDownList ID="ddlGoogleMappings" CssClass="adminInput" runat="server"
                    ToolTip="<% $NopResources:Admin.ProductGoogleMappings.DropDownList %>" />
            </td>
        </tr>
        <tr>
            <td colspan="2" align="left">
                <asp:Button runat="server" ID="btnAddGoogleMapping" CssClass="adminButton" Text="<% $NopResources:Admin.ProductGoogleMappings.Add.Text %>"
                    ToolTip="<% $NopResources:Admin.ProductGoogleMappings.Add.Tooltip %>"
                    />
            </td>
        </tr>
    </table>
</asp:Panel>

//#####################################

...and the 'ProductGoogleMappings.ascx.cs' code behind file as follows:

//#####################################

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;
using NopSolutions.NopCommerce.BusinessLogic.Media;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;
using NopSolutions.NopCommerce.BusinessLogic.Templates;
using NopSolutions.NopCommerce.Common.Utils;
using NopSolutions.NopCommerce.Web.Administration.Modules;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;

namespace NopSolutions.NopCommerce.Web.Administration.Modules
{
    public partial class ProductGoogleMappings : BaseNopAdministrationUserControl
    {
        private void BindData()
        {
            var googlemapping = this.GoogleProductService.GetGoogleProductTypeMapping(this.ProductId);
            if (googlemapping != null)
            {
                pnlData.Visible = true;

                gvwGoogleMappings.Visible = true;
                gvwGoogleMappings.DataSource = googlemapping;
                gvwGoogleMappings.DataBind();

            }
            else
            {
                pnlData.Visible = false;
            }
        }

        private void FillDropDown()
        {
            var googlemapping = this.GoogleProductService.GetAllGoogleProducts();
            ddlGoogleMappings.DataSource = googlemapping;
            ddlGoogleMappings.DataTextField = "GoogleProductType";
            ddlGoogleMappings.DataValueField = "GoogleProductTypeID";
            ddlGoogleMappings.DataBind();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                this.BindData();
                this.FillDropDown();
            }
        }

        public void SaveInfo()
        {
        }

        protected void gvwGoogleMappings_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
            int GoogleProductTypeMappingID = (int)gvwGoogleMappings.DataKeys[e.RowIndex]["GoogleProductTypeMappingID"];
            GoogleProductTypeMapping GoogleMapping = this.GoogleProductService.GetGoogleProductTypeMapping(ProductId, GoogleProductTypeMappingID);
            if (GoogleMapping != null)
            {
                this.GoogleProductService.RemoveGoogleProductFromProduct(ProductId, GoogleProductTypeMappingID);
                BindData();
            }

        }

        protected void btnAddGoogleMapping_Click(object sender, EventArgs e)
        {
            GoogleProductTypeMapping googlemapping = new GoogleProductTypeMapping();
 
            googlemapping.ProductID = ProductId;
            googlemapping.GoogleProductTypeID = Convert.ToInt16(ddlGoogleMappings.SelectedValue);

            this.GoogleProductService.AddGoogleProductToProduct(googlemapping);

            BindData();
        }

        public int ProductId
        {
            get
            {
                return CommonHelper.QueryStringInt("ProductId");
            }
        }
    }
}

//#####################################.

Then, in the 'ProductDetails.ascx', add a reference to the new user control like:

<%@ Register TagPrefix="nopCommerce" TagName="GoogleMappings" Src="~/Administration/Modules/ProductGoogleMappings.ascx" %>

...and add a new tab to the ajax tabb control:

    <ajaxToolkit:TabPanel runat="server" ID="pnlGoogleMappings" HeaderText="<% $NopResources:Admin.ProductDetails.GoogleMappings %>">
        <ContentTemplate>
            <nopCommerce:GoogleMappings ID="ctrlGoogleMappings" runat="server" />
        </ContentTemplate>
    </ajaxToolkit:TabPanel>

That should be pretty straightforward. Now F5 again, login, navigate to a product and you should see a new tab with our newly added list [as yet unpopulated] of google product types and an add option.

I've glossed over some of the minor points of adding the logic and mechanics to the pages, but you can see whaer we add the google products list to the drop down and where we commit the add/update via the entity framework stuff.

Ok, so now we have a list of google product types, a way of adding them and managing them in products, and a way of exposing this information in the code.

All that is left now is to add them to the Froogle.ashx file.

So, open the FroogleService.cs file (Found in PromotionProviders/Nop.Froogle].

Now we have the data in our Product class ready to roll, we can be quite creative with how we use it, but I'm keeping it simple for this implementation.

We willl basically iterate through the 'GoogleProducts' List in the Product class and add  this to the xml feed data. So first, add a reference to the Google Products library:

using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;

Then add the following in the code at around line 97, just after the comments about google product type attributes.

string sout = "";

                        foreach (GoogleProduct c in product.GoogleProducts)
                        {
                            if (sout.Length > 0)
                            {
                                sout += ", ";
                            }
                            
                            sout += "\"" + c.GoogleProductType + "\"";
                        }

                        if (sout.Length > 0)
                        {
                            writer.WriteStartElement("g", "product_type", googleBaseNamespace);
                            writer.WriteCData(sout);
                            writer.WriteFullEndElement();
                        }
.
This simply adds a list of taxonomy entries to the file.

And that's it. We're done.

Please be aware that this will probably have a few errors in it or the odd think I've missed, so please let me know if you spot anything and I'll amend it. Once I've got it in production somewhere, I'll add the complete code to SourceForge (which will be a lot easier for you!). If you have any other comments or suggestions, please let me know.

Enjoy. ed/


Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page | RSS feed for all comments