Skip to content

Commit

Permalink
#7244 Add reviews on vendor details page
Browse files Browse the repository at this point in the history
  • Loading branch information
exileDev committed Nov 22, 2024
1 parent fb3d9e7 commit 563e940
Show file tree
Hide file tree
Showing 21 changed files with 428 additions and 11 deletions.
5 changes: 5 additions & 0 deletions src/Libraries/Nop.Core/Domain/Catalog/CatalogSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -589,4 +589,9 @@ public CatalogSettings()
/// Gets or sets a value indicating whether standard search will be used when the search provider throws an exception
/// </summary>
public bool UseStandardSearchWhenSearchProviderThrowsException { get; set; }

/// <summary>
/// Gets or sets a number of reviews per page on vendor reviews page
/// </summary>
public int VendorProductReviewsPageSize { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2873,7 +2873,8 @@ await settingService.SaveSettingAsync(new CatalogSettings
DisplayAllPicturesOnCatalogPages = false,
ProductUrlStructureTypeId = (int)ProductUrlStructureType.Product,
ActiveSearchProviderSystemName = string.Empty,
UseStandardSearchWhenSearchProviderThrowsException = true
UseStandardSearchWhenSearchProviderThrowsException = true,
VendorProductReviewsPageSize = 6
});

await settingService.SaveSettingAsync(new LocalizationSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Nop.Web.Framework.Migrations.UpgradeTo480;

[NopUpdateMigration("2024-05-15 00:00:00", "4.80", UpdateMigrationType.Localization)]
[NopUpdateMigration("2024-08-01 00:00:01", "4.80", UpdateMigrationType.Localization)]
public class LocalizationMigration : MigrationBase
{
/// <summary>Collect the UP migration expressions</summary>
Expand Down Expand Up @@ -63,6 +63,12 @@ public override void Up()
//#7208
["Admin.Customers.Customers.List.SearchIsActive"] = "Is active",
["Admin.Customers.Customers.List.SearchIsActive.Hint"] = "Search customers by an account status.",

//#7244
["Vendors.ExistingReviews"] = "Existing reviews",
["Vendors.Reviews.All"] = "View all",
["Vendors.Reviews.BackTo"] = "Back to {0}",
["PageTitle.VendorReviews"] = "Reviews of the vendor's products",
}, languageId);

#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using FluentMigrator;
using Nop.Core.Domain.Catalog;
using Nop.Core.Infrastructure;
using Nop.Data;
using Nop.Data.Migrations;
using Nop.Services.Configuration;

namespace Nop.Web.Framework.Migrations.UpgradeTo480;

[NopUpdateMigration("2024-05-15 00:00:00", "4.80.0", UpdateMigrationType.Settings)]
[NopUpdateMigration("2024-08-01 00:00:00", "4.80.0", UpdateMigrationType.Settings)]
public class SettingMigration : MigrationBase
{
/// <summary>Collect the UP migration expressions</summary>
Expand All @@ -22,6 +23,15 @@ public override void Up()
var displayAttributeCombinationImagesOnly = settingService.GetSetting("producteditorsettings.displayattributecombinationimagesonly");
if (displayAttributeCombinationImagesOnly is not null)
settingService.DeleteSetting(displayAttributeCombinationImagesOnly);

//#7244
var catalogSettings = settingService.LoadSetting<CatalogSettings>();
if (!settingService.SettingExists(catalogSettings, settings => settings.VendorProductReviewsPageSize))
{
catalogSettings.VendorProductReviewsPageSize = 6;
settingService.SaveSetting(catalogSettings, settings => settings.VendorProductReviewsPageSize);
}

}

public override void Down()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18543,6 +18543,9 @@
<LocaleResource Name="PageTitle.Vendors.Apply">
<Value>Apply for vendor account</Value>
</LocaleResource>
<LocaleResource Name="PageTitle.VendorReviews">
<Value>Reviews of the vendor's products</Value>
</LocaleResource>
<LocaleResource Name="PageTitle.ViewPM">
<Value>View the private message</Value>
</LocaleResource>
Expand Down Expand Up @@ -20082,9 +20085,18 @@
<LocaleResource Name="Vendors.ApplyAccount.Submitted">
<Value>Your request has been submitted successfully. We'll contact you soon.</Value>
</LocaleResource>
<LocaleResource Name="Vendors.ExistingReviews">
<Value>Existing reviews</Value>
</LocaleResource>
<LocaleResource Name="Vendors.List">
<Value>Vendor List</Value>
</LocaleResource>
<LocaleResource Name="Vendors.Reviews.All">
<Value>View all</Value>
</LocaleResource>
<LocaleResource Name="Vendors.Reviews.BackTo">
<Value>Back to {0}</Value>
</LocaleResource>
<LocaleResource Name="Vendors.ViewAll">
<Value>View all</Value>
</LocaleResource>
Expand Down
12 changes: 12 additions & 0 deletions src/Presentation/Nop.Web/Controllers/CatalogController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,18 @@ public virtual async Task<IActionResult> GetVendorProducts(int vendorId, Catalog
return PartialView("_ProductsInGridOrLines", model);
}

