using System.Text; using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Core.Domain.Directory; using Nop.Services.Common; using Nop.Services.Directory; using Nop.Services.ExportImport; using Nop.Services.Localization; using Nop.Services.Logging; using Nop.Services.Messages; using Nop.Services.Security; using Nop.Services.Stores; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions; using Nop.Web.Areas.Admin.Models.Directory; using Nop.Web.Framework.Mvc; using Nop.Web.Framework.Mvc.Filters; namespace Nop.Web.Areas.Admin.Controllers; public partial class CountryController : BaseAdminController { #region Fields protected readonly IAddressService _addressService; protected readonly ICountryModelFactory _countryModelFactory; protected readonly ICountryService _countryService; protected readonly ICustomerActivityService _customerActivityService; protected readonly IExportManager _exportManager; protected readonly IImportManager _importManager; protected readonly ILocalizationService _localizationService; protected readonly ILocalizedEntityService _localizedEntityService; protected readonly INotificationService _notificationService; protected readonly IPermissionService _permissionService; protected readonly IStateProvinceService _stateProvinceService; protected readonly IStoreMappingService _storeMappingService; protected readonly IStoreService _storeService; #endregion #region Ctor public CountryController(IAddressService addressService, ICountryModelFactory countryModelFactory, ICountryService countryService, ICustomerActivityService customerActivityService, IExportManager exportManager, IImportManager importManager, ILocalizationService localizationService, ILocalizedEntityService localizedEntityService, INotificationService notificationService, IPermissionService permissionService, IStateProvinceService stateProvinceService, IStoreMappingService storeMappingService, IStoreService storeService) { _addressService = addressService; _countryModelFactory = countryModelFactory; _countryService = countryService; _customerActivityService = customerActivityService; _exportManager = exportManager; _importManager = importManager; _localizationService = localizationService; _localizedEntityService = localizedEntityService; _notificationService = notificationService; _permissionService = permissionService; _stateProvinceService = stateProvinceService; _storeMappingService = storeMappingService; _storeService = storeService; } #endregion #region Utilities protected virtual async Task UpdateLocalesAsync(Country country, CountryModel model) { foreach (var localized in model.Locales) { await _localizedEntityService.SaveLocalizedValueAsync(country, x => x.Name, localized.Name, localized.LanguageId); } } protected virtual async Task UpdateLocalesAsync(StateProvince stateProvince, StateProvinceModel model) { foreach (var localized in model.Locales) { await _localizedEntityService.SaveLocalizedValueAsync(stateProvince, x => x.Name, localized.Name, localized.LanguageId); } } protected virtual async Task SaveStoreMappingsAsync(Country country, CountryModel model) { country.LimitedToStores = model.SelectedStoreIds.Any(); await _countryService.UpdateCountryAsync(country); var existingStoreMappings = await _storeMappingService.GetStoreMappingsAsync(country); var allStores = await _storeService.GetAllStoresAsync(); foreach (var store in allStores) { if (model.SelectedStoreIds.Contains(store.Id)) { //new store if (!existingStoreMappings.Any(sm => sm.StoreId == store.Id)) await _storeMappingService.InsertStoreMappingAsync(country, store.Id); } else { //remove store var storeMappingToDelete = existingStoreMappings.FirstOrDefault(sm => sm.StoreId == store.Id); if (storeMappingToDelete != null) await _storeMappingService.DeleteStoreMappingAsync(storeMappingToDelete); } } } #endregion #region Countries public virtual IActionResult Index() { return RedirectToAction("List"); } [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task List() { //prepare model var model = await _countryModelFactory.PrepareCountrySearchModelAsync(new CountrySearchModel()); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task CountryList(CountrySearchModel searchModel) { //prepare model var model = await _countryModelFactory.PrepareCountryListModelAsync(searchModel); return Json(model); } [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task Create() { //prepare model var model = await _countryModelFactory.PrepareCountryModelAsync(new CountryModel(), null); return View(model); } [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task Create(CountryModel model, bool continueEditing) { if (ModelState.IsValid) { var country = model.ToEntity(); await _countryService.InsertCountryAsync(country); //activity log await _customerActivityService.InsertActivityAsync("AddNewCountry", string.Format(await _localizationService.GetResourceAsync("ActivityLog.AddNewCountry"), country.Id), country); //locales await UpdateLocalesAsync(country, model); //Stores await SaveStoreMappingsAsync(country, model); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Configuration.Countries.Added")); if (!continueEditing) return RedirectToAction("List"); return RedirectToAction("Edit", new { id = country.Id }); } //prepare model model = await _countryModelFactory.PrepareCountryModelAsync(model, null, true); //if we got this far, something failed, redisplay form return View(model); } [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task Edit(int id) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(id); if (country == null) return RedirectToAction("List"); //prepare model var model = await _countryModelFactory.PrepareCountryModelAsync(null, country); return View(model); } [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task Edit(CountryModel model, bool continueEditing) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(model.Id); if (country == null) return RedirectToAction("List"); if (ModelState.IsValid) { country = model.ToEntity(country); await _countryService.UpdateCountryAsync(country); //activity log await _customerActivityService.InsertActivityAsync("EditCountry", string.Format(await _localizationService.GetResourceAsync("ActivityLog.EditCountry"), country.Id), country); //locales await UpdateLocalesAsync(country, model); //stores await SaveStoreMappingsAsync(country, model); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Configuration.Countries.Updated")); if (!continueEditing) return RedirectToAction("List"); return RedirectToAction("Edit", new { id = country.Id }); } //prepare model model = await _countryModelFactory.PrepareCountryModelAsync(model, country, true); //if we got this far, something failed, redisplay form return View(model); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task Delete(int id) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(id); if (country == null) return RedirectToAction("List"); try { if (await _addressService.GetAddressTotalByCountryIdAsync(country.Id) > 0) throw new NopException("The country can't be deleted. It has associated addresses"); await _countryService.DeleteCountryAsync(country); //activity log await _customerActivityService.InsertActivityAsync("DeleteCountry", string.Format(await _localizationService.GetResourceAsync("ActivityLog.DeleteCountry"), country.Id), country); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Configuration.Countries.Deleted")); return RedirectToAction("List"); } catch (Exception exc) { await _notificationService.ErrorNotificationAsync(exc); return RedirectToAction("Edit", new { id = country.Id }); } } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task PublishSelected(ICollection selectedIds) { if (selectedIds == null || !selectedIds.Any()) return NoContent(); var countries = await _countryService.GetCountriesByIdsAsync([.. selectedIds]); foreach (var country in countries) { country.Published = true; await _countryService.UpdateCountryAsync(country); } return Json(new { Result = true }); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task UnpublishSelected(ICollection selectedIds) { if (selectedIds == null || !selectedIds.Any()) return NoContent(); var countries = await _countryService.GetCountriesByIdsAsync(selectedIds.ToArray()); foreach (var country in countries) { country.Published = false; await _countryService.UpdateCountryAsync(country); } return Json(new { Result = true }); } #endregion #region States / provinces [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task States(StateProvinceSearchModel searchModel) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(searchModel.CountryId) ?? throw new ArgumentException("No country found with the specified id"); //prepare model var model = await _countryModelFactory.PrepareStateProvinceListModelAsync(searchModel, country); return Json(model); } [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task StateCreatePopup(int countryId) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(countryId); if (country == null) return RedirectToAction("List"); //prepare model var model = await _countryModelFactory.PrepareStateProvinceModelAsync(new StateProvinceModel(), country, null); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task StateCreatePopup(StateProvinceModel model) { //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(model.CountryId); if (country == null) return RedirectToAction("List"); if (ModelState.IsValid) { var sp = model.ToEntity(); await _stateProvinceService.InsertStateProvinceAsync(sp); //activity log await _customerActivityService.InsertActivityAsync("AddNewStateProvince", string.Format(await _localizationService.GetResourceAsync("ActivityLog.AddNewStateProvince"), sp.Id), sp); await UpdateLocalesAsync(sp, model); ViewBag.RefreshPage = true; return View(model); } //prepare model model = await _countryModelFactory.PrepareStateProvinceModelAsync(model, country, null, true); //if we got this far, something failed, redisplay form return View(model); } [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task StateEditPopup(int id) { //try to get a state with the specified id var state = await _stateProvinceService.GetStateProvinceByIdAsync(id); if (state == null) return RedirectToAction("List"); //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(state.CountryId); if (country == null) return RedirectToAction("List"); //prepare model var model = await _countryModelFactory.PrepareStateProvinceModelAsync(null, country, state); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task StateEditPopup(StateProvinceModel model) { //try to get a state with the specified id var state = await _stateProvinceService.GetStateProvinceByIdAsync(model.Id); if (state == null) return RedirectToAction("List"); //try to get a country with the specified id var country = await _countryService.GetCountryByIdAsync(state.CountryId); if (country == null) return RedirectToAction("List"); if (ModelState.IsValid) { state = model.ToEntity(state); await _stateProvinceService.UpdateStateProvinceAsync(state); //activity log await _customerActivityService.InsertActivityAsync("EditStateProvince", string.Format(await _localizationService.GetResourceAsync("ActivityLog.EditStateProvince"), state.Id), state); await UpdateLocalesAsync(state, model); ViewBag.RefreshPage = true; return View(model); } //prepare model model = await _countryModelFactory.PrepareStateProvinceModelAsync(model, country, state, true); //if we got this far, something failed, redisplay form return View(model); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task StateDelete(int id) { //try to get a state with the specified id var state = await _stateProvinceService.GetStateProvinceByIdAsync(id) ?? throw new ArgumentException("No state found with the specified id"); if (await _addressService.GetAddressTotalByStateProvinceIdAsync(state.Id) > 0) { return ErrorJson(await _localizationService.GetResourceAsync("Admin.Configuration.Countries.States.CantDeleteWithAddresses")); } //int countryId = state.CountryId; await _stateProvinceService.DeleteStateProvinceAsync(state); //activity log await _customerActivityService.InsertActivityAsync("DeleteStateProvince", string.Format(await _localizationService.GetResourceAsync("ActivityLog.DeleteStateProvince"), state.Id), state); return new NullJsonResult(); } public virtual async Task GetStatesByCountryId(string countryId, bool? addSelectStateItem, bool? addAsterisk) { //permission validation is not required here // This action method gets called via an ajax request ArgumentException.ThrowIfNullOrEmpty(countryId); var country = await _countryService.GetCountryByIdAsync(Convert.ToInt32(countryId)); var states = country != null ? (await _stateProvinceService.GetStateProvincesByCountryIdAsync(country.Id, showHidden: true)).ToList() : new List(); var result = (from s in states select new { id = s.Id, name = s.Name }).ToList(); if (addAsterisk.HasValue && addAsterisk.Value) { //asterisk result.Insert(0, new { id = 0, name = "*" }); } else { if (country == null) { //country is not selected ("choose country" item) if (addSelectStateItem.HasValue && addSelectStateItem.Value) { result.Insert(0, new { id = 0, name = await _localizationService.GetResourceAsync("Admin.Address.SelectState") }); } else { result.Insert(0, new { id = 0, name = await _localizationService.GetResourceAsync("Admin.Address.Other") }); } } else { //some country is selected if (!result.Any()) { //country does not have states result.Insert(0, new { id = 0, name = await _localizationService.GetResourceAsync("Admin.Address.Other") }); } else { //country has some states if (addSelectStateItem.HasValue && addSelectStateItem.Value) { result.Insert(0, new { id = 0, name = await _localizationService.GetResourceAsync("Admin.Address.SelectState") }); } } } } return Json(result); } #endregion #region Export / import [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task ExportCsv() { var fileName = $"states_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}_{CommonHelper.GenerateRandomDigitCode(4)}.csv"; var states = await _stateProvinceService.GetStateProvincesAsync(true); var result = await _exportManager.ExportStatesToTxtAsync(states); return File(Encoding.UTF8.GetBytes(result), MimeTypes.TextCsv, fileName); } [HttpPost] [CheckPermission(StandardPermission.Configuration.MANAGE_COUNTRIES)] public virtual async Task ImportCsv(IFormFile importcsvfile) { try { if (importcsvfile != null && importcsvfile.Length > 0) { var count = await _importManager.ImportStatesFromTxtAsync(importcsvfile.OpenReadStream()); _notificationService.SuccessNotification(string.Format(await _localizationService.GetResourceAsync("Admin.Configuration.Countries.ImportSuccess"), count)); return RedirectToAction("List"); } _notificationService.ErrorNotification(await _localizationService.GetResourceAsync("Admin.Common.UploadFile")); return RedirectToAction("List"); } catch (Exception exc) { await _notificationService.ErrorNotificationAsync(exc); return RedirectToAction("List"); } } #endregion }