Suggestion - CMS

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
11 years ago
Hello, for our new portal we are looking for an open source eshop project allowing some basic CMS options. We will offer some microchip modules, kits, accessories and provide support for them - documentation, libraries, source code examples on the static pages (via CMS module).
For correct and transparent arrangement of topics we would prefer multilevel/cascade menu for CMS topics instead of the current list. Don't you plan it? Thanks.
11 years ago
Hi Richard,

I'm not sure a CMS for topic pages is a high priority as the primary purpose of the site is a shopping cart?

However I have a suggestion for you which may help.

The categories use a category template;

Presentation\Nop.Web\Views\Catalog\CategoryTemplate.ProductsInGridOrLines.cshtml

You can add further templates which format the page in a different manner.

So you could create a Topic Category Template and use that for topic pages within the category structure.

With a bit of further work you could filter by template type if you wanted to separate the menus.

Look at \Presentation\Nop.Web\Controllers\CatalogController.cs methods CategoryNavigation & GetChildCategoryNavigationModel

HTH

Dave
11 years ago
Thanks, I'll try it.
If we're talking about eShop and CMS you are right these are two different projects. But for a small company as we are (especially companies producing some products) there is no open source eshop allowing to add any other section with information next to eShop. Just info about shop, shipment rules and so on ... Thats' why we need a simple CMS with category and subcategory of topics. Just idea...
Thanks you for your response.
11 years ago
RichardLukes wrote:
Hello, for our new portal we are looking for an open source eshop project allowing some basic CMS options. We will offer some microchip modules, kits, accessories and provide support for them - documentation, libraries, source code examples on the static pages (via CMS module).
For correct and transparent arrangement of topics we would prefer multilevel/cascade menu for CMS topics instead of the current list. Don't you plan it? Thanks.

Someone else is using the forum feature as a knowledge base. Check here and here. Besides there is a work item on the road map for v2.7 (end of december); check and vote it
11 years ago
So having made the suggestion of using Category Templates to implement a CMS feature I have got round to doing it.

This is going to be a multi-part step by step guide as there are going to be parts you want and others where you branch of on your own route.

What I am going to do is create a new Category Template Type (page category) which we will use much like a topic. In addition I'm guessing that some people will not want these page categories appearing in the left hand Category Navigation but somewhere else. Of course some will want those page categories included in the Category Navigation.

In part 2 we will customise the page template to display correctly, handling subcategories etc.

I've got some plans for part 3 but I'm keeping those under my hat for now.

To follow these steps you will need the source code version of 2.65 (it may well work with little or no modification on earlier versions back to 2.0 {untested} and the concept is certainly applicable to versions 1.7-1.9).

Note; We are modifying some core files in nop so you will not be able to upgrade without some work however the modifications are not that large. Quite possibly this could be done as a plugin but I haven't got my head around that. Feel free to do that if you want.

On to the steps.

1. find file Presentation\Nop.Web\Views\Catalog\CategoryTemplate.ProductsInGridOrLines.cshtml

copy the file and paste it into the same folder

it will appear as Presentation\Nop.Web\Views\Catalog\Copy of CategoryTemplate.ProductsInGridOrLines.cshtml

rename it to CategoryTemplate.PageType1.cshtml

2. Run the following code against the nop database replace YOURNOPDBNAMEHERE with the name of your nop database.

INSERT INTO [YOURNOPDBNAMEHERE].[dbo].[CategoryTemplate]
           ([Name]
           ,[ViewPath]
           ,[DisplayOrder])
     VALUES
           ('Page Type 1'
           ,'CategoryTemplate.PageType1'
           ,20)
GO


3. Run the site in Visual Studio and check that this has worked by creating a new category 'Test Page' and selecting the Page Type 1 as the category template. You don't need to enter any other data at this point.

While your at it you need to find out what ID has been assigned to this page type. I've no idea what DB management tools you are using but if you are stuck right click on the web page when you are adding the new category and select view source. Then find the text 'Page Type 1' and look at the value before it. This is the categoryTemplateId we need. It will look like this if you have not added any other category templates;-

<option value="1">Products in Grid or Lines</option>
<option value="2">Page Type 1</option>


Make a note of the Page Type 1 value i.e. its categoryTemplateId

