how should i call prepareProductdetailpage method from catalogController to my pluging's controller - nopcommerce

I want to increment counter of product when product visited by client. I was done this without plugin
First i was made insert method in productservice :
public virtual void InsertMostViewProduct(int productid)
{
var query = from u in _mostviewRepository.Table
where u.ProductId == productid
select u;
if (query.Count()==0)
{
//insert
var Insertproduct = new MostViewProduct();
Insertproduct.ProductId = productid;
Insertproduct.ProductCount = 1;
_mostviewRepository.Insert(Insertproduct);
//event notification
_eventPublisher.EntityInserted(Insertproduct);
}
else
{
//Update
var product = query.FirstOrDefault();
product.ProductCount = product.ProductCount + 1;
_mostviewRepository.Update(product);
// _eventPublisher.EntityUpdated(Updateproduct);
}
}
And called this method in PrepareProductDetailsPageModel :
#region MostViewProduct
_productService.InsertMostViewProduct(product.Id);
#endregion
How should i call prepareProductdetailpage method from catalogController to my pluging's controller?

If you look at the code the PrepareProductDetailsPageModel is not a good place for your code as it is also called for the associated products list. Furthermore, I suggest you not to modify nopCommerce code when possible.
My suggestion is do it in your plugin. Override the implementation of the IRecentlyViewedProductsService within your plugin, this service is called every time an user visits a product page.
Add a class to your plugin extending RecentlyViewedProductsService. Override the AddProductToRecentlyViewedList method adding your own code. Be sure to call the implementation from the base class to keep old functionality.
Register the new service in the Dependency registrar of your plugin (more).
If you need more flexibility consider creating a custom event and handler (more), in this case you will need to modify the original code to properly wire it up.

Related

ASP.NET MVC How does AuthorizeAttribute support checking Roles?

In my controllers, I have code like [Authorize(Roles = "Administrators")] annotated above some actions, and I want to know how AuthorizeAttribute uses the Roles parameter (the implementation of the checking mechanism). My goal is to create an extension of this class, called PrivilegeAttribute for example, so that I can annotate actions like [Privilege(Privileges = "read")]. In this class, I would check if the Role of the user has at least one of the privileges in this custom filter (read in this example). I have already created the association between roles and privileges in the code and in the database, and what I want help with is checking whether the role is associated to the privilege.
I tried seeing if that information is there in HttpContextBase.User.Identity but I couldn't find it.
Thank you.
If you don't need your own custom attribute and could live with using someone else attribute, than I would suggest to use the package Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc as described here
Blog Post by Dominick Baier
and here
Git Hub Sample Code for the Package
so it basically works like this:
you put an attribute over your action like this:
[ResourceAuthorize("View", "Customer")]
The first argument is the name of the Action to check, the second one is the name of the attribute.
Then you derive from ResourceAuthorizationManager in your code and override the CheckAccessAssync Method
public class MyAuthorization : ResourceAuthorizationManager
{
public override Task<bool> CheckAccessAsync(ResourceAuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
// getting the roles that are connected to that resource and action
// from the db. Context could of course be injected into the
// constructor of the class. In my code I assume that the table
// thank links roles, resources and actions is called Roles ToActions
using(var db = MyContext())
var roles = db.RolesToActions // Use your table name here
.Where(r => r.Resource == resource && r.Action == action).ToList();
foreach(var role in roles)
{
if(context.Principal.IsInRole(role.Name)
{
return Ok();
}
}
return Nok();
}
}
}
So I hope this helps. If you prefer to implement your own attribute however, than the source code from the ResourceAuthorization GitHub Repository should be a good starting point

Action requires multiple controllers to execute

I have a UserController that has a Destroy function. It is a rather complex function because it demands to destroy all user's data. I have another action, from the Admin panel that deletes all data from a specific set of users.
Since I don't want to replicate the code from the UserController, I would like to call the Destroy function from UserController for each User to destroy its data.
How should I proceed?
Thanks in advance.
Why not move this functionality to a common class method which can be accessed from both the controllers as needed ?
public class UserManager
{
public void Destroy(List<int> userIdsToDestroy)
{
foreach(var userId in userIdsToDestroy)
{
//Execute code to destroy
}
}
}
and from your action methods, you can call it like
var mgr = new UserManager();
var badUsers = new List<int> { 1,2,3};
mgr.Destroy(badUsers);
Update the badUsers variable value as needed based on from where you are calling it.
Shared functionality like this would ideally be in a business layer, and both controllers would call that code. If it's a little app, you could just create a separate folder structure for shared code. Larger projects would have a business layer dll.
Why not make the Destroy() method as a Non-Action method then like
[Non-Action]
public void Destroy(User user)
{
// code goes here
}
You can as well make this Destroy() function as part of your business layer logic instead of handling this in controller. In that case, you call it from anywhere.
If you want it to be #controller, you can as well consider usig [ChildActionOnly] action filter attribute.

Passing parameters to parameter dialog for Query-based reports

