what does a specific method do in this open source solution - asp.net-mvc

Recently I'm trying to understand how NopCommerce plugins work.
I'll try to make my question as clear as possible and making sense for you without having the solution opened on your computer.
In nopCommerce plugins everything start with having a class which must have the definition for IWidgetPlugin interface and being derived from BasePlugin.
In one of the predefined plugins there is a class called NivoSliderPlugin and this class overrides some of it's base class methods and has the definition for IWidgetPlugin's methods.
here is the code:
public class NivoSliderPlugin : BasePlugin, IWidgetPlugin
{
// Here there are some fields that are referencing to different interfaces and then
they're injected to the class throw it's constructor
// This method gets a configuration page URL
public override string GetConfigurationPageUrl()
{
return _webHelper.GetStoreLocation() + "Admin/WidgetsNivoSlider/Configure";
}
}
In the above code I only mentioned the part of the code that I have a question about.
_webHelper is a reference to an interface and accept the incoming parameter of type bool.
Regardless the second part of the return, I mean the URL path which is "Admin/WidgetsNivoSlider/Configure", I'm wondering how _webHelper.GetStoreLocation() works ?
As you know _webHelper.GetStoreLocation() has no definition !
I hope what I just asked makes sense.
If it's not clear please let me know to make it clearer.
I'll appreciate it.

Let's start with IWidgetPlugin. This interface is not required to implement for all plugins in NopCommerce. This is required when you are developing a Widget type plugin. Here Nivo Slider is a widget type plugin, that's why it implemented IWidgetPlugin interface. Here is the implementation for Nivo slider.
/// <summary>
/// Gets a value indicating whether to hide this plugin on the widget list page in the admin area
/// </summary>
public bool HideInWidgetList => false;
/// <summary>
/// Gets a name of a view component for displaying widget
/// </summary>
/// <param name="widgetZone">Name of the widget zone</param>
/// <returns>View component name</returns>
public string GetWidgetViewComponentName(string widgetZone)
{
return "WidgetsNivoSlider";
}
/// <summary>
/// Gets widget zones where this widget should be rendered
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the widget zones
/// </returns>
public Task<IList<string>> GetWidgetZonesAsync()
{
return Task.FromResult<IList<string>>(new List<string> { PublicWidgetZones.HomepageTop });
}
If you want to develop a payment plugin, then you have to implement IPaymentMethod (i.e. PayPal, Stripe, Cash on Delivery) and IShippingRateComputationMethod for shipping methods (i.e. UPS, USPS). Also there are many other types of plugins available in nopCommerce. See the list below.
BasePlugin is an abstract class, which is required to be inherited for all plugin class. It has a virtual method named GetConfigurationPageUrl() which returns null by default. But this virtual method can be overridden from each plugin class and can return admin side configuration url for that plugin. Here is the overridden method for Nivo slider plugin.
/// <summary>
/// Gets a configuration page URL
/// </summary>
public override string GetConfigurationPageUrl()
{
return _webHelper.GetStoreLocation() + "Admin/WidgetsNivoSlider/Configure";
}
Here _webHelper.GetStoreLocation() is a method which returns base url of the site. This method is implemented in Nop.Core.WebHelper class. The optional boolean parameter (useSsl) for this method is used whether to consider https:// or not for the site base url. If this method is overridden from a specific plugin, then a Configure button will be displayed in local plugin list page.

Related

How to implement Search function and Add Comments in Swagger