4. Go back to the front end of nop and you will see the new category in the left hand category navigation. We want to gain some control over the display of categories in this navigation and we are going to do this based on which template the category uses.

5. To do this we need to know which category template a category uses when we write out the category navigation. unfortunately this information is not in the Category Navigation Model so we need to add it.

Open file Presentation\Nop.Web\Models\Catalog\CategoryNavigationModel.cs

and replace the code with the following;

using Nop.Web.Framework.Mvc;

namespace Nop.Web.Models.Catalog
{
    public partial class CategoryNavigationModel : BaseNopEntityModel
    {
        public string Name { get; set; }

        public string SeName { get; set; }

        public int NumberOfParentCategories { get; set; }

        public bool DisplayNumberOfProducts { get; set; }
        public int NumberOfProducts { get; set; }

        public bool IsActive { get; set; }

        // added for template driven navigation menus
        public int CategoryTemplateId { get; set; }
    }
}


Now I could have placed the added code in a new class file extending the CategoryNavigationModel as this is a partial class but I'm being lazy.

6. Now we need to populate this value

find file Presentation\Nop.Web\Controllers\CatalogController.cs

and find method GetChildCategoryNavigationModel

replace the method with the following code

[NonAction]
        protected IList<CategoryNavigationModel> GetChildCategoryNavigationModel(IList<Category> breadCrumb, int rootCategoryId, Category currentCategory, int level)
        {
            var result = new List<CategoryNavigationModel>();
            foreach (var category in _categoryService.GetAllCategoriesByParentCategoryId(rootCategoryId))
            {
                // added CategoryTemplateId for template driven navigation menus
                var model = new CategoryNavigationModel()
                {
                    Id = category.Id,
                    Name = category.GetLocalized(x => x.Name),
                    SeName = category.GetSeName(),
                    IsActive = currentCategory != null && currentCategory.Id == category.Id,
                    NumberOfParentCategories = level,
                    CategoryTemplateId = category.CategoryTemplateId
                };

                if (_catalogSettings.ShowCategoryProductNumber)
                {
                    model.DisplayNumberOfProducts = true;
                    var categoryIds = new List<int>();
                    categoryIds.Add(category.Id);
                    if (_catalogSettings.ShowCategoryProductNumberIncludingSubcategories)
                    {
                        //include subcategories
                        categoryIds.AddRange(GetChildCategoryIds(category.Id));
                    }
                    IList<int> filterableSpecificationAttributeOptionIds = null;
                    model.NumberOfProducts = _productService.SearchProducts(categoryIds,
                        0, null, null, null, 0, string.Empty, false, false, 0, null,
                        ProductSortingEnum.Position, 0, 1,
                        false, out filterableSpecificationAttributeOptionIds).TotalCount;
                }
                result.Add(model);

                for (int i = 0; i <= breadCrumb.Count - 1; i++)
                    if (breadCrumb[i].Id == category.Id)
                        result.AddRange(GetChildCategoryNavigationModel(breadCrumb, category.Id, currentCategory, level + 1));
            }

            return result;
        }


7. Now we need to modify the Category Navigation view to exclude certain category templates. We want to provide some control over which ones are included so we will use a setting.

Run the following SQL code against your DB remembering to replace YOURNOPDBNAMEHERE with the name of your nop database.

INSERT INTO [YOURNOPDBNAMEHERE ].[dbo].[Setting]
           ([Name]
           ,[Value])
     VALUES
           ('catalogsettings.categorynavigationallowedtemplateids'
           ,'1')
GO


Note if you already have more category templates that you have created that you wish to include in the category navigation you need to add them after the 1 separated by commas eg '1,2,3,etc'

and

INSERT INTO [YOURNOPDBNAMEHERE ].[dbo].[Setting]
           ([Name]
           ,[Value])
     VALUES
           ('catalogsettings.menunavigationallowedtemplateids'
           ,'2')
GO


Note if your categoryTemplateId that you noted in step 3 is not 2 then change the 2 to whatever above

we are going to use the second setting later but may as well add it now

8. Find File Libraries\Nop.Core\Domain\Catalog\CatalogSettings.cs

and replace the CatalogSettings() method with