public virtual async Task<IActionResult> VendorReviews(int vendorId, VendorReviewsPagingFilteringModel pagingModel)
{
var vendor = await _vendorService.GetVendorByIdAsync(vendorId);

if (!await CheckVendorAvailabilityAsync(vendor))
return NotFound();

var model = await _catalogModelFactory.PrepareVendorProductReviewsModelAsync(vendor, pagingModel);

return View(model);
}

public virtual async Task<IActionResult> VendorAll()
{
//we don't allow viewing of vendors if "vendors" block is hidden
Expand Down
101 changes: 96 additions & 5 deletions src/Presentation/Nop.Web/Factories/CatalogModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Nop.Core.Domain.Blogs;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Forums;
using Nop.Core.Domain.Media;
using Nop.Core.Domain.Seo;
Expand Down Expand Up @@ -37,13 +38,15 @@ public partial class CatalogModelFactory : ICatalogModelFactory

protected readonly BlogSettings _blogSettings;
protected readonly CatalogSettings _catalogSettings;
protected readonly CustomerSettings _customerSettings;
protected readonly DisplayDefaultMenuItemSettings _displayDefaultMenuItemSettings;
protected readonly ForumSettings _forumSettings;
protected readonly ICategoryService _categoryService;
protected readonly ICategoryTemplateService _categoryTemplateService;
protected readonly ICurrencyService _currencyService;
protected readonly ICustomerService _customerService;
protected readonly IEventPublisher _eventPublisher;
protected readonly IGenericAttributeService _genericAttributeService;
protected readonly IHttpContextAccessor _httpContextAccessor;
protected readonly IJsonLdModelFactory _jsonLdModelFactory;
protected readonly ILocalizationService _localizationService;
Expand Down Expand Up @@ -74,13 +77,15 @@ public partial class CatalogModelFactory : ICatalogModelFactory

public CatalogModelFactory(BlogSettings blogSettings,
CatalogSettings catalogSettings,
CustomerSettings customerSettings,
DisplayDefaultMenuItemSettings displayDefaultMenuItemSettings,
ForumSettings forumSettings,
ICategoryService categoryService,
ICategoryTemplateService categoryTemplateService,
ICurrencyService currencyService,
ICustomerService customerService,
IEventPublisher eventPublisher,
IGenericAttributeService genericAttributeService,
IHttpContextAccessor httpContextAccessor,
IJsonLdModelFactory jsonLdModelFactory,
ILocalizationService localizationService,
Expand All @@ -106,13 +111,15 @@ public CatalogModelFactory(BlogSettings blogSettings,
{
_blogSettings = blogSettings;
_catalogSettings = catalogSettings;
_customerSettings = customerSettings;
_displayDefaultMenuItemSettings = displayDefaultMenuItemSettings;
_forumSettings = forumSettings;
_categoryService = categoryService;
_categoryTemplateService = categoryTemplateService;
_currencyService = currencyService;
_customerService = customerService;
_eventPublisher = eventPublisher;
_genericAttributeService = genericAttributeService;
_httpContextAccessor = httpContextAccessor;
_jsonLdModelFactory = jsonLdModelFactory;
_localizationService = localizationService;
Expand Down Expand Up @@ -648,7 +655,7 @@ public virtual async Task<List<CategorySimpleModel>> PrepareRootCategoriesAsync(
var doc = await PrepareCategoryXmlDocumentAsync();

var models = from xe in doc.Root.XPathSelectElements("CategorySimpleModel")
select GetCategorySimpleModel(xe);
select GetCategorySimpleModel(xe);

return models.ToList();
}
Expand All @@ -666,11 +673,11 @@ public virtual async Task<List<CategorySimpleModel>> PrepareSubCategoriesAsync(i
var doc = await PrepareCategoryXmlDocumentAsync();

var model = from xe in doc.Descendants("CategorySimpleModel")
where xe.XPathSelectElement("Id").Value == id.ToString()
select xe;
where xe.XPathSelectElement("Id").Value == id.ToString()
select xe;

var models = from xe in model.First().XPathSelectElements("SubCategories/CategorySimpleModel")
select GetCategorySimpleModel(xe);
select GetCategorySimpleModel(xe);

return models.ToList();
}
Expand Down Expand Up @@ -1195,7 +1202,8 @@ public virtual async Task<VendorModel> PrepareVendorModelAsync(Vendor vendor, Ca
MetaTitle = await _localizationService.GetLocalizedAsync(vendor, x => x.MetaTitle),
SeName = await _urlRecordService.GetSeNameAsync(vendor),
AllowCustomersToContactVendors = _vendorSettings.AllowCustomersToContactVendors,
CatalogProductsModel = await PrepareVendorProductsModelAsync(vendor, command)
CatalogProductsModel = await PrepareVendorProductsModelAsync(vendor, command),
ProductReviews = await PrepareVendorProductReviewsModelAsync(vendor, new VendorReviewsPagingFilteringModel())
};

return model;
Expand Down Expand Up @@ -1373,6 +1381,89 @@ public virtual async Task<VendorNavigationModel> PrepareVendorNavigationModelAsy
return cachedModel;
}


/// <summary>
/// Prepare review models for vendor products
/// </summary>
/// <returns>
/// <param name="vendor">Vendor</param>
/// <param name="pagingModel">Model to filter product reviews</param>
/// A task that represents the asynchronous operation
/// The task result contains a list of product reviews
/// </returns>
public virtual async Task<VendorProductReviewsListModel> PrepareVendorProductReviewsModelAsync(Vendor vendor, VendorReviewsPagingFilteringModel pagingModel)
{
ArgumentNullException.ThrowIfNull(vendor);
ArgumentNullException.ThrowIfNull(pagingModel);

if (pagingModel.PageSize <= 0)
pagingModel.PageSize = _catalogSettings.VendorProductReviewsPageSize;
if (pagingModel.PageNumber <= 0)
pagingModel.PageNumber = 1;

var model = new VendorProductReviewsListModel {
VendorId = vendor.Id,
VendorName = await _localizationService.GetLocalizedAsync(vendor, x => x.Name),
VendorUrl = await _nopUrlHelper.RouteGenericUrlAsync<Vendor>(new { SeName = await _urlRecordService.GetSeNameAsync(vendor) })
};

var currentStore = await _storeContext.GetCurrentStoreAsync();
var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopModelCacheDefaults.VendorReviewsModelKey, vendor, currentStore);
var vendorReviewModels = await _staticCacheManager.GetAsync(cacheKey, async () =>
{
var vendorReviews = await _productService.GetAllProductReviewsAsync(
vendorId: vendor.Id,
approved: true,
storeId: currentStore.Id);

return await vendorReviews.SelectAwait(async pr =>
{
var customer = await _customerService.GetCustomerByIdAsync(pr.CustomerId);
var product = await _productService.GetProductByIdAsync(pr.ProductId);

var model = new VendorProductReviewModel
{
ProductName = await _localizationService.GetLocalizedAsync(product, x => x.Name),
ProductSeName = await _urlRecordService.GetSeNameAsync(product),
CustomerId = pr.CustomerId,
CustomerName = await _customerService.FormatUsernameAsync(customer),
AllowViewingProfiles = _customerSettings.AllowViewingProfiles && customer != null && !await _customerService.IsGuestAsync(customer),
Title = pr.Title,
ReviewText = pr.ReviewText,
ReplyText = pr.ReplyText,
Rating = pr.Rating,
Helpfulness = new ProductReviewHelpfulnessModel
{
ProductReviewId = pr.Id,
HelpfulYesTotal = pr.HelpfulYesTotal,
HelpfulNoTotal = pr.HelpfulNoTotal,
},
CreatedOnUtc = pr.CreatedOnUtc,
};

if (_customerSettings.AllowCustomersToUploadAvatars)
{
model.CustomerAvatarUrl = await _pictureService.GetPictureUrlAsync(
await _genericAttributeService.GetAttributeAsync<int>(customer, NopCustomerDefaults.AvatarPictureIdAttribute),
_mediaSettings.AvatarPictureSize, _customerSettings.DefaultAvatarEnabled, defaultPictureType: PictureType.Avatar);
}

return model;
})
.OrderBy(m => m.CreatedOnUtc)
.ToListAsync();
});

var pagedVendorReviews = new PagedList<VendorProductReviewModel>(vendorReviewModels, pagingModel.PageNumber - 1, pagingModel.PageSize);

//re-init pager
model.PagingFilteringContext.LoadPagedList(pagedVendorReviews);

model.Reviews = pagedVendorReviews;

return model;
}

#endregion

#region Product tags
Expand Down
11 changes: 11 additions & 0 deletions src/Presentation/Nop.Web/Factories/ICatalogModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ Task<CategoryNavigationModel> PrepareCategoryNavigationModelAsync(int currentCat
/// </returns>
Task<VendorNavigationModel> PrepareVendorNavigationModelAsync();

/// <summary>
/// Prepare review models for vendor products
/// </summary>
/// <returns>
/// <param name="vendor">Vendor</param>
/// <param name="pagingModel">Model to filter product reviews</param>
/// A task that represents the asynchronous operation
/// The task result contains a list of product reviews
/// </returns>
Task<VendorProductReviewsListModel> PrepareVendorProductReviewsModelAsync(Vendor vendor, VendorReviewsPagingFilteringModel pagingModel);

#endregion

#region Product tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,14 @@ public async Task HandleEventAsync(EntityUpdatedEvent<Vendor> eventMessage)
{
await _staticCacheManager.RemoveAsync(NopModelCacheDefaults.VendorNavigationModelKey);
await _staticCacheManager.RemoveByPrefixAsync(string.Format(NopModelCacheDefaults.VendorPicturePrefixCacheKeyById, eventMessage.Entity.Id));
await _staticCacheManager.RemoveByPrefixAsync(string.Format(NopModelCacheDefaults.VendorReviewsByVendorIdPrefixCacheKey, eventMessage.Entity.Id));
}

/// <returns>A task that represents the asynchronous operation</returns>
public async Task HandleEventAsync(EntityDeletedEvent<Vendor> eventMessage)
{
await _staticCacheManager.RemoveAsync(NopModelCacheDefaults.VendorNavigationModelKey);
await _staticCacheManager.RemoveByPrefixAsync(string.Format(NopModelCacheDefaults.VendorReviewsByVendorIdPrefixCacheKey, eventMessage.Entity.Id));
}

#endregion
Expand Down Expand Up @@ -282,6 +284,7 @@ public async Task HandleEventAsync(EntityUpdatedEvent<Product> eventMessage)
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.ProductsAlsoPurchasedIdsPrefixCacheKey);
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.SitemapPrefixCacheKey);
await _staticCacheManager.RemoveByPrefixAsync(string.Format(NopModelCacheDefaults.ProductReviewsPrefixCacheKeyById, eventMessage.Entity.Id));
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.VendorReviewsPrefixCacheKey);
}

