diff --git a/src/.idea/.idea.NopCommerce/.idea/.gitignore b/src/.idea/.idea.NopCommerce/.idea/.gitignore
new file mode 100644
index 00000000000..4eba5f6ca8c
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/contentModel.xml
+/modules.xml
+/.idea.NopCommerce.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/src/.idea/.idea.NopCommerce/.idea/.name b/src/.idea/.idea.NopCommerce/.idea/.name
new file mode 100644
index 00000000000..be061eeea83
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/.name
@@ -0,0 +1 @@
+NopCommerce
\ No newline at end of file
diff --git a/src/.idea/.idea.NopCommerce/.idea/encodings.xml b/src/.idea/.idea.NopCommerce/.idea/encodings.xml
new file mode 100644
index 00000000000..df87cf951fb
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.NopCommerce/.idea/indexLayout.xml b/src/.idea/.idea.NopCommerce/.idea/indexLayout.xml
new file mode 100644
index 00000000000..7b08163cebc
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.NopCommerce/.idea/material_theme_project_new.xml b/src/.idea/.idea.NopCommerce/.idea/material_theme_project_new.xml
new file mode 100644
index 00000000000..09b9ee06266
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/material_theme_project_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.NopCommerce/.idea/vcs.xml b/src/.idea/.idea.NopCommerce/.idea/vcs.xml
new file mode 100644
index 00000000000..6c0b8635858
--- /dev/null
+++ b/src/.idea/.idea.NopCommerce/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Libraries/Nop.Core/Http/NopRouteNames.cs b/src/Libraries/Nop.Core/Http/NopRouteNames.cs
index 0f5cd7896ba..0cb82d95a3a 100644
--- a/src/Libraries/Nop.Core/Http/NopRouteNames.cs
+++ b/src/Libraries/Nop.Core/Http/NopRouteNames.cs
@@ -48,7 +48,7 @@ public static partial class General
///
/// Gets the product search route name
///
- public const string SEARCH = "ProductSearch";
+ public const string SEARCH = "ProductSearch";
///
/// Gets the compare products route name
@@ -703,6 +703,11 @@ public static partial class Ajax
///
public const string DELETE_CUSTOM_WISHLIST = "DeleteCustomWishlist";
+ ///
+ /// Gets the edit custom wishlist route name
+ ///
+ public const string RENAME_CUSTOM_WISHLIST = "RenameCustomWishlist";
+
///
/// Gets the estimate shipping route name
///
diff --git a/src/Libraries/Nop.Services/Orders/CustomWishlistService.cs b/src/Libraries/Nop.Services/Orders/CustomWishlistService.cs
index 7d5191dd883..2db92b7c7c6 100644
--- a/src/Libraries/Nop.Services/Orders/CustomWishlistService.cs
+++ b/src/Libraries/Nop.Services/Orders/CustomWishlistService.cs
@@ -17,7 +17,7 @@ public partial class CustomWishlistService : ICustomWishlistService
#region Ctor
- public CustomWishlistService(IRepository customWishlistRepository,
+ public CustomWishlistService(IRepository customWishlistRepository,
ShoppingCartSettings shoppingCartSettings)
{
_customWishlistRepository = customWishlistRepository;
@@ -56,6 +56,26 @@ public virtual async Task AddCustomWishlistAsync(CustomWishlist item)
await _customWishlistRepository.InsertAsync(item);
}
+ public virtual async Task EditCustomWishlistAsync(int wishlistId, string newName, int customerId)
+ {
+ var wishlist = await _customWishlistRepository.GetByIdAsync(wishlistId);
+ if (wishlist == null)
+ throw new ArgumentException($"Custom wishlist with ID {wishlistId} not found.");
+
+ // Verify ownership
+ if (wishlist.CustomerId != customerId)
+ throw new UnauthorizedAccessException("You are not allowed to edit this wishlist.");
+
+ // Validate new name
+ if (string.IsNullOrWhiteSpace(newName))
+ throw new ArgumentException("Wishlist name cannot be empty.");
+
+ wishlist.Name = newName.Trim();
+
+ await _customWishlistRepository.UpdateAsync(wishlist);
+ }
+
+
///
/// Removes a custom wishlist item with the specified identifier.
///
@@ -69,6 +89,7 @@ public virtual async Task RemoveCustomWishlistAsync(int itemId)
}
}
+
///
/// Retrieves a custom wishlist by its unique identifier.
///
@@ -81,4 +102,4 @@ public virtual async Task GetCustomWishlistByIdAsync(int itemId)
}
#endregion
-}
+}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Services/Orders/ICustomWishlistService.cs b/src/Libraries/Nop.Services/Orders/ICustomWishlistService.cs
index a597e1b2e36..e20d775fe9c 100644
--- a/src/Libraries/Nop.Services/Orders/ICustomWishlistService.cs
+++ b/src/Libraries/Nop.Services/Orders/ICustomWishlistService.cs
@@ -27,6 +27,7 @@ public partial interface ICustomWishlistService
///
/// The unique identifier of the custom wishlist item to remove. Must be a valid identifier of an existing item.
Task RemoveCustomWishlistAsync(int itemId);
+ Task EditCustomWishlistAsync(int wishlistId, string newName, int customer);
///
/// Retrieves a custom wishlist by its unique identifier.
@@ -35,4 +36,4 @@ public partial interface ICustomWishlistService
/// A object representing the custom wishlist with the specified identifier. Returns
/// null if no wishlist is found with the given identifier.
Task GetCustomWishlistByIdAsync(int itemId);
-}
+}
\ No newline at end of file
diff --git a/src/Presentation/Nop.Web.Framework/Mvc/Routing/SlugRouteTransformer.cs b/src/Presentation/Nop.Web.Framework/Mvc/Routing/SlugRouteTransformer.cs
index ce36ca6515f..e511b91289c 100644
--- a/src/Presentation/Nop.Web.Framework/Mvc/Routing/SlugRouteTransformer.cs
+++ b/src/Presentation/Nop.Web.Framework/Mvc/Routing/SlugRouteTransformer.cs
@@ -92,14 +92,12 @@ protected virtual async Task SingleSlugRoutingAsync(HttpContext httpContext, Rou
var store = await _storeContext.GetCurrentStoreAsync();
var languages = await _languageService.GetAllLanguagesAsync(storeId: store.Id);
var language = languages
- .FirstOrDefault(lang => lang.Published && lang.UniqueSeoCode.Equals(langValue?.ToString(), StringComparison.InvariantCultureIgnoreCase))
- ?? languages.FirstOrDefault();
+ .FirstOrDefault(lang => lang.UniqueSeoCode.Equals(langValue?.ToString(), StringComparison.InvariantCultureIgnoreCase))
+ ?? languages.FirstOrDefault();
- var slugLocalized = await _urlRecordService.GetActiveSlugAsync(urlRecord.EntityId, urlRecord.EntityName, language.Id);
+ var slugLocalized = await _urlRecordService.GetSeNameAsync(urlRecord.EntityId, urlRecord.EntityName, language.Id, true, false);
if (!string.IsNullOrEmpty(slugLocalized) && !slugLocalized.Equals(slug, StringComparison.InvariantCultureIgnoreCase))
{
- //we should make validation above because some entities does not have SeName for standard (Id = 0) language (e.g. news, blog posts)
-
//redirect to the page for current language
InternalRedirect(httpContext, values, $"/{language.UniqueSeoCode}/{slugLocalized}", false);
return;
@@ -208,11 +206,11 @@ protected virtual async Task TryProductCatalogRoutingAsync(HttpContext htt
var store = await _storeContext.GetCurrentStoreAsync();
var languages = await _languageService.GetAllLanguagesAsync(storeId: store.Id);
var language = languages
- .FirstOrDefault(lang => lang.Published && lang.UniqueSeoCode.Equals(langValue?.ToString(), StringComparison.InvariantCultureIgnoreCase))
- ?? languages.FirstOrDefault();
+ .FirstOrDefault(lang => lang.UniqueSeoCode.Equals(langValue?.ToString(), StringComparison.InvariantCultureIgnoreCase))
+ ?? languages.FirstOrDefault();
- var slugLocalized = await _urlRecordService.GetActiveSlugAsync(urlRecord.EntityId, urlRecord.EntityName, language.Id);
- var catalogSlugLocalized = await _urlRecordService.GetActiveSlugAsync(catalogUrlRecord.EntityId, catalogUrlRecord.EntityName, language.Id);
+ var slugLocalized = await _urlRecordService.GetSeNameAsync(urlRecord.EntityId, urlRecord.EntityName, language.Id, true, false);
+ var catalogSlugLocalized = await _urlRecordService.GetSeNameAsync(catalogUrlRecord.EntityId, catalogUrlRecord.EntityName, language.Id, true, false);
if ((!string.IsNullOrEmpty(slugLocalized) && !slugLocalized.Equals(slug, StringComparison.InvariantCultureIgnoreCase)) ||
(!string.IsNullOrEmpty(catalogSlugLocalized) && !catalogSlugLocalized.Equals(catalogUrlRecord.Slug, StringComparison.InvariantCultureIgnoreCase)))
{
diff --git a/src/Presentation/Nop.Web/Controllers/ShoppingCartController.cs b/src/Presentation/Nop.Web/Controllers/ShoppingCartController.cs
index bdd1bab2ba4..bbc652d44f2 100644
--- a/src/Presentation/Nop.Web/Controllers/ShoppingCartController.cs
+++ b/src/Presentation/Nop.Web/Controllers/ShoppingCartController.cs
@@ -1888,6 +1888,51 @@ public virtual async Task MoveToCustomWishlist(int shoppingCartIt
});
}
+ [HttpPost]
+ public virtual async Task RenameWishlist(int wishlistId, string name)
+ {
+ var customer = await _workContext.GetCurrentCustomerAsync();
+ var isGuest = await _customerService.IsGuestAsync(customer);
+
+ if (isGuest)
+ {
+ return Json(new
+ {
+ success = false,
+ message = await _localizationService.GetResourceAsync("Wishlist.MultipleWishlistNotForGuest")
+ });
+ }
+
+ if (!_shoppingCartSettings.AllowMultipleWishlist)
+ {
+ return Json(new
+ {
+ success = false,
+ message = await _localizationService.GetResourceAsync("Wishlist.NotAllowMultipleWishlist")
+ });
+ }
+
+ try
+ {
+ await _customWishlistService.EditCustomWishlistAsync(wishlistId, name, customer.Id);
+
+ return Json(new
+ {
+ success = true,
+ message = await _localizationService.GetResourceAsync("Wishlist.Rename.Success")
+ });
+ }
+ catch (Exception ex)
+ {
+ return Json(new
+ {
+ success = false,
+ message = ex.Message
+ });
+ }
+ }
+
+
[HttpPost]
public virtual async Task DeleteWishlist(int wishlistId)
{
diff --git a/src/Presentation/Nop.Web/Infrastructure/RouteProvider.cs b/src/Presentation/Nop.Web/Infrastructure/RouteProvider.cs
index 8a5f3dc388e..ee873b06bdc 100644
--- a/src/Presentation/Nop.Web/Infrastructure/RouteProvider.cs
+++ b/src/Presentation/Nop.Web/Infrastructure/RouteProvider.cs
@@ -226,6 +226,10 @@ public virtual void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
pattern: $"addcustomwishlist",
defaults: new { controller = "ShoppingCart", action = "AddWishlist" });
+ endpointRouteBuilder.MapControllerRoute(name: NopRouteNames.Ajax.RENAME_CUSTOM_WISHLIST,
+ pattern: $"renamecustomwishlist",
+ defaults: new { controller = "ShoppingCart", action = "RenameWishlist" });
+
//comparing products (AJAX)
endpointRouteBuilder.MapControllerRoute(name: NopRouteNames.Ajax.ADD_PRODUCT_TO_COMPARE,
pattern: $"compareproducts/add/{{productId:min(0)}}",
@@ -508,12 +512,12 @@ public virtual void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
pattern: $"{lang}/customer/gdpr",
defaults: new { controller = "Customer", action = "GdprTools" });
- //customer check gift card balance
+ //customer check gift card balance
endpointRouteBuilder.MapControllerRoute(name: NopRouteNames.General.CHECK_GIFT_CARD_BALANCE,
pattern: $"{lang}/customer/checkgiftcardbalance",
defaults: new { controller = "Customer", action = "CheckGiftCardBalance" });
- //customer multi-factor authentication settings
+ //customer multi-factor authentication settings
endpointRouteBuilder.MapControllerRoute(name: NopRouteNames.Standard.MULTI_FACTOR_AUTHENTICATION_SETTINGS,
pattern: $"{lang}/customer/multifactorauthentication",
defaults: new { controller = "Customer", action = "MultiFactorAuthentication" });