public CatalogSettings()
        {
            FileUploadAllowedExtensions = new List<string>();
            // DMB 16/11/2012 added
            CategoryNavigationAllowedTemplateIds = new List<string>();
            MenuNavigationAllowedTemplateIds = new List<string>();
        }


and at the bottom of CatalogSettings.cs add the following code

/// <summary>
        /// added
        /// Gets or sets a list of allowed template ids for category navigation
        /// </summary>
        public List<string> CategoryNavigationAllowedTemplateIds { get; set; }

        /// <summary>
        /// added
        /// Gets or sets a list of allowed template ids for menu navigation
        /// </summary>
        public List<string> MenuNavigationAllowedTemplateIds { get; set; }


Note we are also adding code to handle the second setting now.

I was really impressed with how easy it is to add settings to Nop and as you will see they are very easy to access even from the views.

9. Now we can modify the category navigation

find file Presentation\Nop.Web\Views\Catalog\CategoryNavigation.cshtml

after line 7 add the following lines

// added for filtering based of category templates
var allowedTemplateIdList = EngineContext.Current.Resolve<CatalogSettings>().MenuNavigationAllowedTemplateIds;


so it looks like

@{
    var categoryPadding = 15;
    // added for filtering based of category templates
    var allowedTemplateIdList = EngineContext.Current.Resolve<CatalogSettings>().CategoryNavigationAllowedTemplateIds;
}


and  replace the main block of code (starts line 19 or 21 after the above change) with

@foreach (var category in Model)
                {
                    // added to filter out those templates not displayed here
                    if (allowedTemplateIdList.Contains(category.CategoryTemplateId.ToString()))
                    {
                        <li class="@(category.IsActive ? "active" : "inactive")"
                        @if (category.NumberOfParentCategories > 0)
                        {
                            if (this.ShouldUseRtlTheme())
                            {
                            <text>style="margin-right: @(category.NumberOfParentCategories * categoryPadding)px"</text>
                            }
                            else
                            {
                            <text>style="margin-left: @(category.NumberOfParentCategories * categoryPadding)px"</text>
                            }
                        }
                        ><a href="@Url.RouteUrl("Category", new { categoryId = category.Id, SeName = category.SeName })">@category.Name
                            @if (category.DisplayNumberOfProducts)
                            {
                                <text> (@(category.NumberOfProducts))</text>
                            }
                        </a></li>
                    }
                }


10. Run the project and check that the Test Page category is no longer being displayed by the Category Navigation. Great but now we need to display an alternate navigation for our pages.

There are going to be numerous options/requirements for this but I'm going to create a dynamic navigation block for pages below the Category Navigation.

11. Find file Presentation\Nop.Web\Views\Catalog\CategoryNavigation.cshtml

copy the file and paste it into the same folder

it will appear as Presentation\Nop.Web\Views\Catalog\Copy of CategoryNavigation.cshtml

rename it to MenuNavigation.cshtml

change line 9 to

var allowedTemplateIdList = EngineContext.Current.Resolve<CatalogSettings>().MenuNavigationAllowedTemplateIds;


and line 15 to

@T("Pages")


12. Find file Presentation\Nop.Web\Controllers\CatalogController.cs

and add the following method (I would place it below the CategoryNavigation Method)

//added
        [ChildActionOnly]
        //[OutputCache(Duration = 120, VaryByCustom = "WorkingLanguage")]
        public ActionResult MenuNavigation(int currentCategoryId, int currentProductId)
        {
            // this should only change when the current category is in this tree
            // if they are not in the tree then return the tree for currentCategoryId = 0
            // am ignoring products for now due to multiple category parents
            var currentCategory = _categoryService.GetCategoryById(currentCategoryId);

            if (currentCategory != null)
            {
                var allowedTemplateIdList = _catalogSettings.MenuNavigationAllowedTemplateIds;

                if (!allowedTemplateIdList.Contains(currentCategory.CategoryTemplateId.ToString()))
                {
                    currentCategoryId = 0;
                    currentCategory = _categoryService.GetCategoryById(currentCategoryId);
                }
            }

            string cacheKey = string.Format(ModelCacheEventConsumer.CATEGORY_MENU_MODEL_KEY, currentCategoryId, currentProductId, _workContext.WorkingLanguage.Id);
            var cacheModel = _cacheManager.Get(cacheKey, () =>
            {
                //var currentCategory = _categoryService.GetCategoryById(currentCategoryId);
                if (currentCategory == null && currentProductId > 0)
                {
                    var productCategories = _categoryService.GetProductCategoriesByProductId(currentProductId);
                    if (productCategories.Count > 0)
                        currentCategory = productCategories[0].Category;
                }
                var breadCrumb = currentCategory != null ? GetCategoryBreadCrumb(currentCategory) : new List<Category>();
                var model = GetChildCategoryNavigationModel(breadCrumb, 0, currentCategory, 0);
                return model;
            });

            return PartialView(cacheModel);
        }