/// <returns>A task that represents the asynchronous operation</returns>
Expand All @@ -290,6 +293,7 @@ public async Task HandleEventAsync(EntityDeletedEvent<Product> eventMessage)
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.HomepageBestsellersIdsPrefixCacheKey);
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.ProductsAlsoPurchasedIdsPrefixCacheKey);
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.SitemapPrefixCacheKey);
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.VendorReviewsPrefixCacheKey);
}

#endregion
Expand Down Expand Up @@ -534,6 +538,7 @@ public async Task HandleEventAsync(EntityUpdatedEvent<ShoppingCartItem> eventMes
public async Task HandleEventAsync(EntityDeletedEvent<ProductReview> eventMessage)
{
await _staticCacheManager.RemoveByPrefixAsync(string.Format(NopModelCacheDefaults.ProductReviewsPrefixCacheKeyById, eventMessage.Entity.ProductId));
await _staticCacheManager.RemoveByPrefixAsync(NopModelCacheDefaults.VendorReviewsPrefixCacheKey);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ public static partial class NopModelCacheDefaults
public static string VendorPicturePrefixCacheKey => "Nop.pres.vendor.picture";
public static string VendorPicturePrefixCacheKeyById => "Nop.pres.vendor.picture-{0}-";

/// <summary>
/// Key for vendor reviews caching
/// </summary>
/// <remarks>
/// {0} : vendor id
/// {1} : current store ID
/// </remarks>
public static CacheKey VendorReviewsModelKey => new("Nop.pres.vendor.reviews-{0}-{1}", VendorReviewsPrefixCacheKey, VendorReviewsByVendorIdPrefixCacheKey);
public static string VendorReviewsPrefixCacheKey => "Nop.pres.vendor.reviews";
public static string VendorReviewsByVendorIdPrefixCacheKey => "Nop.pres.vendor.reviews-{0}-";

/// <summary>
/// Key for cart picture caching
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Presentation/Nop.Web/Infrastructure/RouteProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
pattern: $"{lang}/vendor/all/",
defaults: new { controller = "Catalog", action = "VendorAll" });

endpointRouteBuilder.MapControllerRoute(name: "VendorReviews",
pattern: $"{lang}/vendor/{{vendorId:min(0)}}/reviews",
defaults: new { controller = "Catalog", action = "VendorReviews" });

//add product to cart (without any attributes and options). used on catalog pages. (AJAX)
endpointRouteBuilder.MapControllerRoute(name: "AddProductToCart-Catalog",
pattern: $"addproducttocart/catalog/{{productId:min(0)}}/{{shoppingCartTypeId:min(0)}}/{{quantity:min(0)}}",
Expand Down
Loading

0 comments on commit 563e940

Please sign in to comment.