guys:
I use swagger to make api document. I use ASP.NET WebAPI2 to develop WebAPI.
And I met three questions:
First: How could I Add Comments for the WebAPI Controller? I try to add Comment on Controller
namespace IMCAPI.Controllers
{
/// <summary>
/// Value API
/// </summary>
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
/// <summary>
/// Get all Values
/// </summary>
/// <returns></returns>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
, but not work.
Second:How could I search specific WebAPI in Swagger by brower Url? like this Browser Url Search
For example, if I want to search Value API, I input Value in the red section,I want the search result would just show Value API, I don't want other API appears(like Account). Or how to setting SwaggerConfig.cs that can have the search function?
Third:I want to know whether swagger could just read one xml file? I search the Internet, they demo like this
private static string GetXmlCommentsPath()
{
return String.Format("{0}/App_Data/IMCAPI.XML", AppDomain.CurrentDomain.BaseDirectory);
}
If I have multiple XML file, how could I integrate in the swagger?
that it. Hope somebody can help me. Thank you!
Your first question: The comments are appearing, you've put them on the Get method in your code, they're appearing on the Get method in your screenshot. If what you want is a description of the whole service, the Swagger documentation says to use the Description method.
Your second: You can't it shows all the methods on that API.
Third: The Swagger documentation says you call IncludeXmlComments multiple times with the path to each XML.

Is this a bug in the MvcContrib TestHelper OutBoundUrl class?

I am trying to test the outbound URL of an action method in an MVC area using the MvcContrib TestHelper's static OutBoundUrl class. Here is what I have:
OutBoundUrl.Of<ErrorsController>(action => action.NotFound())
.ShouldMapToUrl("/errors/404");
I stepped into the source and found that the UrlHelper was returning null for the outbound URL. Basically it creates a new UrlHelper and delegates the task to UrlHelper.Action(action, controller, routeValues).
When invoked, routeValues does not contain anything (the action method takes no args). Is this a bug? Or do I need to do something extra to make this work with areas? The following line comes just before the above line in the unit test, so my routes are registered (the UrlHelper.RouteCollection contains 100+ routes).
"~/errors/404".WithMethod(HttpVerbs.Get)
.ShouldMapTo<ErrorsController>(action => action.NotFound());
The route is not named, and I don't want to name it, so please don't suggest I use the OfRouteNamed method.
Update
I made an extension method that could work, except for one thing. The OutBoundController Of<TController> method has this code:
var methodCall = ((MethodCallExpression)action.Body);
var methodName = methodCall.Method.Name;
There is similar, but critically different code in the RouteTestingExtensions ShouldMapTo<TController> method:
var methodCall = (MethodCallExpression) action.Body;
// this line is not important
string expectedAction = methodCall.Method.ActionName();
The ActionName() extension method is kind of awesome if you use ActionNameAttributes:
/// <summary>
/// Will return the name of the action specified in the ActionNameAttribute
/// for a method if it has an ActionNameAttribute.
/// Will return the name of the method otherwise.
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
public static string ActionName(this MethodInfo method)
{
if (method.IsDecoratedWith<ActionNameAttribute>())
return method.GetAttribute<ActionNameAttribute>().Name;
return method.Name;
}
Back to the question... is this by design, and if so, why? The fact that OutBoundUrl does not use the .ActionName() extension is the one thing keeping me from writing an extension method to generate outbound url's for area actions decorated with [ActionName].

Bundling a Controller and Views to create a User Control in MVC .NET

I am creating AJAX enabled reusable controls in MVC razor code. It uses a Controller and 1 or more Razor views to work. What is the best practice for isolating those code files from the rest of my project? Logically, it does not make sense to me to have the Controller and the Views mixed in the with main Controller and View files I use for the rest of the project.
And what if i wanted to reuse the control?
I would probably make an extension method off HtmlHelper, so you can use it by calling:
#Html.MyControl("blah", "blah")
from within your view. This is how my MarkdownHelper works (though it's not actually a control, it just formats some text). This is also how the built-in stuff tends to work (g. Html.TextBox, etc.):
/// <summary>
/// Helper class for transforming Markdown.
/// </summary>
public static partial class MarkdownHelper
{
/// <summary>
/// Transforms a string of Markdown into HTML.
/// </summary>
/// <param name="helper">HtmlHelper - Not used, but required to make this an extension method.</param>
/// <param name="text">The Markdown that should be transformed.</param>
/// <returns>The HTML representation of the supplied Markdown.</returns>
public static IHtmlString Markdown(this HtmlHelper helper, string text)
{
// Transform the supplied text (Markdown) into HTML.
var markdownTransformer = new Markdown();
string html = markdownTransformer.Transform(text);
// Wrap the html in an MvcHtmlString otherwise it'll be HtmlEncoded and displayed to the user as HTML :(
return new MvcHtmlString(html);
}
}

How do I pass in the repository to an authorize attribute in ASP.NET MVC

I am castle Windsor and it works great for controller constructors in passing in the repository that is being used.
private IStoryRepository Repository;
public StoryController(IStoryRepository Repository)
{
this.Repository = Repository;
}
Now I have an Action that is in the admin area to display the main admin menu. I have used a custom authorisation attribute which will just check that the logged in user is an admin (just an isAdmin flag in the users table)
[AdminAuthorize]
public ActionResult Menu()
private IStoryRepository Repository;
/// <summary>
/// Initializes a new instance of the <see cref="AdminAuthorizeAttribute"/> class.
/// </summary>
public AdminAuthorizeAttribute(IStoryRepository Repository)
{
this.Repository = Repository;
}
/// <summary>
/// Checks if the user is authorised
/// </summary>
/// <param name="httpContext">The HTTP context.</param>
/// <returns>
/// <c>true</c> if authorized; otherwise, <c>false</c>.
/// </returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return this.Repository.UserIsAdmin(httpContext.User.Identity.Name);
}
How can I get Castle to pass the repository into attribute constructor like it does for a controller constructor?
You basically have two options. Wrap the filter in a proxy, a good example of this can be found here.
Or, within your custom filter you can do an explicit container call. For example using StructureMap (I have no used castle extensively)
ObjectFactory.GetInstance(IStoryRepository)
There may be a third way which is to extend the ActionInvoker to do the injection but I am not sure how this would be done.
The problem is that attributes are constructed by reflection rather than through calls that can be intercepted and replaced with calls that delegate to the container.
There are numerous approaches that can be used to create filters that can support DI, the simplest IMHO is to extend the action invoker and override GetFilters, providing an implementation that uses the attribute to determine the filter type and then resolving that type from the container. An implementation of this approach can be seen in MvcTurbine ( http://mvcturbine.codeplex.com/sourcecontrol/changeset/view/37298?projectName=mvcturbine#758440 ).
Why don't you get the IRepository object from a static factory method inside the Filter constructor? You just use the factory method in a way to allow DI to do its work.
DI will work on your "gateway" method instead of the standard "constructor parameter" approach.
I might be worth looking at FluentMVC project. It allows you to configure attributes at startup and because it uses windsor under the hood should allow for this to be injected pritty easily. For example
FluentMvcConfiguration.Configure = x => {
x.UsingControllerFactory(new WindsorControllerFactory()); x.WithFilter<HandleErrorAttribute>();
x.WithFilter<AuthorizeAttribute>(
Except
.For<AccountController>(ac => ac.LogOn())
.AndFor<AccountController>(ac => ac.LogOn(null, null, false, null))
.AndFor<HomeController>());
};
The code above will add the AuthorizeAttribute to all actions except Login and the home controller
Not sure what the current state of the project is but have used it a few times and works pretty well for me.

ASP.NET MVC Session vs Global vs Cache

I have an application that was written in vanilla ASP.NET that I would like to port over to ASP.NET MVC.
I, however, am confused about the right place to persist objects. I need to persist for a couple reasons:
I would like all to have a single database connection, wrapped in a "repository" or "manager" style object.
Each user has a user object that needs to be saved on a per-session basis.
Normally, I would say that #1 would be saved as a static item in the Globals.asax that can be hit using Global.Repository or similar.
And I would normally say that #2 should be a property with a session backing store somewhere in the base class of the pages.
Now the reason I am confused is that I have heard that sessions have changed in MVC, and the Global.asax no longer holds the same class. Also, the concept of pages has been removed, so adding a property to the base class of a controller seems... wrong.
What say yall?
Your database would go in a base class for your controllers. This base class should extend Controller, and all your controllers should extend the base class. Here's a little example:
public class BaseController : Controller
{
private AuthServices _auth;
private LogHelper _log;
private Repository _repository;
/// <summary>
/// <see cref="AuthServices"/>
/// </summary>
protected AuthServices Authorization
{
get { return _auth ?? (_auth = new AuthServices()); }
}
/// <summary>
/// <see cref="LogHelper"/>
/// </summary>
protected LogHelper Log
{
get { return _log ?? (_log = new LogHelper()); }
}
/// <summary>
/// <see cref="Repository"/>
/// </summary>
protected Repository Repository
{
get { return _repository ?? (_repository = new Repository()); }
}
}
Notice the lazy instantiation. That allows me to sneak in before running tests and set my private fields with mocks.
As for the session, your User object can still be saved in the session just as in a traditional ASP.NET application. Almost everything is still around (Response, Cache, Session, etc), but some of them have been wrapped with classes from System.Web.Abstractions so that they can be mocked for testing. They all still behave the same way, though you shouldn't use some of them in their traditional role (e.g., don't Response.Redirect, return an ActionResult such as RedirectToRouteResult that performs your redirection).
As for the reasoning behind your questions....
Don't stress on a single db connection. Depending on your implementation it may even be a bad idea, as requests may step on each other. Just open your connex, use it, and dispose/close it when done.
Also, one of the biggest changes that MVC brings is the rejection of the stateful model that traditional ASP.NET attempted to bring to web development. All that framework and viewstate doesn't exist anymore (pay no attention to the man behind the curtain). The less state you hold onto the less complex and more robust your web application is. Try it, you might like it.
If you use sessions I would recommend having a session class, so that you only need to specify the string name once in code and this will give you IntelliSence also.
public static class SessionHandler
{
// User Values
private static string _userID = "UserID";
private static string _userRole = "UserRole";
public static string UserID
{
get
{
if (HttpContext.Current.Session[SessionHandler._userID] == null)
{ return string.Empty; }
else
{ return HttpContext.Current.Session[SessionHandler._userID].ToString(); }
}
set
{ HttpContext.Current.Session[SessionHandler._userID] = value; }
}
public static string UserRole
{
get
{
if (HttpContext.Current.Session[SessionHandler._userRole] == null)
{ return string.Empty; }
else
{ return HttpContext.Current.Session[SessionHandler._userRole].ToString(); }
}
set
{ HttpContext.Current.Session[SessionHandler._userRole] = value; }
}
}
Sessions haven't changed at all in MVC. The GlobalApplication class in Global.asax still exists, too. Pages exist, also, that you would want to refer to a repository in a controller rather than a page. Adding a property to a base controller class is fine; I do it all the time.
You can create a model binder to encapsulate state.
(See Steve Sanderson's mvc book on his shopping cart implementation)
With the model binder, you have access to the controllerContext - which has the HttpContext.

Resources