Overriding controller and view in nopCommerce 4.0

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
6 years ago
I have problem with overriding controller and view with a plugin in nopCommerce 4.0

I want to override CategoryController and replace the _CreateOrUpdate.Info.cshtml view with _CreateOrUpdateCustom.Info.cshtml view.

Here is my controller and routes:

[Area(AreaNames.Admin)]
    public partial class CustomCategoryController : Nop.Web.Areas.Admin.Controllers.CategoryController
    {
        #region Fields

        private readonly ICategoryService _categoryService;
        private readonly ICategoryTemplateService _categoryTemplateService;
        private readonly IManufacturerService _manufacturerService;
        private readonly IProductService _productService;
        private readonly ICustomerService _customerService;
        private readonly IUrlRecordService _urlRecordService;
        private readonly IPictureService _pictureService;
        private readonly ILanguageService _languageService;
        private readonly ILocalizationService _localizationService;
        private readonly ILocalizedEntityService _localizedEntityService;
        private readonly IDiscountService _discountService;
        private readonly IPermissionService _permissionService;
        private readonly IAclService _aclService;
        private readonly IStoreService _storeService;
        private readonly IStoreMappingService _storeMappingService;
        private readonly IExportManager _exportManager;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly IVendorService _vendorService;
        private readonly CatalogSettings _catalogSettings;
        private readonly IWorkContext _workContext;
        private readonly IImportManager _importManager;
        private readonly IStaticCacheManager _cacheManager;
        private readonly IErpCategoryService _erpCategoryService;
        private IRepository<ErpCategory> _erpCategory;

        #endregion

        #region Constructors

        public CustomCategoryController(ICategoryService categoryService, ICategoryTemplateService categoryTemplateService,
            IManufacturerService manufacturerService, IProductService productService,
            ICustomerService customerService,
            IUrlRecordService urlRecordService,
            IPictureService pictureService,
            ILanguageService languageService,
            ILocalizationService localizationService,
            ILocalizedEntityService localizedEntityService,
            IDiscountService discountService,
            IPermissionService permissionService,
            IAclService aclService,
            IStoreService storeService,
            IStoreMappingService storeMappingService,
            IExportManager exportManager,
            IVendorService vendorService,
            ICustomerActivityService customerActivityService,
            CatalogSettings catalogSettings,
            IWorkContext workContext,
            IImportManager importManager,
            IStaticCacheManager cacheManager,
            IErpCategoryService erpCategoryService,
            IRepository<ErpCategory> erpCategory)
            : base(categoryService, categoryTemplateService, manufacturerService, productService, customerService, urlRecordService, pictureService,
            languageService, localizationService, localizedEntityService, discountService, permissionService, aclService, storeService, storeMappingService,
            exportManager, vendorService, customerActivityService, catalogSettings, workContext, importManager, cacheManager)
        {
            this._categoryService = categoryService;
            this._categoryTemplateService = categoryTemplateService;
            this._manufacturerService = manufacturerService;
            this._productService = productService;
            this._customerService = customerService;
            this._urlRecordService = urlRecordService;
            this._pictureService = pictureService;
            this._languageService = languageService;
            this._localizationService = localizationService;
            this._localizedEntityService = localizedEntityService;
            this._discountService = discountService;
            this._permissionService = permissionService;
            this._vendorService = vendorService;
            this._aclService = aclService;
            this._storeService = storeService;
            this._storeMappingService = storeMappingService;
            this._exportManager = exportManager;
            this._customerActivityService = customerActivityService;
            this._catalogSettings = catalogSettings;
            this._workContext = workContext;
            this._importManager = importManager;
            this._cacheManager = cacheManager;
            this._erpCategoryService = erpCategoryService;
            this._erpCategory = erpCategory;
        }

        #endregion
        
        #region Create / Edit / Delete

        [ActionName("CustomCreate")]
        new public IActionResult Create()
        {
            var model = new AdminCategoryModel();

            //default values
            model.ErpCategoryId = "";

            IActionResult actionResult = base.Create();
            return View("Create", ((ViewResult)actionResult).Model);
        }

        [ActionName("CustomCreate")]
        [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
        public IActionResult Create(AdminCategoryModel model, bool continueEditing)
        {
            if (ModelState.IsValid)
            {
                var category = model.ToEntity();
                var nextId = _erpCategoryService.GetTableId();

                if (model.ErpCategoryId != null)
                {
                    // Setup the product to save
                    var record = new ErpCategory();
                    record.CategoryId = nextId.GetValueOrDefault() + 1;
                    record.ErpCategoryId = model.ErpCategoryId;

                    // Map the values we're interested in to our new entity
                    _erpCategoryService.InsertErpCategory(record);
                }

                IActionResult actionResult = base.Create(model, continueEditing);
                if (continueEditing)
                {
                    //selected tab
                    SaveSelectedTabName();

                    return RedirectToAction("Edit", new { id = nextId.GetValueOrDefault() + 1 });
                }
                return RedirectToAction("List");
            }

            return View("Create", model);
        }
}



This is the RouteProvider.cs from the plugin:


public partial class RouteProvider : IRouteProvider
    {
        public void RegisterRoutes(IRouteBuilder routes)
        {
            routes.MapAreaRoute(
                    name: "Nop.Plugin.Misc.EsoftErpConnector.CreateOrUpdate",
                    areaName: AreaNames.Admin,
                    template: "Admin/Category/Create",
                    defaults: new { controller = "CustomCategory", action = "CustomCreate" }
                );
        }

        public int Priority
        {
            get { return -1; } // I try with 1 but that is not working to
        }
    }


Any help would be more than welcome :)
6 years ago
No need to inherit admin controller.
you can override by using route only and use priority int.MaxValue
You can try below code.