13. Find file Presentation\Nop.Web\Infrastructure\Cache\ModelCacheEventConsumer.cs

add the following (I placed it below the entries for CATEGORY_NAVIGATION_MODEL_KEY)

/// <summary>
        /// added
        /// Key for CategoryMenuModel caching
        /// </summary>
        /// <remarks>
        /// {0} : current category id
        /// {1} : current product id
        /// {2} : language id
        /// </remarks>
        public const string CATEGORY_MENU_MODEL_KEY = "nop.pres.category.navigation.menu-{0}-{1}-{2}";
        public const string CATEGORY_MENU_PATTERN_KEY = "nop.pres.category.navigation.menu";


14. Finally :) find file Presentation\Nop.Web\Views\Shared\_ColumnsThree.cshtml

Place the following code below the action to create the Category navigation

@Html.Action("MenuNavigation", "Catalog", new { currentCategoryId = currentCategoryId, currentProductId = currentProductId })
        <div class="clear">
        </div>


so it looks like this (partial)

@Html.Action("CategoryNavigation", "Catalog", new { currentCategoryId = currentCategoryId, currentProductId = currentProductId })
        <div class="clear">
        </div>
        @Html.Action("MenuNavigation", "Catalog", new { currentCategoryId = currentCategoryId, currentProductId = currentProductId })
        <div class="clear">
        </div>
        @Html.Action("ManufacturerNavigation", "Catalog", new { currentManufacturerId = currentManufacturerId })
        <div class="clear">
        </div>


Run it in Visual Studio and check that you now have an additional navigation block below the categories titled Pages.

Some Notes

The ids that are included in each navigation block are stored as a comma separated list so you can have more than one template in each navigation. You can even have a template in both blocks.

You should be able to create navigation elements anywhere so for instance you could have navigation in the footer or header tho you wouldn't want to use the Category Navigation as it currently works.  

In the next part I will go over various customisations of the Category Page Template including modifying how the subcategories display and removing various unwanted items.

If anyone spots any errors or has any queries let me know.
11 years ago
Slight bug fix (I've updated the code above)

Fixed code is in file Presentation\Nop.Web\Infrastructure\Cache\ModelCacheEventConsumer.cs

public const string CATEGORY_MENU_MODEL_KEY = "nop.pres.category.navigation.menu-{0}-{1}-{2}";
        public const string CATEGORY_MENU_PATTERN_KEY = "nop.pres.category.navigation.menu";


Because adding new categories was not clearing the cache for the new navigation. I figured it was easier to do it this way than to specifically clear this cache in numerous different places within ModelCacheEventConsumer.cs.
11 years ago
Nop2.65 & Nop2.70

Stage 6

I can not find method GetChildCategoryNavigation, only GetChildCategoryIds
11 years ago
Definitely there in 2.65, it's called GetChildCategoryNavigationModel line 415
11 years ago
Still encountering errors at stage 6, however the category template solution is a good one for extending content functionality. Nop 2.80 has some changes to the CategoryNavigationModel - not sure how that affects your code modifications. I'll consult my C# / software developers when they get a spare minute. In the meantime I'm halfway to a simpler css/jscript header menu for implementing topics as sub items as a dropdown (something like the information block as a header menu item), similar to the nopCommerce.com headerMenu.

*EDIT* Now using the manufacturers menu as it allows an unordered list of sub-items.
11 years ago
Yeah looks like things changed in 2.7+

Looks like the method is called PrepareCategoryNavigationModel now and there are some differences.

I'm unable to work with 2.7 so unlikely to update this code anytime soon but it should be trivial.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.