Bug in Admin Category Sorting page

For general questions and discussions specific to the AbleCommerce 7.0 Asp.Net product.
Post Reply
bgreenwald54
Ensign (ENS)
Ensign (ENS)
Posts: 10
Joined: Tue Jan 31, 2012 1:20 pm

Bug in Admin Category Sorting page

Post by bgreenwald54 » Wed Aug 15, 2012 2:22 pm

In the Admin Sort Category page (/Admin/Catalog/SortCategory.aspx), there is a bug in the way the sort is handled.

The page uses the CatalogNodeCollection to populate a listbox that is resorted through javascript calls to the functions UP(int) and DN(int). The javascript functions are responsible for reordering the listbox, and the SaveButton_Click event reads the resulting listbox state and attempts to save the changes.

The bug is in how these list items are created.

Code: Select all

                <asp:ListBox ID="CatalogNodeList" runat="server" DataTextField="Name" DataValueField="CatalogNodeId" Rows="20">
                </asp:ListBox>
The value field is bound to CatalogNodeId, which is NOT a unique identifier for items in the list. A catalog node can be one of 4 different objects:

Code: Select all

    public enum CatalogNodeType
    {
        Category = 0,
        Product = 1,
        Webpage = 2,
        Link = 3,
    }
Each of these objects is stored in a separate database table and has a separate identity pool. Because of this, duplicate values could exist across the identity pools. Therefore, it is insufficient to use the identity value by itself (catalognodeid) without qualifying which type of node (catalognodetypeid) it is. The listbox does not distinguish the node types in any way.

Assume we have 2 categories and 4 products:

Category A (Id = 1)
Category B (Id = 2)

Product 1 (Id = 1)
Product 2 (Id = 2)
Product 3 (Id = 3)
Product 4 (Id = 4)

Their current sort order in the listbox is such:

Category A (Id = 1)
Product 1 (Id = 1)
Category B (Id = 2)
Product 2 (Id = 2)
Product 3 (Id = 3)
Product 4 (Id = 4)

If we use the FIRST, ^,v, and LAST buttons, we see that the item moves appropriately up and down the list. If we move Category B to the top, we now have:

Category B (Id = 2)
Category A (Id = 1)
Product 1 (Id = 1)
Product 2 (Id = 2)
Product 3 (Id = 3)
Product 4 (Id = 4)

Then we click save. But we notice that Category B moves back above Product 2. Why? Lets look at the SaveButton_Click code:

Code: Select all

protected void SaveButton_Click(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(SortOrder.Value))
        {
SortOrder is the hidden field that holds the comma-separated list of ids in the desired sort order. Right now, it looks like this:

2,1,1,2,3,4

Lets see what happens next in the SaveButton_Click:

Code: Select all

            CatalogNodeCollection catalogNodes = _CatalogNodes;

            string[] catalogNodesIds = SortOrder.Value.Split(",".ToCharArray());
            int order = 0;
            int tempId = 0;
            int index = -1;
            foreach (string sPartId in catalogNodesIds)
            {
                foreach (CatalogNode cn in _CatalogNodes)
                {
                    tempId = AlwaysConvert.ToInt(sPartId);
                    if (cn.CatalogNodeId == tempId)
                    {
                        index = catalogNodes.IndexOf(cn);
                        if (index > -1)
                            catalogNodes[index].OrderBy = (short)order;
                        order++;
                    }
                }
            }
            catalogNodes.Save();
        }
        Response.Redirect("Browse.aspx?CategoryId=" + _CategoryId.ToString());
    }
Here we are going to go through each id and attempt to locate it in the catalog nodes, and when we find it, assign a new OrderBy value and increment the order value for the next item. You can see that, because of the duplication of Id values in the SortOrder string, simply looping through catalog nodes and checking to see if the Id matches is not sufficient. The catalog node type has to be checked as well. Unfortunately, the listbox has no knowledge of the catalog node type after it is bound. The listbox has to be able to distinguish products from categories from webpages from links in order to perform the sort properly. this could be accomplished through using both id and catalog node type in the value field, separated by some delimiter other than a comma, and then split the values and do the checking of both id and node type as you loop through the catalog nodes.

Has this bug been reported? Is there an expected release of a fix?

sweeperq
Commodore (COMO)
Commodore (COMO)
Posts: 497
Joined: Tue Jan 03, 2006 2:45 pm

Re: Bug in Admin Category Sorting page

Post by sweeperq » Tue Jun 24, 2014 1:35 pm

I just came across this today in AC 7.0.4. We plan on upgrading to AC Gold R8, but in the mean time, was there ever a fix for this?

User avatar
Katie
AbleCommerce Admin
AbleCommerce Admin
Posts: 2651
Joined: Tue Dec 02, 2003 1:54 am
Contact:

Re: Bug in Admin Category Sorting page

Post by Katie » Tue Jun 24, 2014 1:47 pm

Using the example from the original post, I cannot reproduce the issue in Gold R8. Sorting of a mix of products and categories work fine now.