controller :
 public class CustomOrderController : BasePluginController
    {
        #region Fields

        private readonly ILocalizationService _localizationService;
        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly IPermissionService _permissionService;
        private readonly IShippingService _shippingService;

        #endregion
        public CustomOrderController(
            ILocalizationService localizationService,
            ICountryService countryService,
            IStateProvinceService stateProvinceService,
            IPermissionService permissionService,
            IShippingService shippingService)
        {
            this._localizationService = localizationService;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._permissionService = permissionService;
            this._shippingService = shippingService;
        }

        public IActionResult ShipmentList()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = new ShipmentListModel();
            //countries
            model.AvailableCountries.Add(new SelectListItem { Text = "*", Value = "0" });
            foreach (var c in _countryService.GetAllCountries(showHidden: true))
                model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString() });
            //states
            model.AvailableStates.Add(new SelectListItem { Text = "*", Value = "0" });

            //warehouses
            model.AvailableWarehouses.Add(new SelectListItem { Text =
             _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var w in _shippingService.GetAllWarehouses())
                model.AvailableWarehouses.Add(new SelectListItem { Text = w.Name, Value = w.Id.ToString() });

            //For view give full path of your published plugin
            return View("~/Plugins/Payments.PayPalDirect/Views/CustomOrder/ShipmentList.cshtml", model);
        }
    }





Route provider :

public partial class RouteProvider : IRouteProvider
    {
        /// <summary>
        /// Register routes
        /// </summary>
        /// <param name="routeBuilder">Route builder</param>
        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {
            routeBuilder.MapRoute("CustomShipment", "Admin/Order/ShipmentList",
                new { controller = "CustomOrder", action = "ShipmentList" });
        }

        /// <summary>
        /// Gets a priority of route provider
        /// </summary>
        public int Priority
        {
            get { return int.MaxValue; }
        }
    }
6 years ago
chetan.bhaliya wrote:
No need to inherit admin controller.
you can override by using route only and use priority int.MaxValue
You can try below code.


controller :
 public class CustomOrderController : BasePluginController
    {
        #region Fields

        private readonly ILocalizationService _localizationService;
        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly IPermissionService _permissionService;
        private readonly IShippingService _shippingService;

        #endregion
        public CustomOrderController(
            ILocalizationService localizationService,
            ICountryService countryService,
            IStateProvinceService stateProvinceService,
            IPermissionService permissionService,
            IShippingService shippingService)
        {
            this._localizationService = localizationService;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._permissionService = permissionService;
            this._shippingService = shippingService;
        }

        public IActionResult ShipmentList()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = new ShipmentListModel();
            //countries
            model.AvailableCountries.Add(new SelectListItem { Text = "*", Value = "0" });
            foreach (var c in _countryService.GetAllCountries(showHidden: true))
                model.AvailableCountries.Add(new SelectListItem { Text = c.Name, Value = c.Id.ToString() });
            //states
            model.AvailableStates.Add(new SelectListItem { Text = "*", Value = "0" });

            //warehouses
            model.AvailableWarehouses.Add(new SelectListItem { Text =
             _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var w in _shippingService.GetAllWarehouses())
                model.AvailableWarehouses.Add(new SelectListItem { Text = w.Name, Value = w.Id.ToString() });

            //For view give full path of your published plugin
            return View("~/Plugins/Payments.PayPalDirect/Views/CustomOrder/ShipmentList.cshtml", model);
        }
    }





Route provider :

