Overriding NopCommerce Admin Views and Partial Views

Overriding NopCommerce Admin Views is often a topic of confusion for Nop developers, but the task seems more difficult than it really is.  If you haven’t read my other blog post, Overriding and Adding Views in a NopCommerce Plugin,  I recommend you do before reading this.  The other reading isn’t required to understand this post, particularly if you’re adept at MVC, but it provides some additional insights.  This post is meant to be a comprehensive overview of Overriding Admin views – I will try to strike a balance between conceptual and coding examples.

So anyway, the easiest way to override a NopCommerce Admin View is to create a Custom View Engine that extends the Razor View Engine to search your plugin.  For full Views (meaning not partial views) you also need to override the corresponding Admin route and Controller Action if you want to alter logic.  This process is really no different than overriding other NopCommerce views in Nop.Web, but the Admin routes are a bit trickier because they use MVC areas.  Remember, MVC areas are like large sections of an MVC application that can contain their own controllers, views and routes.  They are a way of easily seperating major portions of your application, such as an admin area and a user facing area (as is the case with Nop).

Remember, a View Engine is responsible for finding and rendering our views.  By default the NopCommerce View Engine (ThemableRazorViewEngine) does not know it needs to search our plugin for views.  We have to create a View Engine that is aware of our plugin and proritizes it in its search locations.

There are basically four major steps to making this whole process work.  Again, I cover this overall concept in more depth in my other post, here I am just providing the general steps.  This process also has an added benefit in that it allows you to override only the view (for example, styling/layout changes), or the view AND its controller action method logic if you so wish.

In this example we will be overriding the Edit view of the Products page in the Nop Admin.

This post assumes you have a basic, empty NopCommerce plugin created (or one already created that you’re working on) and are at least generally familiar with NopCommerce development.  My plugin shown here is called Nop.Plugin.Misc.HelloWorld.

Step 1 – Create the Custom View Engine

Create a new class that inherits from the RazorViewEngine.  This will expose a few search location properties that we can add our plugin to.  Notice that I am NOT inheriting from ThemeableRazorViewEngine like in my other blog post.  The reason for this is because the ThemeableRazorViewEngine adds additional search locations for areas and the Nop Admin.   We don’t want this. If we inherit from ThemeableRazorViewEngine, the base class search locations will be registered BEFORE our search locations, which means in some scenarios the views we define in our plugin will not take priority.  This mostly applies to Partial Views, which have no Route that can be intercepted and overriden.

Basically we need to grab a sledgehammer and make sure that our custom View Engine searches our plugin first for all views, regardless of whether they are in areas or if they are partials, etc.  By inheriting from the Razor View Engine we can guarantee this.  Because Nop will still register its own view engine as well in global.asax, we don’t have to worry about losing Nop functionality.  Our View Engine will be inserted first, meaning anything it doesn’t find, NopCommerce’s engine can take a stab at.  MVC can handle multiple view engines at once.  We also need to make sure we map our plugin’s search location to both the regular search locations and the area search locations – again, we want to make sure our plugin has priority in all scenarios.

public class CustomViewEngine : RazorViewEngine
    {
        public CustomViewEngine()
        {
            PartialViewLocationFormats = new[] { "~/Plugins/Misc.HelloWorld/Views/{0}.cshtml" };
            ViewLocationFormats = new[] { "~/Plugins/Misc.HelloWorld/Views/{0}.cshtml" };
 
            AreaPartialViewLocationFormats = new[] { "~/Plugins/Misc.HelloWorld/Views/{0}.cshtml" };
            AreaViewLocationFormats = new[] { "~/Plugins/Misc.HelloWorld/Views/{0}.cshtml" };
        }
    }

Step 2 – Register the Custom View Engine and Override the Route for the Admin View you are trying to replace.

To actually implement our View Engine, add the code below to your RouteProvider.cs file (or whatever one implements IRouteProvider).  Make sure you get the parameters for Custom Routes that override Admin Routes exactly right – specifying the MVC area and the URL parameters is very important.  Also make sure you register your Custom View Engine using the Insert method with an index parameter of 0.  This will make sure your View Engine gets evaluated first like we have in the image above.  We also need to remove the Route and then add it back to make sure that it gets inserted before the other MVC area routes.  MVC Area routes are registered before other routes in MVC by default, so we have to manually prioritize ours (Thanks to Twisted Whisper on stackoverflow for this tip).

ViewEngines.Engines.Insert(0, new CustomViewEngine());
 
            var route = routes.MapRoute("Plugin.Misc.HelloWorld.Edit",
                 "Admin/Product/Edit/{id}",
                 new { controller = "HelloWorld", action = "Edit", area = "Admin" }, //notice 'area="Admin"' is added
                 new { id = @"\d+" },
                 new[] { "Nop.Plugin.Misc.HelloWorld.Controllers" }
            );
            routes.Remove(route);
            routes.Insert(0, route); 

The image shown below is an example of a useful error page that gets displayed when the View Engines can’t find an appropriate view – MVC lists all the locations it searched. Basically after we implement our custom view engine we will get the following search location order, which is what we want.  The very top search location is the one defined in our Custom View Engine.  Notice it gets checked first in front of all other scenarios, but the default Nop search locations are still intact after it.

Admin-Search-Locations

Step 3 – Provide a Controller method that our override Route will map to.

Make sure you name your controller and action method properly.  In this case our action method should be the same as our view, and the same as we specified in the route.  We will simply call it Edit.  Because we have named our View the same as the action method and added our plugin as a View Engine search location, MVC will find the view based on standard naming conventions.

public ActionResult Edit()
        {
            return View();
        }

Step 4 – Create a new View file that will replace the View you are trying to override.

In this case we want to override the Edit View for our products, so I have added Edit.cshtml to a Views folder at the root of our project.  Make sure you set “Copy to Output Directory” in the properties of the view to “Always”. Also notice that with this method is it not necessary to replicate NopCommerce folder structures or paths to the Admin folders or anything like that – it’s very clean.  This sample is kind of ridiculous because it overrides the entire Product view with a simple hello world, but you get the idea.

@{
    Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
Hello world!

The code examples here will allow you to override the Edit view for Products in Nop.Admin, but it lets you do so much more.  You can now override Nop.Admin views and partial views by simply creating a view with the same name and dropping it in the Views folder of your plugin.  This will allow you to override parts of the Nop Admin that have no Route or Controller Action to hook into.

Hope this helps, leave a comment if you have additional insights to this convoluted topic.

No Comments

Comments Closed