I have a query-based report in which the query has some interactive ranges enabled on them. This is great except the value is blank, or has the last values used pre-populated. One of these is Vendor account number. If I wanted to have this report to pre-populate the Vend account based on whichever Vendor account record is selected (the caller), how would I be able to achieve this?
The answer was easy, although hard to find. I wasn't aware that you could access query objects from within a controller. The solution is to create a Controller class with only a main() method defined as normal, and the prePromptModifyContract method overridden. The following code will solve the problem:
SomeTable someTable;
Query query;
super();
if (this.parmArgs() && this.parmArgs().dataset() == tableNum(SomeTable))
{
someTable = this.parmArgs().record();
query = this.getFirstQuery();
SysQuery::findOrCreateRange(query.dataSourceTable(tableNum(SomeOtherTable)), fieldNum(SomeOtherTable, SomeOtherField)).value(SysQuery::value(someTable.SomeField));
}
I haven't tried this, but you could override the query's init method and call to element.args().record() as in a Form.
Something like this:
public void init()
{
VendTable vendTable;
super();
if (element.args().dataset() == tableNum(VendTable))
{
vendTable = element.args().record();
//populate your ranges with vendTable
}
}
I hope it works!

Orchard: Custom Registration fields

For my Orchard project, I need some additional information from the user at registration time. (Say, First Name, Last Name, Pants Color). This information must be entered while registering and can not be deferred until later (as per client's orders).
I tried using the Profile and Extended Registration plugins to ask for those, but as far as I see, this only gives me optional fields to display in the registration form. Is there a way to present fields that are mandatory?
I also had a quick foray into overwriting the AccountController's Register method, as per this discussion, but I couldn't get it to work: The controller is in a different place, it can't be subclassed and even if I force it to, code is never executed. I presume they are using a much older version of Orchard.
So, in which direction should I go to create a mandatory field that is close to the Orchard philosophy? Should I create a new field type that rejects empty values maybe? (is that even possible)?
I wrote the ExtendedRegistration module because of that same need.
You need to create a custom part, e.g.: MyRegistrationPart.
Then you add that part to the User ContentType.
In your part just add the [Required] attribute (Data annotations) to any properties that are mandatory.
Registration will not succeed until those mandatory values have been filled out!
Hope it's clear now.
While this probably won't answer your question just wanted to point out that it is my understanding that you don't need to override/subclass the AccountController class. Instead you need to "overwrite" the Users/Account/Register route by adding your own with a higher priority. To do that you need to implement an IRouteProvider as part of our module. Since it's an IDependency it will be loaded and processed automagically at run time. Something like:
public class Routes : IRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
routes.AddRange(GetRoutes());
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new RouteDescriptor {
// Make sure to be higher than the default
Priority = ##### PRIORITY HERE (int) ######,
Route = new Route(
"Users/Account/Register",
new RouteValueDictionary {
{"area", "#### YOUR MODULE AREA HERE ####"},
{"controller", "#### YOUR ACCOUNT CONTROLLER HERE ####"},
{"action", "#### YOUR REGISTER ACTION HERE ####"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "#### YOUR MODULE AREA HERE ####"}
},
new MvcRouteHandler())
}
};
}
}

Dynamically bind a DataRepeater (Microsoft.VisualBasic.PowerPacks)

I am using a DataRepeater to show data from a business objects on the screen. I am using windows forms in C# to accomplish this. The datasource is not available at compile time so I want to bind the datasource at runtime.
Here is the simplified scenario. I'm using this business class:
public class Product
{
private double _price;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
}
}
}
I have created a ProductDataSource with the VisualStudio interface and bound the price to a label. Now I filled the datasource of my repeater in code:
dataRepeater1.DataSource = _productDataAgent.GetProducts();
When I startup my application the prices are correctly filled in the labels. So far so good.
Now I want the price labels to be updated when the product is updated. The Visual Studio interface helps me, and let me choose a 'Data Source Update Mode'. So I choose "OnPropertyChanged".
Here comes the tricky part. How does the .NET runtime know that the price property is updated from the backend. So I modify my business class to implement INotifyPropertyChanged. Like this:
public class Product : INotifyPropertyChanged
{
private double _price;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The problem is this doesn't work. When I update a product it remeanes un-updated in the interface. When I debug and change the property, I see that the PropertyChanged event is null so no one is listening.
Delving a little deeper in to the problem I found the following on the System.Windows.Forms.Binding Constructor page on MSDN:
An event named PropertyNameChanged.
So I tried using a (custom) PriceChanged event, but that did not work.
Am I doing something wrong here? I am comming from using WPF, so maybe this works a little different in Windows Forms? Is this because I am binding at runtime?
Jep found the sollution. Apparently you cannot simply bind to a list of products. You will see the products initially, but they will not be updated when a property is changed. Instead you need to statically bind to a BindingSource. Just create an object datasource using the Visual Studio (in the data menu). Code like this is generated:
private System.Windows.Forms.BindingSource beursProductDisplayBindingSource;
this.beursProductDisplayBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.dataRepeater1.DataSource = this.beursProductDisplayBindingSource;
Now you can dynamically bind like this:
BindingSource productBinding = ((BindingSource)dataRepeater1.DataSource);
_productDataAgent.BeursProducts.ForEach(product => productBinding.Add(product));
Now when implementing INotifyPropertyChanged in your data object like I did is works like expected. Just forgot one step which is not needed when using WPF.

Resources