Good Luck with your upgrade!
Thank you for choosing AbleCommerce!

http://help.ablecommerce.com - product support
http://wiki.ablecommerce.com - developer support

sweeperq
Commodore (COMO)
Commodore (COMO)
Posts: 497
Joined: Tue Jan 03, 2006 2:45 pm

Re: Bug in Admin Category Sorting page

Post by sweeperq » Wed Jun 25, 2014 5:02 am

In case anyone else is on AC7 and encountering this issue, here is my code for /Admin/Catalog/SortCategory.aspx.cs. I merged the Node Id with the Node Type as the value in the drop down list. Then on the post back, I split and compare the NodeId and the NodeType when trying to match the node.

Code: Select all

public partial class Admin_Catalog_SortCategory : CommerceBuilder.Web.UI.AbleCommerceAdminPage
{


    private int _CategoryId;
    private Category _Category;
    private CatalogNodeCollection _CatalogNodes;
    protected void Page_Load(object sender, EventArgs e)
    {
        _CategoryId = PageHelper.GetCategoryId();
        _Category = CategoryDataSource.Load(_CategoryId);
        _CatalogNodes = CatalogDataSource.LoadForCategory(_CategoryId, false);
        if (_CatalogNodes != null && _CatalogNodes.Count > 0)
        {
            if (_Category == null)
                Caption.Text += " Sort Items in Root Category";
            else
                Caption.Text += " Sort Items in " + _Category.Name;
            if (!Page.IsPostBack)
            {
                BindCatalogNodesList();
            }
        }
        else
            Response.Redirect("Browse.aspx");
    }

    protected void CancelButton_Click(object sender, EventArgs e)
    {
        Response.Redirect("Browse.aspx?CategoryId=" + _CategoryId.ToString());
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(SortOrder.Value))
        {
            CatalogNodeCollection catalogNodes = _CatalogNodes;

            string[] catalogNodesIds = SortOrder.Value.Split(",".ToCharArray());
            int order = 0;
            int tempId = 0;
            int tempTypeId = 0;
            int index = -1;
            foreach (string sPartId in catalogNodesIds)
            {
                string[] idParts = sPartId.Split('|');
                tempId = AlwaysConvert.ToInt(idParts[0]);
                tempTypeId = AlwaysConvert.ToInt(idParts[1]);

                foreach (CatalogNode cn in _CatalogNodes)
                {
                    //tempId = AlwaysConvert.ToInt(sPartId);
                    if (cn.CatalogNodeId == tempId && cn.CatalogNodeTypeId == tempTypeId)
                    {
                        index = catalogNodes.IndexOf(cn);
                        if (index > -1)
                            catalogNodes[index].OrderBy = (short)order;
                        order++;
                    }
                }
            }
            catalogNodes.Save();
        }
        Response.Redirect("Browse.aspx?CategoryId=" + _CategoryId.ToString());
    }

    protected void QuickSort_SelectedIndexChanged(object sender, EventArgs e)
    {
        CatalogNodeCollection catalogNodes = new CatalogNodeCollection();
        foreach (CatalogNode catalogNode in _CatalogNodes)
        {
            catalogNodes.Add(catalogNode);
        }
        switch (QuickSort.SelectedIndex)
        {
            case 2:
                catalogNodes.Sort("Name", GenericComparer.SortDirection.DESC);
                break;
            default:
                catalogNodes.Sort("Name", GenericComparer.SortDirection.ASC);
                break;
        }
        List<CatalogNodeListItem> items = ConvertToListItems(catalogNodes);
        CatalogNodeList.DataSource = items;
        CatalogNodeList.DataBind();
        //CatalogNodeList.DataSource = catalogNodes;
        //CatalogNodeList.DataBind();
        QuickSort.SelectedIndex = 0;
    }

    private void BindCatalogNodesList()
    {        
        /*
        CatalogNodeCollection catalogNodes = new CatalogNodeCollection();
        foreach (CatalogNode catalogNode in _CatalogNodes)
        {
            catalogNodes.Add(catalogNode);
        }
        CatalogNodeList.DataSource = catalogNodes;
        CatalogNodeList.DataBind();
        */
        List<CatalogNodeListItem> items = ConvertToListItems(_CatalogNodes);
        CatalogNodeList.DataSource = items;
        CatalogNodeList.DataBind();
    }

    private List<CatalogNodeListItem> ConvertToListItems(CatalogNodeCollection catalogNodes)
    {
        List<CatalogNodeListItem> items = new List<CatalogNodeListItem>();
        foreach (CatalogNode catalogNode in catalogNodes)
        {
            CatalogNodeListItem item = new CatalogNodeListItem();
            item.CatalogNodeId = catalogNode.CatalogNodeId + "|" + catalogNode.CatalogNodeTypeId;
            item.Name = catalogNode.Name;
            items.Add(item);
        }
        return items;
    }

    public class CatalogNodeListItem
    {
        public string CatalogNodeId { get; set; }
        public string Name { get; set; }
    }
}

Post Reply