public partial class RouteProvider : IRouteProvider
    {
        /// <summary>
        /// Register routes
        /// </summary>
        /// <param name="routeBuilder">Route builder</param>
        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {
            routeBuilder.MapRoute("CustomShipment", "Admin/Order/ShipmentList",
                new { controller = "CustomOrder", action = "ShipmentList" });
        }

        /// <summary>
        /// Gets a priority of route provider
        /// </summary>
        public int Priority
        {
            get { return int.MaxValue; }
        }
    }


Is this working ?
6 years ago
rajupaladiya wrote:

Is this working ?


Yes, It is working
5 years ago
Thought I would just resurrect this topic as I am having a similar issue and I can't seem to get it to work

I want to override the control for showing the "/Admin/Product/List" page to add a few more buttons and show some more columns. I have the control working fine on "/Admin/CustomProducts/List" but I can't get it to override the standard view

This is what I have in the RouteProvider



namespace Nop.Plugin.Custom.Products
{
    public partial class RouteProvider : IRouteProvider
    {
        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {
            routeBuilder.MapRoute("Plugin.Custom.Products.admin_override", "Admin/Product/List",
                 new { controller = "Nop.Plugin.Custom.Products.Controllers", action = "List" });

        }
        public int Priority
        {
            get
            {
                return int.MaxValue;
            }
        }
    }
}



What have I missed?
5 years ago
Since, I've been struggling to get this to work, I would like to share my experiences. For starters, I think this stack overflow post deserves a mention since it put me in the right direction https://stackoverflow.com/questions/50525834/nopcommerce-customization.

Additionally, I also would like to add that what @chetan.bhaliya has mentioned is partially correct. Because with what he's mentioned you should be able to get MVC to load your view. Because it worked for me too. So thank you Chetan.

Now with asp.net core you can provide additional locations for the Razor Engine to search for Views using ViewLocationExpander, but I would highly recommend you not to. Its a lot easier to get things working by specifying the entire plugin path as Chetan has shown.

I was trying to customise the Store page. Specifically the Store Edit page. So when I followed @chetan.bhaliya instructions, it worked for me. This is how my RouteProvider looks like

routes.MapLocalizedRoute("Admin.Plugin.StoreEdit",
                  "Admin/Store/Edit/{id}",
                  new { controller = "PluginStore", action = "Edit"},
                  new {},
                  new[] { "Plugin.Admin.Controllers" }
              );
public int Priority => 100;

But when I added

@{
    Layout = "_AdminLayout";
}


So that I could use the AdminLayout the page crashed saying that it could not find AdminLayout. Some more googling
brought me to this link https://www.nopcommerce.com/boards/t/49415/do-we-still-use-_adminlayoutcshtml-in-plugins.aspx. Here the MVP mentions that decorating the plugin controller with the area attribute fixes the problem, but then again another guy says that it now breaks his route which is true. In his case he could get around it since he wasn't overriding an admin page, but I was. So some more trial and error finally got me what I wanted. I removed the Area attribute from my plugin controller and inherited it from BaseAdminController. And then I changed my RouteProvider to this


routes.MapLocalizedRoute("Labfriend.Admin.Plugin.StoreEdit",
                  "Admin/Store/Edit/{id}",
                  new { controller = "LabfriendStore", action = "Edit" , area = "Admin"},//notice the new area
                  new {},
                  new[] { "LabFriend.Plugin.Admin.Controllers" }
              );


And now my page overrides Store Edit page and I can see the admin menu and all other admin headers :).

Additionally if you need to reuse any Partial Views. Like in my case _CreateOrUpdate from the Store folder, you need to give the complete path like this

@await Html.PartialAsync("/Areas/Admin/Views/Store/_CreateOrUpdate.cshtml", Model)


Hope this helps someone. If it does. Please upvote :)
5 years ago
Thanks, mshenoy83! That fixed the problem I was having with a plugin in 4.10.  It's that extra new{} in the route which made all the difference for me.
5 years ago
@mshenoy83 But is this solution work without removing default route provided for admin action ?
I refer this link to remove default admin route
https://stackoverflow.com/questions/51091247/remove-route-from-routecollection-in-asp-net-core-and-add-new-with-same-route-na
5 years ago
I thought I would post what worked for me although, I was trying to override a non-generic route in the public store.  I am using nopCommerce 4.10

I was facing a similar issue with trying to override the "/customer/downloadableproducts". I tried various ways to override the route, I tried to register the custom controller, which inherited from the CustomerController.  None of those methods were working.

I finally got it working by using the Route attribute:
[Route("customer/downloadableproducts")]

on my custom controller action method.  I didn't register a new route, I didn't register the custom controller and I didn't inherit from the original CustomerController.  Hopefully this helps someone.
5 years ago
If you used this method after plugin uninstall your url hit this action, Did you check this case ?
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.