init
This commit is contained in:
parent
6c30c84067
commit
a61f230d0a
24
Dockerfile
24
Dockerfile
|
|
@ -39,6 +39,8 @@ WORKDIR /src/Plugins/Nop.Plugin.Shipping.EasyPost
|
|||
RUN dotnet build Nop.Plugin.Shipping.EasyPost.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.FixedByWeightByTotal
|
||||
RUN dotnet build Nop.Plugin.Shipping.FixedByWeightByTotal.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.ShipStation
|
||||
RUN dotnet build Nop.Plugin.Shipping.ShipStation.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.UPS
|
||||
RUN dotnet build Nop.Plugin.Shipping.UPS.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Tax.Avalara
|
||||
|
|
@ -60,24 +62,6 @@ RUN dotnet build Nop.Plugin.Widgets.What3words.csproj -c Release
|
|||
WORKDIR /src/Presentation/Nop.Web
|
||||
RUN dotnet publish Nop.Web.csproj -c Release -o /app/published
|
||||
|
||||
WORKDIR /app/published
|
||||
|
||||
RUN mkdir logs
|
||||
RUN mkdir bin
|
||||
|
||||
RUN chmod 775 App_Data/
|
||||
RUN chmod 775 App_Data/DataProtectionKeys
|
||||
RUN chmod 775 bin
|
||||
RUN chmod 775 logs
|
||||
RUN chmod 775 Plugins
|
||||
RUN chmod 775 wwwroot/bundles
|
||||
RUN chmod 775 wwwroot/db_backups
|
||||
RUN chmod 775 wwwroot/files/exportimport
|
||||
RUN chmod 775 wwwroot/icons
|
||||
RUN chmod 775 wwwroot/images
|
||||
RUN chmod 775 wwwroot/images/thumbs
|
||||
RUN chmod 775 wwwroot/images/uploaded
|
||||
|
||||
# create the runtime instance
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS runtime
|
||||
|
||||
|
|
@ -94,7 +78,9 @@ RUN apk add tzdata --no-cache
|
|||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
RUN chmod 755 /entrypoint.sh
|
||||
|
||||
WORKDIR /app
|
||||
WORKDIR /app
|
||||
RUN mkdir bin
|
||||
RUN mkdir logs
|
||||
|
||||
COPY --from=build /app/published .
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -81,9 +80,6 @@ namespace Nop.Core.Caching
|
|||
var item = JsonConvert.DeserializeObject<T>(json);
|
||||
_perRequestCache.Set(key.Key, item);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
_keys.Add(key.Key);
|
||||
|
||||
return (true, item);
|
||||
}
|
||||
|
||||
|
|
@ -103,9 +99,6 @@ namespace Nop.Core.Caching
|
|||
var item = JsonConvert.DeserializeObject<T>(json);
|
||||
_perRequestCache.Set(key.Key, item);
|
||||
|
||||
using var _ = _locker.Lock();
|
||||
_keys.Add(key.Key);
|
||||
|
||||
return (true, item);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,5 @@
|
|||
/// Gets the path to file that contains app settings
|
||||
/// </summary>
|
||||
public static string AppSettingsFilePath => "App_Data/appsettings.json";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file that contains app settings for specific hosting environment
|
||||
/// </summary>
|
||||
/// <remarks>0 - Environment name</remarks>
|
||||
public static string AppSettingsEnvironmentFilePath => "App_Data/appsettings.{0}.json";
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
/// <summary>
|
||||
/// Gets the minor store version
|
||||
/// </summary>
|
||||
public const string MINOR_VERSION = "4";
|
||||
public const string MINOR_VERSION = "0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full store version
|
||||
|
|
|
|||
|
|
@ -42,20 +42,19 @@ namespace Nop.Data.Migrations
|
|||
#endregion
|
||||
|
||||
#region Utils
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the instances for found types implementing FluentMigrator.IMigration which ready to Up process
|
||||
/// Returns the instances for found types implementing FluentMigrator.IMigration
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly to find migrations</param>
|
||||
/// <param name="migrationProcessType">Type of migration process; pass MigrationProcessType.NoMatter to load all migrations</param>
|
||||
/// <param name="migrationProcessType">Type of migration process; pass null to load all migrations</param>
|
||||
/// <returns>The instances for found types implementing FluentMigrator.IMigration</returns>
|
||||
protected virtual IEnumerable<IMigrationInfo> GetUpMigrations(Assembly assembly, MigrationProcessType migrationProcessType = MigrationProcessType.NoMatter)
|
||||
protected virtual IEnumerable<IMigrationInfo> GetMigrations(Assembly assembly, MigrationProcessType migrationProcessType = MigrationProcessType.NoMatter)
|
||||
{
|
||||
var migrations = _filteringMigrationSource
|
||||
.GetMigrations(t =>
|
||||
{
|
||||
var migrationAttribute = t.GetCustomAttribute<NopMigrationAttribute>();
|
||||
|
||||
if (migrationAttribute is null || _versionLoader.Value.VersionInfo.HasAppliedMigration(migrationAttribute.Version))
|
||||
return false;
|
||||
|
||||
|
|
@ -65,40 +64,12 @@ namespace Nop.Data.Migrations
|
|||
return false;
|
||||
|
||||
return assembly == null || t.Assembly == assembly;
|
||||
|
||||
}) ?? Enumerable.Empty<IMigration>();
|
||||
|
||||
return migrations
|
||||
.Select(m => _migrationRunnerConventions.GetMigrationInfoForMigration(m))
|
||||
.OrderBy(migration => migration.Version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the instances for found types implementing FluentMigrator.IMigration which ready to Down process
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly to find migrations</param>
|
||||
/// <param name="migrationProcessType">Type of migration process; pass MigrationProcessType.NoMatter to load all migrations</param>
|
||||
/// <returns>The instances for found types implementing FluentMigrator.IMigration</returns>
|
||||
protected virtual IEnumerable<IMigrationInfo> GetDownMigrations(Assembly assembly, MigrationProcessType migrationProcessType = MigrationProcessType.NoMatter)
|
||||
{
|
||||
var migrations = _filteringMigrationSource
|
||||
.GetMigrations(t =>
|
||||
{
|
||||
var migrationAttribute = t.GetCustomAttribute<NopMigrationAttribute>();
|
||||
|
||||
if (migrationAttribute is null || !_versionLoader.Value.VersionInfo.HasAppliedMigration(migrationAttribute.Version))
|
||||
return false;
|
||||
|
||||
if (migrationAttribute.TargetMigrationProcess != MigrationProcessType.NoMatter &&
|
||||
migrationProcessType != MigrationProcessType.NoMatter &&
|
||||
migrationProcessType != migrationAttribute.TargetMigrationProcess)
|
||||
return false;
|
||||
|
||||
return assembly == null || t.Assembly == assembly;
|
||||
}) ?? Enumerable.Empty<IMigration>();
|
||||
|
||||
return migrations
|
||||
.Select(m => _migrationRunnerConventions.GetMigrationInfoForMigration(m))
|
||||
//.OrderBy(m => m.Migration.GetType().GetCustomAttribute<NopMigrationAttribute>().MigrationTarget)
|
||||
//.ThenBy(migration => migration.Version);
|
||||
.OrderBy(migration => migration.Version);
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +87,7 @@ namespace Nop.Data.Migrations
|
|||
if (assembly is null)
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
|
||||
foreach (var migrationInfo in GetUpMigrations(assembly, migrationProcessType))
|
||||
foreach (var migrationInfo in GetMigrations(assembly, migrationProcessType))
|
||||
{
|
||||
_migrationRunner.Up(migrationInfo.Migration);
|
||||
|
||||
|
|
@ -131,7 +102,7 @@ namespace Nop.Data.Migrations
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all found (and applied) migrations
|
||||
/// Executes all found (and unapplied) migrations
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly to find the migration</param>
|
||||
public void ApplyDownMigrations(Assembly assembly)
|
||||
|
|
@ -139,7 +110,9 @@ namespace Nop.Data.Migrations
|
|||
if(assembly is null)
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
|
||||
foreach (var migrationInfo in GetDownMigrations(assembly).Reverse())
|
||||
var migrations = GetMigrations(assembly).Reverse();
|
||||
|
||||
foreach (var migrationInfo in migrations)
|
||||
{
|
||||
_migrationRunner.Down(migrationInfo.Migration);
|
||||
_versionLoader.Value.DeleteVersion(migrationInfo.Version);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<PackageReference Include="FluentMigrator.Runner" Version="3.3.1" />
|
||||
<PackageReference Include="linq2db" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="4.0.0" />
|
||||
<PackageReference Include="MySql.Data" Version="8.0.31" />
|
||||
<PackageReference Include="MySql.Data" Version="8.0.27" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.1" />
|
||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Localization;
|
||||
|
|
@ -14,6 +12,7 @@ using Nop.Core.Http.Extensions;
|
|||
using Nop.Data;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Html;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Messages;
|
||||
|
||||
|
|
@ -28,7 +27,6 @@ namespace Nop.Services.Authentication.External
|
|||
|
||||
private readonly CustomerSettings _customerSettings;
|
||||
private readonly ExternalAuthenticationSettings _externalAuthenticationSettings;
|
||||
private readonly IActionContextAccessor _actionContextAccessor;
|
||||
private readonly IAuthenticationPluginManager _authenticationPluginManager;
|
||||
private readonly ICustomerRegistrationService _customerRegistrationService;
|
||||
private readonly ICustomerService _customerService;
|
||||
|
|
@ -38,7 +36,6 @@ namespace Nop.Services.Authentication.External
|
|||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IRepository<ExternalAuthenticationRecord> _externalAuthenticationRecordRepository;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
private readonly IWorkContext _workContext;
|
||||
private readonly IWorkflowMessageService _workflowMessageService;
|
||||
private readonly LocalizationSettings _localizationSettings;
|
||||
|
|
@ -49,7 +46,6 @@ namespace Nop.Services.Authentication.External
|
|||
|
||||
public ExternalAuthenticationService(CustomerSettings customerSettings,
|
||||
ExternalAuthenticationSettings externalAuthenticationSettings,
|
||||
IActionContextAccessor actionContextAccessor,
|
||||
IAuthenticationPluginManager authenticationPluginManager,
|
||||
ICustomerRegistrationService customerRegistrationService,
|
||||
ICustomerService customerService,
|
||||
|
|
@ -59,14 +55,12 @@ namespace Nop.Services.Authentication.External
|
|||
ILocalizationService localizationService,
|
||||
IRepository<ExternalAuthenticationRecord> externalAuthenticationRecordRepository,
|
||||
IStoreContext storeContext,
|
||||
IUrlHelperFactory urlHelperFactory,
|
||||
IWorkContext workContext,
|
||||
IWorkflowMessageService workflowMessageService,
|
||||
LocalizationSettings localizationSettings)
|
||||
{
|
||||
_customerSettings = customerSettings;
|
||||
_externalAuthenticationSettings = externalAuthenticationSettings;
|
||||
_actionContextAccessor = actionContextAccessor;
|
||||
_authenticationPluginManager = authenticationPluginManager;
|
||||
_customerRegistrationService = customerRegistrationService;
|
||||
_customerService = customerService;
|
||||
|
|
@ -76,7 +70,6 @@ namespace Nop.Services.Authentication.External
|
|||
_localizationService = localizationService;
|
||||
_externalAuthenticationRecordRepository = externalAuthenticationRecordRepository;
|
||||
_storeContext = storeContext;
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
_workContext = workContext;
|
||||
_workflowMessageService = workflowMessageService;
|
||||
_localizationSettings = localizationSettings;
|
||||
|
|
@ -247,10 +240,8 @@ namespace Nop.Services.Authentication.External
|
|||
/// <returns>Result of an authentication</returns>
|
||||
protected virtual IActionResult SuccessfulAuthentication(string returnUrl)
|
||||
{
|
||||
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
|
||||
|
||||
//redirect to the return URL if it's specified
|
||||
if (!string.IsNullOrEmpty(returnUrl) && urlHelper.IsLocalUrl(returnUrl))
|
||||
if (!string.IsNullOrEmpty(returnUrl))
|
||||
return new RedirectResult(returnUrl);
|
||||
|
||||
return new RedirectToRouteResult("Homepage", null);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ using System.Threading.Tasks;
|
|||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Discounts;
|
||||
using Nop.Core.Domain.Media;
|
||||
using Nop.Core.Infrastructure;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Media;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Services.Seo;
|
||||
using Nop.Services.Stores;
|
||||
|
||||
|
|
@ -653,7 +651,6 @@ namespace Nop.Services.Catalog
|
|||
MetaTitle = product.MetaTitle,
|
||||
AllowCustomerReviews = product.AllowCustomerReviews,
|
||||
LimitedToStores = product.LimitedToStores,
|
||||
SubjectToAcl = product.SubjectToAcl,
|
||||
Sku = newSku,
|
||||
ManufacturerPartNumber = product.ManufacturerPartNumber,
|
||||
Gtin = product.Gtin,
|
||||
|
|
@ -806,18 +803,11 @@ namespace Nop.Services.Catalog
|
|||
await CopyAttributesMappingAsync(product, productCopy, originalNewPictureIdentifiers);
|
||||
//product <-> discounts mapping
|
||||
await CopyDiscountsMappingAsync(product, productCopy);
|
||||
|
||||
//store mapping
|
||||
var selectedStoreIds = await _storeMappingService.GetStoresIdsWithAccessAsync(product);
|
||||
foreach (var id in selectedStoreIds)
|
||||
await _storeMappingService.InsertStoreMappingAsync(productCopy, id);
|
||||
|
||||
//customer role mapping
|
||||
var aclService = EngineContext.Current.Resolve<IAclService>();
|
||||
var customerRoleIds = await aclService.GetCustomerRoleIdsWithAccessAsync(product);
|
||||
foreach (var id in customerRoleIds)
|
||||
await aclService.InsertAclRecordAsync(productCopy, id);
|
||||
|
||||
//tier prices
|
||||
await CopyTierPricesAsync(product, productCopy);
|
||||
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@ namespace Nop.Services.Catalog
|
|||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string FilterableSpecificationAttributeOptionsPrefix => "Nop.specificationattributeoption";
|
||||
public static string FilterableSpecificationAttributeOptionsPrefix => "Nop.filterablespecificationattributeoptions";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for specification attribute groups caching by product id
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ namespace Nop.Services.Catalog
|
|||
if (value.PriceAdjustmentUsePercentage)
|
||||
{
|
||||
if (!productPrice.HasValue)
|
||||
productPrice = (await GetFinalPriceAsync(product, customer)).finalPrice;
|
||||
productPrice = (await GetFinalPriceAsync(product, customer)).priceWithoutDiscounts;
|
||||
|
||||
adjustment = (decimal)((float)productPrice * (float)value.PriceAdjustment / 100f);
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ namespace Nop.Services.Catalog
|
|||
//bundled product
|
||||
var associatedProduct = await _productService.GetProductByIdAsync(value.AssociatedProductId);
|
||||
if (associatedProduct != null)
|
||||
adjustment = (await GetFinalPriceAsync(associatedProduct, customer)).finalPrice * value.Quantity;
|
||||
adjustment = (await GetFinalPriceAsync(associatedProduct, customer)).priceWithoutDiscounts * value.Quantity;
|
||||
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -41,16 +41,16 @@ namespace Nop.Services.Catalog
|
|||
keyGroup = localizedProperty.LocaleKeyGroup,
|
||||
key = localizedProperty.LocaleKey
|
||||
} into localizedProperties
|
||||
from localizedProperty in localizedProperties.DefaultIfEmpty(new LocalizedProperty { LocaleValue = product.Name })
|
||||
from localizedProperty in localizedProperties.DefaultIfEmpty(new LocalizedProperty {LocaleValue = product.Name})
|
||||
select new { localizedProperty, product };
|
||||
|
||||
if (orderBy == ProductSortingEnum.NameAsc)
|
||||
productsQuery = from item in query
|
||||
orderby item.localizedProperty.LocaleValue, item.product.Name
|
||||
orderby item.localizedProperty.LocaleValue
|
||||
select item.product;
|
||||
else
|
||||
productsQuery = from item in query
|
||||
orderby item.localizedProperty.LocaleValue descending, item.product.Name descending
|
||||
orderby item.localizedProperty.LocaleValue descending
|
||||
select item.product;
|
||||
|
||||
return productsQuery;
|
||||
|
|
|
|||
|
|
@ -872,21 +872,6 @@ namespace Nop.Services.Catalog
|
|||
(searchSku && p.Sku == keywords)
|
||||
select p.Id;
|
||||
|
||||
if (searchLocalizedValue)
|
||||
{
|
||||
productsByKeywords = productsByKeywords.Union(
|
||||
from lp in _localizedPropertyRepository.Table
|
||||
let checkName = lp.LocaleKey == nameof(Product.Name) &&
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
let checkShortDesc = searchDescriptions &&
|
||||
lp.LocaleKey == nameof(Product.ShortDescription) &&
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
where
|
||||
lp.LocaleKeyGroup == nameof(Product) && lp.LanguageId == languageId && (checkName || checkShortDesc)
|
||||
|
||||
select lp.EntityId);
|
||||
}
|
||||
|
||||
//search by SKU for ProductAttributeCombination
|
||||
if (searchSku)
|
||||
{
|
||||
|
|
@ -901,7 +886,7 @@ namespace Nop.Services.Catalog
|
|||
productsByKeywords = productsByKeywords.Union(
|
||||
from pptm in _productTagMappingRepository.Table
|
||||
join pt in _productTagRepository.Table on pptm.ProductTagId equals pt.Id
|
||||
where pt.Name.Contains(keywords)
|
||||
where pt.Name == keywords
|
||||
select pptm.ProductId
|
||||
);
|
||||
|
||||
|
|
@ -912,12 +897,31 @@ namespace Nop.Services.Catalog
|
|||
join lp in _localizedPropertyRepository.Table on pptm.ProductTagId equals lp.EntityId
|
||||
where lp.LocaleKeyGroup == nameof(ProductTag) &&
|
||||
lp.LocaleKey == nameof(ProductTag.Name) &&
|
||||
lp.LocaleValue.Contains(keywords) &&
|
||||
lp.LanguageId == languageId
|
||||
select pptm.ProductId);
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
select lp.EntityId);
|
||||
}
|
||||
}
|
||||
|
||||
if (searchLocalizedValue)
|
||||
{
|
||||
productsByKeywords = productsByKeywords.Union(
|
||||
from lp in _localizedPropertyRepository.Table
|
||||
let checkName = lp.LocaleKey == nameof(Product.Name) &&
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
let checkShortDesc = searchDescriptions &&
|
||||
lp.LocaleKey == nameof(Product.ShortDescription) &&
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
let checkProductTags = searchProductTags &&
|
||||
lp.LocaleKeyGroup == nameof(ProductTag) &&
|
||||
lp.LocaleKey == nameof(ProductTag.Name) &&
|
||||
lp.LocaleValue.Contains(keywords)
|
||||
where
|
||||
lp.LocaleKeyGroup == nameof(Product) && lp.LanguageId == languageId && (checkName || checkShortDesc) ||
|
||||
checkProductTags
|
||||
|
||||
select lp.EntityId);
|
||||
}
|
||||
|
||||
productsQuery =
|
||||
from p in productsQuery
|
||||
join pbk in productsByKeywords on p.Id equals pbk
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Events;
|
||||
|
|
@ -27,7 +25,6 @@ namespace Nop.Services.Customers
|
|||
#region Fields
|
||||
|
||||
private readonly CustomerSettings _customerSettings;
|
||||
private readonly IActionContextAccessor _actionContextAccessor;
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly ICustomerActivityService _customerActivityService;
|
||||
private readonly ICustomerService _customerService;
|
||||
|
|
@ -42,7 +39,6 @@ namespace Nop.Services.Customers
|
|||
private readonly IShoppingCartService _shoppingCartService;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly IStoreService _storeService;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
private readonly IWorkContext _workContext;
|
||||
private readonly IWorkflowMessageService _workflowMessageService;
|
||||
private readonly RewardPointsSettings _rewardPointsSettings;
|
||||
|
|
@ -52,7 +48,6 @@ namespace Nop.Services.Customers
|
|||
#region Ctor
|
||||
|
||||
public CustomerRegistrationService(CustomerSettings customerSettings,
|
||||
IActionContextAccessor actionContextAccessor,
|
||||
IAuthenticationService authenticationService,
|
||||
ICustomerActivityService customerActivityService,
|
||||
ICustomerService customerService,
|
||||
|
|
@ -67,13 +62,11 @@ namespace Nop.Services.Customers
|
|||
IShoppingCartService shoppingCartService,
|
||||
IStoreContext storeContext,
|
||||
IStoreService storeService,
|
||||
IUrlHelperFactory urlHelperFactory,
|
||||
IWorkContext workContext,
|
||||
IWorkflowMessageService workflowMessageService,
|
||||
RewardPointsSettings rewardPointsSettings)
|
||||
{
|
||||
_customerSettings = customerSettings;
|
||||
_actionContextAccessor = actionContextAccessor;
|
||||
_authenticationService = authenticationService;
|
||||
_customerActivityService = customerActivityService;
|
||||
_customerService = customerService;
|
||||
|
|
@ -88,7 +81,6 @@ namespace Nop.Services.Customers
|
|||
_shoppingCartService = shoppingCartService;
|
||||
_storeContext = storeContext;
|
||||
_storeService = storeService;
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
_workContext = workContext;
|
||||
_workflowMessageService = workflowMessageService;
|
||||
_rewardPointsSettings = rewardPointsSettings;
|
||||
|
|
@ -442,10 +434,8 @@ namespace Nop.Services.Customers
|
|||
await _customerActivityService.InsertActivityAsync(customer, "PublicStore.Login",
|
||||
await _localizationService.GetResourceAsync("ActivityLog.PublicStore.Login"), customer);
|
||||
|
||||
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
|
||||
|
||||
//redirect to the return URL if it's specified
|
||||
if (!string.IsNullOrEmpty(returnUrl) && urlHelper.IsLocalUrl(returnUrl))
|
||||
if (!string.IsNullOrEmpty(returnUrl))
|
||||
return new RedirectResult(returnUrl);
|
||||
|
||||
return new RedirectToRouteResult("Homepage", null);
|
||||
|
|
|
|||
|
|
@ -1659,18 +1659,11 @@ namespace Nop.Services.ExportImport
|
|||
//category mappings
|
||||
var categories = isNew || !allProductsCategoryIds.ContainsKey(product.Id) ? Array.Empty<int>() : allProductsCategoryIds[product.Id];
|
||||
|
||||
var storesIds = product.LimitedToStores
|
||||
? (await _storeMappingService.GetStoresIdsWithAccessAsync(product)).ToList()
|
||||
: new List<int>();
|
||||
|
||||
var importedCategories = await categoryList.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(categoryName => new CategoryKey(categoryName, storesIds))
|
||||
.Select(categoryName => new CategoryKey(categoryName))
|
||||
.SelectAwait(async categoryKey =>
|
||||
{
|
||||
var rez = (allCategories.ContainsKey(categoryKey) ? allCategories[categoryKey].Id : allCategories.Values.FirstOrDefault(c => c.Name == categoryKey.Key)?.Id) ??
|
||||
allCategories.FirstOrDefault(p =>
|
||||
p.Key.Key.Equals(categoryKey.Key, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Value?.Id;
|
||||
var rez = allCategories.ContainsKey(categoryKey) ? allCategories[categoryKey].Id : allCategories.Values.FirstOrDefault(c => c.Name == categoryKey.Key)?.Id;
|
||||
|
||||
if (!rez.HasValue && int.TryParse(categoryKey.Key, out var id))
|
||||
rez = id;
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ namespace Nop.Services.Html
|
|||
private static readonly Regex regexBold = new(@"\[b\](.+?)\[/b\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexItalic = new(@"\[i\](.+?)\[/i\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexUnderLine = new(@"\[u\](.+?)\[/u\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexUrl1 = new(@"\[url\=(https?:.+?)\]([^\]]+)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexUrl2 = new(@"\[url\](https?:.+?)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexUrl1 = new(@"\[url\=([^\]]+)\]([^\]]+)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexUrl2 = new(@"\[url\](.+?)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexQuote = new(@"\[quote=(.+?)\](.+?)\[/quote\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex regexImg = new(@"\[img\](.+?)\[/img\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
|
|
|
|||
|
|
@ -543,8 +543,7 @@ namespace Nop.Services.Media.RoxyFileman
|
|||
if (GetFileType(_fileProvider.GetFileExtension(files[i])) == "image")
|
||||
{
|
||||
await using var stream = new FileStream(physicalPath, FileMode.Open);
|
||||
var skData = SKData.Create(stream);
|
||||
var image = SKBitmap.DecodeBounds(skData);
|
||||
var image = SKBitmap.DecodeBounds(stream);
|
||||
width = image.Width;
|
||||
height = image.Height;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ namespace Nop.Services.Orders
|
|||
bool allowHyperlinks = true)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
var currentLanguage = await _workContext.GetWorkingLanguageAsync();
|
||||
var currentLanguage = _workContext.GetWorkingLanguageAsync();
|
||||
var attributes = await _checkoutAttributeParser.ParseCheckoutAttributesAsync(attributesXml);
|
||||
for (var i = 0; i < attributes.Count; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,11 +74,11 @@ namespace Nop.Services.Orders
|
|||
/// <summary>
|
||||
/// Get sales summary report
|
||||
/// </summary>
|
||||
/// <param name="storeId">Store identifier (orders placed in a specific store); 0 to load all records</param>
|
||||
/// <param name="vendorId">Vendor identifier; 0 to load all records</param>
|
||||
/// <param name="categoryId">Category identifier; 0 to load all records</param>
|
||||
/// <param name="productId">Product identifier; 0 to load all records</param>
|
||||
/// <param name="manufacturerId">Manufacturer identifier; 0 to load all records</param>
|
||||
/// <param name="storeId">Store identifier (orders placed in a specific store); 0 to load all records</param>
|
||||
/// <param name="vendorId">Vendor identifier; 0 to load all records</param>
|
||||
/// <param name="createdFromUtc">Order created date from (UTC); null to load all records</param>
|
||||
/// <param name="createdToUtc">Order created date to (UTC); null to load all records</param>
|
||||
/// <param name="os">Order status; null to load all records</param>
|
||||
|
|
|
|||
|
|
@ -269,26 +269,18 @@ namespace Nop.Services.Orders
|
|||
query = query.Where(o => o.Id == orderId);
|
||||
|
||||
if (vendorId > 0)
|
||||
{
|
||||
query = from o in query
|
||||
join oi in _orderItemRepository.Table on o.Id equals oi.OrderId
|
||||
join p in _productRepository.Table on oi.ProductId equals p.Id
|
||||
where p.VendorId == vendorId
|
||||
select o;
|
||||
|
||||
query = query.Distinct();
|
||||
}
|
||||
|
||||
if (productId > 0)
|
||||
{
|
||||
query = from o in query
|
||||
join oi in _orderItemRepository.Table on o.Id equals oi.OrderId
|
||||
where oi.ProductId == productId
|
||||
select o;
|
||||
|
||||
query = query.Distinct();
|
||||
}
|
||||
|
||||
if (warehouseId > 0)
|
||||
{
|
||||
var manageStockInventoryMethodId = (int)ManageInventoryMethod.ManageStock;
|
||||
|
|
@ -305,8 +297,6 @@ namespace Nop.Services.Orders
|
|||
//we use standard "warehouse" property
|
||||
((p.ManageInventoryMethodId != manageStockInventoryMethodId || !p.UseMultipleWarehouses) && p.WarehouseId == warehouseId)
|
||||
select o;
|
||||
|
||||
query = query.Distinct();
|
||||
}
|
||||
|
||||
query = from o in query
|
||||
|
|
@ -337,15 +327,11 @@ namespace Nop.Services.Orders
|
|||
query = query.Where(o => endTimeUtc.Value >= o.CreatedOnUtc);
|
||||
|
||||
if (!string.IsNullOrEmpty(orderNotes))
|
||||
{
|
||||
query = from o in query
|
||||
join n in _orderNoteRepository.Table on o.Id equals n.OrderId
|
||||
where n.Note.Contains(orderNotes)
|
||||
select o;
|
||||
|
||||
query.Distinct();
|
||||
}
|
||||
|
||||
var item = await (from oq in query
|
||||
group oq by 1
|
||||
into result
|
||||
|
|
@ -448,11 +434,11 @@ namespace Nop.Services.Orders
|
|||
/// <summary>
|
||||
/// Get sales summary report
|
||||
/// </summary>
|
||||
/// <param name="storeId">Store identifier (orders placed in a specific store); 0 to load all records</param>
|
||||
/// <param name="vendorId">Vendor identifier; 0 to load all records</param>
|
||||
/// <param name="categoryId">Category identifier; 0 to load all records</param>
|
||||
/// <param name="productId">Product identifier; 0 to load all records</param>
|
||||
/// <param name="manufacturerId">Manufacturer identifier; 0 to load all records</param>
|
||||
/// <param name="storeId">Store identifier (orders placed in a specific store); 0 to load all records</param>
|
||||
/// <param name="vendorId">Vendor identifier; 0 to load all records</param>
|
||||
/// <param name="createdFromUtc">Order created date from (UTC); null to load all records</param>
|
||||
/// <param name="createdToUtc">Order created date to (UTC); null to load all records</param>
|
||||
/// <param name="os">Order status; null to load all records</param>
|
||||
|
|
@ -545,15 +531,6 @@ namespace Nop.Services.Orders
|
|||
if (storeId > 0)
|
||||
query = query.Where(o => o.StoreId == storeId);
|
||||
|
||||
if (vendorId > 0)
|
||||
{
|
||||
query = from o in query
|
||||
join oi in _orderItemRepository.Table on o.Id equals oi.OrderId
|
||||
join p in _productRepository.Table on oi.ProductId equals p.Id
|
||||
where p.VendorId == vendorId
|
||||
select o;
|
||||
}
|
||||
|
||||
var primaryStoreCurrency = await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId);
|
||||
|
||||
var items = groupBy switch
|
||||
|
|
|
|||
|
|
@ -264,34 +264,24 @@ namespace Nop.Services.Orders
|
|||
continue;
|
||||
|
||||
//prepare warning message
|
||||
var url = urlHelper.RouteUrl(nameof(Product), new { SeName = await _urlRecordService.GetSeNameAsync(requiredProduct) });
|
||||
var requiredProductName = WebUtility.HtmlEncode(await _localizationService.GetLocalizedAsync(requiredProduct, x => x.Name));
|
||||
var requiredProductWarning = _catalogSettings.UseLinksInRequiredProductWarnings
|
||||
? string.Format(warningLocale, $"<a href=\"{url}\">{requiredProductName}</a>", requiredProductRequiredQuantity)
|
||||
? string.Format(warningLocale, $"<a href=\"{urlHelper.RouteUrl(nameof(Product), new { SeName = await _urlRecordService.GetSeNameAsync(requiredProduct) })}\">{requiredProductName}</a>", requiredProductRequiredQuantity)
|
||||
: string.Format(warningLocale, requiredProductName, requiredProductRequiredQuantity);
|
||||
|
||||
//add to cart (if possible)
|
||||
if (addRequiredProducts && product.AutomaticallyAddRequiredProducts)
|
||||
{
|
||||
//do not add required products to prevent circular references
|
||||
var addToCartWarnings = await GetShoppingCartItemWarningsAsync(
|
||||
customer: customer,
|
||||
product: requiredProduct,
|
||||
attributesXml: null,
|
||||
customerEnteredPrice: decimal.Zero,
|
||||
shoppingCartType: shoppingCartType,
|
||||
storeId: storeId,
|
||||
quantity: quantityToAdd,
|
||||
addRequiredProducts: true);
|
||||
var addToCartWarnings = await AddToCartAsync(customer, requiredProduct, shoppingCartType, storeId,
|
||||
quantity: quantityToAdd, addRequiredProducts: false);
|
||||
|
||||
//don't display all specific errors only the generic one
|
||||
if (addToCartWarnings.Any())
|
||||
warnings.Add(requiredProductWarning);
|
||||
}
|
||||
else
|
||||
{
|
||||
warnings.Add(requiredProductWarning);
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
|
|
@ -1565,11 +1555,6 @@ namespace Nop.Services.Orders
|
|||
customerEnteredPrice, rentalStartDate, rentalEndDate,
|
||||
newQuantity, addRequiredProducts, shoppingCartItem.Id));
|
||||
|
||||
if (warnings.Any())
|
||||
return warnings;
|
||||
|
||||
await addRequiredProductsToCartAsync();
|
||||
|
||||
if (warnings.Any())
|
||||
return warnings;
|
||||
|
||||
|
|
@ -1587,11 +1572,6 @@ namespace Nop.Services.Orders
|
|||
rentalStartDate, rentalEndDate,
|
||||
quantity, addRequiredProducts));
|
||||
|
||||
if (warnings.Any())
|
||||
return warnings;
|
||||
|
||||
await addRequiredProductsToCartAsync();
|
||||
|
||||
if (warnings.Any())
|
||||
return warnings;
|
||||
|
||||
|
|
@ -1643,43 +1623,6 @@ namespace Nop.Services.Orders
|
|||
}
|
||||
|
||||
return warnings;
|
||||
|
||||
async Task addRequiredProductsToCartAsync()
|
||||
{
|
||||
//get these required products
|
||||
var requiredProducts = await _productService.GetProductsByIdsAsync(_productService.ParseRequiredProductIds(product));
|
||||
if (!requiredProducts.Any())
|
||||
return;
|
||||
|
||||
foreach (var requiredProduct in requiredProducts)
|
||||
{
|
||||
var productsRequiringRequiredProduct = await GetProductsRequiringProductAsync(cart, requiredProduct);
|
||||
|
||||
//get the required quantity of the required product
|
||||
var requiredProductRequiredQuantity = quantity +
|
||||
cart.Where(ci => productsRequiringRequiredProduct.Any(p => p.Id == ci.ProductId))
|
||||
.Where(item => item.Id != (shoppingCartItem?.Id ?? 0))
|
||||
.Sum(item => item.Quantity);
|
||||
|
||||
//whether required product is already in the cart in the required quantity
|
||||
var quantityToAdd = requiredProductRequiredQuantity - (cart.FirstOrDefault(item => item.ProductId == requiredProduct.Id)?.Quantity ?? 0);
|
||||
if (quantityToAdd <= 0)
|
||||
continue;
|
||||
|
||||
if (addRequiredProducts && product.AutomaticallyAddRequiredProducts)
|
||||
{
|
||||
//do not add required products to prevent circular references
|
||||
var addToCartWarnings = await AddToCartAsync(customer, requiredProduct, shoppingCartType, storeId,
|
||||
quantity: quantityToAdd, addRequiredProducts: requiredProduct.AutomaticallyAddRequiredProducts);
|
||||
|
||||
if (addToCartWarnings.Any())
|
||||
{
|
||||
warnings.AddRange(addToCartWarnings);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ namespace Nop.Services.ScheduleTasks
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (!_disposed && _timer != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
if (RunOnlyOnce)
|
||||
Dispose();
|
||||
|
|
|
|||
|
|
@ -535,11 +535,9 @@ namespace Nop.Services.Seo
|
|||
getRouteParamsAwait != null ? await getRouteParamsAwait(null) : null,
|
||||
await GetHttpProtocolAsync());
|
||||
|
||||
var store = await _storeContext.GetCurrentStoreAsync();
|
||||
|
||||
var updatedOn = dateTimeUpdatedOn ?? DateTime.UtcNow;
|
||||
var languages = _localizationSettings.SeoFriendlyUrlsForLanguagesEnabled
|
||||
? await _languageService.GetAllLanguagesAsync(storeId: store.Id)
|
||||
? await _languageService.GetAllLanguagesAsync()
|
||||
: null;
|
||||
|
||||
if (languages == null)
|
||||
|
|
|
|||
|
|
@ -410,26 +410,18 @@ namespace Nop.Services.Shipping
|
|||
public virtual async Task<IShipmentTracker> GetShipmentTrackerAsync(Shipment shipment)
|
||||
{
|
||||
var order = await _orderRepository.GetByIdAsync(shipment.OrderId, cache => default);
|
||||
IShipmentTracker shipmentTracker = null;
|
||||
|
||||
if (order.PickupInStore)
|
||||
{
|
||||
var pickupPointProvider = await _pickupPluginManager
|
||||
.LoadPluginBySystemNameAsync(order.ShippingRateComputationMethodSystemName);
|
||||
|
||||
if (pickupPointProvider != null)
|
||||
shipmentTracker = await pickupPointProvider.GetShipmentTrackerAsync();
|
||||
}
|
||||
else
|
||||
if (!order.PickupInStore)
|
||||
{
|
||||
var shippingRateComputationMethod = await _shippingPluginManager
|
||||
.LoadPluginBySystemNameAsync(order.ShippingRateComputationMethodSystemName);
|
||||
|
||||
if (shippingRateComputationMethod != null)
|
||||
shipmentTracker = await shippingRateComputationMethod.GetShipmentTrackerAsync();
|
||||
return await shippingRateComputationMethod?.GetShipmentTrackerAsync();
|
||||
}
|
||||
|
||||
return shipmentTracker;
|
||||
var pickupPointProvider = await _pickupPluginManager
|
||||
.LoadPluginBySystemNameAsync(order.ShippingRateComputationMethodSystemName);
|
||||
return await pickupPointProvider?.GetShipmentTrackerAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nop.Plugin.Misc.Sendinblue"
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nop.Plugin.Tax.Avalara", "Plugins\Nop.Plugin.Tax.Avalara\Nop.Plugin.Tax.Avalara.csproj", "{2426B75D-23D4-41EF-8924-47650C8A18C7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nop.Plugin.Shipping.ShipStation", "Plugins\Nop.Plugin.Shipping.ShipStation\Nop.Plugin.Shipping.ShipStation.csproj", "{83F05E8D-2D6C-41FC-A95C-9621ED358615}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nop.Plugin.Widgets.FacebookPixel", "Plugins\Nop.Plugin.Widgets.FacebookPixel\Nop.Plugin.Widgets.FacebookPixel.csproj", "{87013DC8-D032-465B-A950-753E4EB15502}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nop.Plugin.MultiFactorAuth.GoogleAuthenticator", "Plugins\Nop.Plugin.MultiFactorAuth.GoogleAuthenticator\Nop.Plugin.MultiFactorAuth.GoogleAuthenticator.csproj", "{F9F61CE0-6FC0-43A5-930A-A27099F4DE44}"
|
||||
|
|
@ -279,6 +281,18 @@ Global
|
|||
{2426B75D-23D4-41EF-8924-47650C8A18C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{2426B75D-23D4-41EF-8924-47650C8A18C7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2426B75D-23D4-41EF-8924-47650C8A18C7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615}.Release|x86.Build.0 = Release|Any CPU
|
||||
{87013DC8-D032-465B-A950-753E4EB15502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87013DC8-D032-465B-A950-753E4EB15502}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87013DC8-D032-465B-A950-753E4EB15502}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -399,6 +413,7 @@ Global
|
|||
{4C0C889A-A3B2-43FA-824A-F529402C1BBF} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{7B08DACB-8E11-416D-9DDF-B0EEF847A8ED} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{2426B75D-23D4-41EF-8924-47650C8A18C7} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{83F05E8D-2D6C-41FC-A95C-9621ED358615} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{87013DC8-D032-465B-A950-753E4EB15502} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{F9F61CE0-6FC0-43A5-930A-A27099F4DE44} = {7881B112-7843-4542-B1F7-F99553FB9BB7}
|
||||
{FD31C133-B4DE-4BB2-B327-1D0E2F2C95D4} = {E8FC6874-E230-468A-9685-4747354B92FF}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<form asp-controller="GoogleAuthenticator" asp-action="Configure" method="post">
|
||||
<selection class="content">
|
||||
<div class="container-fluid">
|
||||
<dv class="container-fluid">
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
|
|
@ -102,6 +102,5 @@
|
|||
})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</selection>
|
||||
</form>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"Group": "Multi-factor authentication",
|
||||
"FriendlyName": "Google Authenticator",
|
||||
"SystemName": "MultiFactorAuth.GoogleAuthenticator",
|
||||
"Version": "1.11",
|
||||
"Version": "1.10",
|
||||
"SupportedVersions": [ "4.50" ],
|
||||
"Author": "nopCommerce team",
|
||||
"DisplayOrder": 1,
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace PayPalCheckoutSdk.Payments
|
||||
{
|
||||
/// <summary>
|
||||
/// Voids, or cancels, an authorized payment, by ID. You cannot void an authorized payment that has been fully captured.
|
||||
/// </summary>
|
||||
public class VoidRequest : PayPalHttp.HttpRequest
|
||||
{
|
||||
public VoidRequest(string authorizationId) : base("/v2/payments/authorizations/{authorization_id}/void?", HttpMethod.Post, typeof(Authorization))
|
||||
{
|
||||
try
|
||||
{
|
||||
Path = Path.Replace("{authorization_id}", Uri.EscapeDataString(Convert.ToString(authorizationId)));
|
||||
}
|
||||
catch { }
|
||||
|
||||
ContentType = "application/json";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -78,12 +78,6 @@ namespace Nop.Plugin.Payments.PayPalCommerce
|
|||
"PAYMENT.CAPTURE.REFUNDED"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of currencies that do not support decimals.
|
||||
/// Refer to https://developer.paypal.com/docs/integration/direct/rest/currency-codes/ for more information
|
||||
/// </summary>
|
||||
public static List<string> CurrenciesWithoutDecimals => new() { "HUF", "JPY", "TWD" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a name of the view component to display payment info in public store
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -427,12 +427,6 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
};
|
||||
}
|
||||
|
||||
PayPalCheckoutSdk.Orders.Money prepareMoney(decimal value) => new()
|
||||
{
|
||||
CurrencyCode = currency,
|
||||
Value = value.ToString(PayPalCommerceDefaults.CurrenciesWithoutDecimals.Contains(currency.ToUpperInvariant()) ? "0" : "0.00", CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
//set order items
|
||||
purchaseUnit.Items = await shoppingCart.SelectAwait(async item =>
|
||||
{
|
||||
|
|
@ -448,7 +442,7 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
Quantity = item.Quantity.ToString(),
|
||||
Category = (product.IsDownload ? ItemCategoryType.Digital_goods : ItemCategoryType.Physical_goods)
|
||||
.ToString().ToUpperInvariant(),
|
||||
UnitAmount = prepareMoney(itemPrice)
|
||||
UnitAmount = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = itemPrice.ToString("0.00", CultureInfo.InvariantCulture) }
|
||||
};
|
||||
}).ToListAsync();
|
||||
|
||||
|
|
@ -466,7 +460,7 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
Name = CommonHelper.EnsureMaximumLength(attribute.Name, 127),
|
||||
Description = CommonHelper.EnsureMaximumLength($"{attribute.Name} - {attributeValue.Name}", 127),
|
||||
Quantity = 1.ToString(),
|
||||
UnitAmount = prepareMoney(attributePrice)
|
||||
UnitAmount = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = attributePrice.ToString("0.00", CultureInfo.InvariantCulture) }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -486,13 +480,13 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
purchaseUnit.AmountWithBreakdown = new AmountWithBreakdown
|
||||
{
|
||||
CurrencyCode = currency,
|
||||
Value = prepareMoney(orderTotal).Value,
|
||||
Value = orderTotal.ToString("0.00", CultureInfo.InvariantCulture),
|
||||
AmountBreakdown = new AmountBreakdown
|
||||
{
|
||||
ItemTotal = prepareMoney(itemTotal),
|
||||
TaxTotal = prepareMoney(taxTotal),
|
||||
Shipping = prepareMoney(shippingTotal),
|
||||
Discount = prepareMoney(discountTotal)
|
||||
ItemTotal = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = itemTotal.ToString("0.00", CultureInfo.InvariantCulture) },
|
||||
TaxTotal = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = taxTotal.ToString("0.00", CultureInfo.InvariantCulture) },
|
||||
Shipping = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = shippingTotal.ToString("0.00", CultureInfo.InvariantCulture) },
|
||||
Discount = new PayPalCheckoutSdk.Orders.Money { CurrencyCode = currency, Value = discountTotal.ToString("0.00", CultureInfo.InvariantCulture) }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -590,9 +584,9 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
if (!IsConfigured(settings))
|
||||
throw new NopException("Plugin not configured");
|
||||
|
||||
var request = new VoidRequest(authorizationId);
|
||||
var request = new AuthorizationsVoidRequest(authorizationId);
|
||||
|
||||
return await HandleCheckoutRequestAsync<VoidRequest, PayPalCheckoutSdk.Payments.Authorization>(settings, request);
|
||||
return await HandleCheckoutRequestAsync<AuthorizationsVoidRequest, object>(settings, request);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -622,7 +616,7 @@ namespace Nop.Plugin.Payments.PayPalCommerce.Services
|
|||
refundRequest.Amount = new PayPalCheckoutSdk.Payments.Money
|
||||
{
|
||||
CurrencyCode = currency,
|
||||
Value = amount.Value.ToString(PayPalCommerceDefaults.CurrenciesWithoutDecimals.Contains(currency.ToUpperInvariant()) ? "0" : "0.00", CultureInfo.InvariantCulture)
|
||||
Value = amount.Value.ToString("0.00", CultureInfo.InvariantCulture)
|
||||
};
|
||||
}
|
||||
var request = new CapturesRefundRequest(captureId).RequestBody(refundRequest);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"Group": "Payment methods",
|
||||
"FriendlyName": "PayPal Commerce",
|
||||
"SystemName": "Payments.PayPalCommerce",
|
||||
"Version": "1.10.2",
|
||||
"Version": "1.10",
|
||||
"SupportedVersions": [ "4.50" ],
|
||||
"Author": "nopCommerce team",
|
||||
"DisplayOrder": -1,
|
||||
|
|
|
|||
|
|
@ -767,10 +767,7 @@ namespace Nop.Plugin.Shipping.EasyPost.Services
|
|||
if (order is null)
|
||||
throw new ArgumentNullException(nameof(order));
|
||||
|
||||
if (order.ShippingRateComputationMethodSystemName != EasyPostDefaults.SystemName)
|
||||
return false;
|
||||
|
||||
//move the shipment from the customer to the order entry
|
||||
//move the shipment if from the customer to the order entry
|
||||
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
|
||||
var shipmentId = await _genericAttributeService
|
||||
.GetAttributeAsync<string>(customer, EasyPostDefaults.ShipmentIdAttribute, order.StoreId);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"Group": "Shipping rate computation",
|
||||
"FriendlyName": "EasyPost",
|
||||
"SystemName": "Shipping.EasyPost",
|
||||
"Version": "1.13",
|
||||
"Version": "1.12",
|
||||
"SupportedVersions": [ "4.50" ],
|
||||
"Author": "nopCommerce team",
|
||||
"DisplayOrder": 1,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Nop.Core;
|
||||
using Nop.Plugin.Shipping.ShipStation.Models;
|
||||
using Nop.Plugin.Shipping.ShipStation.Services;
|
||||
using Nop.Services;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Messages;
|
||||
using Nop.Web.Framework;
|
||||
using Nop.Web.Framework.Controllers;
|
||||
using Nop.Web.Framework.Mvc.Filters;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation.Controllers
|
||||
{
|
||||
public class ShipStationController : BasePluginController
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly ISettingService _settingService;
|
||||
private readonly IShipStationService _shipStationService;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly IWebHelper _webHelper;
|
||||
|
||||
public ShipStationController(ILocalizationService localizationService,
|
||||
INotificationService notificationService,
|
||||
ISettingService settingService,
|
||||
IShipStationService shipStationService,
|
||||
IStoreContext storeContext,
|
||||
IWebHelper webHelper)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_notificationService = notificationService;
|
||||
_settingService = settingService;
|
||||
_shipStationService = shipStationService;
|
||||
_storeContext = storeContext;
|
||||
_webHelper = webHelper;
|
||||
}
|
||||
|
||||
[AuthorizeAdmin]
|
||||
[Area(AreaNames.Admin)]
|
||||
public async Task<IActionResult> Configure()
|
||||
{
|
||||
//load settings for a chosen store scope
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var shipStationSettings = await _settingService.LoadSettingAsync<ShipStationSettings>(storeScope);
|
||||
|
||||
var model = new ShipStationModel
|
||||
{
|
||||
ApiKey = shipStationSettings.ApiKey,
|
||||
ApiSecret = shipStationSettings.ApiSecret,
|
||||
PackingPackageVolume = shipStationSettings.PackingPackageVolume,
|
||||
PackingType = Convert.ToInt32(shipStationSettings.PackingType),
|
||||
PackingTypeValues = await shipStationSettings.PackingType.ToSelectListAsync(),
|
||||
PassDimensions = shipStationSettings.PassDimensions,
|
||||
ActiveStoreScopeConfiguration = storeScope,
|
||||
UserName = shipStationSettings.UserName,
|
||||
Password = shipStationSettings.Password,
|
||||
WebhookURL = $"{_webHelper.GetStoreLocation()}Plugins/ShipStation/Webhook"
|
||||
};
|
||||
|
||||
if (storeScope <= 0)
|
||||
return View("~/Plugins/Shipping.ShipStation/Views/Configure.cshtml", model);
|
||||
|
||||
model.ApiKey_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.ApiKey, storeScope);
|
||||
model.ApiSecret_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.ApiSecret, storeScope);
|
||||
model.PackingPackageVolume_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.PackingPackageVolume, storeScope);
|
||||
model.PackingType_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.PackingType, storeScope);
|
||||
model.PassDimensions_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.PassDimensions, storeScope);
|
||||
model.Password_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.Password, storeScope);
|
||||
model.UserName_OverrideForStore = await _settingService.SettingExistsAsync(shipStationSettings, x => x.UserName, storeScope);
|
||||
|
||||
return View("~/Plugins/Shipping.ShipStation/Views/Configure.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[AuthorizeAdmin]
|
||||
[Area(AreaNames.Admin)]
|
||||
public async Task<IActionResult> Configure(ShipStationModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return await Configure();
|
||||
|
||||
//load settings for a chosen store scope
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var shipStationSettings = await _settingService.LoadSettingAsync<ShipStationSettings>(storeScope);
|
||||
|
||||
//save settings
|
||||
shipStationSettings.ApiKey = model.ApiKey;
|
||||
shipStationSettings.ApiSecret = model.ApiSecret;
|
||||
shipStationSettings.PackingPackageVolume = model.PackingPackageVolume;
|
||||
shipStationSettings.PackingType = (PackingType)model.PackingType;
|
||||
shipStationSettings.PassDimensions = model.PassDimensions;
|
||||
shipStationSettings.Password = model.Password;
|
||||
shipStationSettings.UserName = model.UserName;
|
||||
|
||||
/* We do not clear cache after each setting update.
|
||||
* This behavior can increase performance because cached settings will not be cleared
|
||||
* and loaded from database after each update */
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.ApiKey, model.ApiKey_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.ApiSecret, model.ApiSecret_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.PackingPackageVolume, model.PackingPackageVolume_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.PackingType, model.PackingType_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.PassDimensions, model.PassDimensions_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.Password, model.Password_OverrideForStore, storeScope, false);
|
||||
await _settingService.SaveSettingOverridablePerStoreAsync(shipStationSettings, x => x.UserName, model.UserName_OverrideForStore, storeScope, false);
|
||||
|
||||
//now clear settings cache
|
||||
await _settingService.ClearCacheAsync();
|
||||
|
||||
_notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Plugins.Saved"));
|
||||
|
||||
return await Configure();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Webhook()
|
||||
{
|
||||
var userName = _webHelper.QueryString<string>("SS-UserName");
|
||||
var password = _webHelper.QueryString<string>("SS-Password");
|
||||
|
||||
//load settings for a chosen store scope
|
||||
var storeScope = await _storeContext.GetActiveStoreScopeConfigurationAsync();
|
||||
var shipStationSettings = await _settingService.LoadSettingAsync<ShipStationSettings>(storeScope);
|
||||
|
||||
if (!userName.Equals(shipStationSettings.UserName) || !password.Equals(shipStationSettings.Password))
|
||||
return Content(string.Empty);
|
||||
|
||||
var action = _webHelper.QueryString<string>("action") ?? string.Empty;
|
||||
|
||||
if (Request.Method == WebRequestMethods.Http.Post &&
|
||||
action.Equals("shipnotify", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var orderNumber = _webHelper.QueryString<string>("order_number");
|
||||
var carrier = _webHelper.QueryString<string>("carrier");
|
||||
var service = _webHelper.QueryString<string>("service");
|
||||
var trackingNumber = _webHelper.QueryString<string>("tracking_number");
|
||||
|
||||
await _shipStationService.CreateOrUpdateShippingAsync(orderNumber, carrier, service, trackingNumber);
|
||||
|
||||
//nothing should be rendered to visitor
|
||||
return Content(string.Empty);
|
||||
}
|
||||
|
||||
if (!action.Equals("export", StringComparison.InvariantCultureIgnoreCase))
|
||||
return Content(string.Empty);
|
||||
|
||||
var startDateParam = _webHelper.QueryString<string>("start_date");
|
||||
var endDateParam = _webHelper.QueryString<string>("end_date");
|
||||
var pageIndex = _webHelper.QueryString<int>("page");
|
||||
|
||||
if (pageIndex > 0)
|
||||
pageIndex -= 1;
|
||||
|
||||
var startDate = string.IsNullOrEmpty(startDateParam) ? (DateTime?)null : DateTime.ParseExact(startDateParam, _shipStationService.DateFormat, CultureInfo.InvariantCulture);
|
||||
var endDate = string.IsNullOrEmpty(endDateParam) ? (DateTime?)null : DateTime.ParseExact(endDateParam, _shipStationService.DateFormat, CultureInfo.InvariantCulture);
|
||||
|
||||
return Content(await _shipStationService.GetXmlOrdersAsync(startDate, endDate, pageIndex, 200), "text/xml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Nop.Core.Infrastructure;
|
||||
using Nop.Plugin.Shipping.ShipStation.Services;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents object for the configuring services on application startup
|
||||
/// </summary>
|
||||
public class NopStartup : INopStartup
|
||||
{
|
||||
/// <summary>
|
||||
/// Add and configure any of the middleware
|
||||
/// </summary>
|
||||
/// <param name="services">Collection of service descriptors</param>
|
||||
/// <param name="configuration">Configuration of the application</param>
|
||||
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddScoped<IShipStationService, ShipStationService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the using of added middleware
|
||||
/// </summary>
|
||||
/// <param name="application">Builder for configuring an application's request pipeline</param>
|
||||
public void Configure(IApplicationBuilder application)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets order of this startup configuration implementation
|
||||
/// </summary>
|
||||
public int Order => 3000;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation.Models
|
||||
{
|
||||
public record ShipStationModel : BaseNopModel
|
||||
{
|
||||
public int ActiveStoreScopeConfiguration { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.ApiKey")]
|
||||
public string ApiKey { get; set; }
|
||||
public bool ApiKey_OverrideForStore { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.ApiSecret")]
|
||||
public string ApiSecret { get; set; }
|
||||
public bool ApiSecret_OverrideForStore { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.PackingType")]
|
||||
public SelectList PackingTypeValues { get; set; }
|
||||
public bool PackingType_OverrideForStore { get; set; }
|
||||
public int PackingType { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.PackingPackageVolume")]
|
||||
public int PackingPackageVolume { get; set; }
|
||||
public bool PackingPackageVolume_OverrideForStore { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.PassDimensions")]
|
||||
public bool PassDimensions { get; set; }
|
||||
public bool PassDimensions_OverrideForStore { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.UserName")]
|
||||
public string UserName { get; set; }
|
||||
public bool UserName_OverrideForStore { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.Shipping.ShipStation.Fields.Password")]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
public bool Password_OverrideForStore { get; set; }
|
||||
|
||||
public string WebhookURL { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Copyright>Copyright © Nop Solutions, Ltd</Copyright>
|
||||
<Company>Nop Solutions, Ltd</Company>
|
||||
<Authors>Nop Solutions, Ltd</Authors>
|
||||
<PackageLicenseUrl></PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://www.nopcommerce.com/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<OutputPath>..\..\Presentation\Nop.Web\Plugins\Shipping.ShipStation</OutputPath>
|
||||
<OutDir>$(OutputPath)</OutDir>
|
||||
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
|
||||
You need to set this parameter to true if your plugin has a nuget package
|
||||
to ensure that the dlls copied from the NuGet cache to the output of your project-->
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="logo.jpg" />
|
||||
<None Remove="plugin.json" />
|
||||
<None Remove="Views\Configure.cshtml" />
|
||||
<None Remove="Views\_ViewImports.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Configure.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\_ViewImports.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Presentation\Nop.Web\Nop.Web.csproj" />
|
||||
<ClearPluginAssemblies Include="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Connected Services" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This target execute after "Build" target -->
|
||||
<Target Name="NopTarget" AfterTargets="Build">
|
||||
<!-- Delete unnecessary libraries from plugins path -->
|
||||
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
Important points when developing plugins
|
||||
|
||||
|
||||
- All views (cshtml files) and web.config file should have "Build action" set to "Content" and "Copy to output directory" set to "Copy if newer"
|
||||
|
||||
- When you develop a new plugin from scratch, and when a new class library is added to the solution, open its .csproj file (a main project file) in any text editor and replace its content with the following one
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<OutputPath>..\..\Presentation\Nop.Web\Plugins\PLUGIN_OUTPUT_DIRECTORY</OutputPath>
|
||||
<OutDir>$(OutputPath)</OutDir>
|
||||
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
|
||||
You need to set this parameter to true if your plugin has a nuget package
|
||||
to ensure that the dlls copied from the NuGet cache to the output of your project-->
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClearPluginAssemblies Include="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This target execute after "Build" target -->
|
||||
<Target Name="NopTarget" AfterTargets="Build">
|
||||
<!-- Delete unnecessary libraries from plugins path -->
|
||||
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
Replace “PLUGIN_OUTPUT_DIRECTORY” in the code above with your real plugin output directory name.
|
||||
|
||||
It’s not required. But this way we can use a new ASP.NET approach to add third-party references. It was introduced in .NET Core. Furthermore, references from already referenced libraries will be loaded automatically. It’s very convenient.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a packing type
|
||||
/// </summary>
|
||||
public enum PackingType
|
||||
{
|
||||
/// <summary>
|
||||
/// Pack by dimensions
|
||||
/// </summary>
|
||||
PackByDimensions,
|
||||
|
||||
/// <summary>
|
||||
/// Pack by volume
|
||||
/// </summary>
|
||||
PackByVolume
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Nop.Web.Framework.Mvc.Routing;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
public partial class RouteProvider : IRouteProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Register routes
|
||||
/// </summary>
|
||||
/// <param name="endpointRouteBuilder">Route builder</param>
|
||||
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
|
||||
{
|
||||
//Webhook
|
||||
endpointRouteBuilder.MapControllerRoute("Plugin.Payments.ShipStation.WebhookHandler", "Plugins/ShipStation/Webhook",
|
||||
new { controller = "ShipStation", action = "Webhook" });
|
||||
}
|
||||
|
||||
public int Priority => 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
public enum ServiceType
|
||||
{
|
||||
/// <summary>
|
||||
/// All services
|
||||
/// </summary>
|
||||
All,
|
||||
|
||||
/// <summary>
|
||||
/// Domestic services
|
||||
/// </summary>
|
||||
Domestic,
|
||||
|
||||
/// <summary>
|
||||
/// International services
|
||||
/// </summary>
|
||||
International
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Services.Shipping;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation.Services
|
||||
{
|
||||
public interface IShipStationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all rates
|
||||
/// </summary>
|
||||
/// <param name="shippingOptionRequest"></param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the
|
||||
/// </returns>
|
||||
Task<IList<ShipStationServiceRate>> GetAllRatesAsync(GetShippingOptionRequest shippingOptionRequest);
|
||||
|
||||
/// <summary>
|
||||
/// Create or update shipping
|
||||
/// </summary>
|
||||
/// <param name="orderNumber">Order number</param>
|
||||
/// <param name="carrier">Carrier</param>
|
||||
/// <param name="service">Service</param>
|
||||
/// <param name="trackingNumber">Tracking number</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task CreateOrUpdateShippingAsync(string orderNumber, string carrier, string service, string trackingNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get XML view of orders to sending to the ShipStation service
|
||||
/// </summary>
|
||||
/// <param name="startDate">Created date from (UTC); null to load all records</param>
|
||||
/// <param name="endDate">Created date to (UTC); null to load all records</param>
|
||||
/// <param name="pageIndex">Page index</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the xML view of orders
|
||||
/// </returns>
|
||||
Task<string> GetXmlOrdersAsync(DateTime? startDate, DateTime? endDate, int pageIndex, int pageSize);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string that defines the required format of date time
|
||||
/// </summary>
|
||||
string DateFormat { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,669 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Newtonsoft.Json;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Directory;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Directory;
|
||||
using Nop.Services.ExportImport;
|
||||
using Nop.Services.Logging;
|
||||
using Nop.Services.Orders;
|
||||
using Nop.Services.Shipping;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation.Services
|
||||
{
|
||||
public partial class ShipStationService : IShipStationService
|
||||
{
|
||||
#region constants
|
||||
|
||||
private const string API_URL = "https://ssapi.shipstation.com/";
|
||||
private readonly CacheKey _carriersCacheKey = new("Nop.plugins.shipping.shipstation.carrierscachekey");
|
||||
private readonly CacheKey _serviceCacheKey = new("Nop.plugins.shipping.shipstation.servicecachekey.{0}");
|
||||
|
||||
private const string CONTENT_TYPE = "application/json";
|
||||
private const string DATE_FORMAT = "MM/dd/yyyy HH:mm";
|
||||
|
||||
private const string LIST_CARRIERS_CMD = "carriers";
|
||||
private const string LIST_SERVICES_CMD = "carriers/listservices?carrierCode={0}";
|
||||
private const string LIST_RATES_CMD = "shipments/getrates";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private readonly IAddressService _addressService;
|
||||
private readonly ICountryService _countryService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMeasureService _measureService;
|
||||
private readonly IOrderService _orderService;
|
||||
private readonly IProductAttributeParser _productAttributeParser;
|
||||
private readonly IProductService _productService;
|
||||
private readonly IShipmentService _shipmentService;
|
||||
private readonly IShippingService _shippingService;
|
||||
private readonly IStateProvinceService _stateProvinceService;
|
||||
private readonly IStaticCacheManager _staticCacheManager;
|
||||
private readonly IStoreContext _storeContext;
|
||||
private readonly ShipStationSettings _shipStationSettings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public ShipStationService(IAddressService addressService,
|
||||
ICountryService countryService,
|
||||
ICustomerService customerService,
|
||||
ILogger logger,
|
||||
IMeasureService measureService,
|
||||
IOrderService orderService,
|
||||
IProductAttributeParser productAttributeParser,
|
||||
IProductService productService,
|
||||
IShipmentService shipmentService,
|
||||
IShippingService shippingService,
|
||||
IStateProvinceService stateProvinceService,
|
||||
IStaticCacheManager staticCacheManager,
|
||||
IStoreContext storeContext,
|
||||
ShipStationSettings shipStationSettings)
|
||||
{
|
||||
_addressService = addressService;
|
||||
_countryService = countryService;
|
||||
_customerService = customerService;
|
||||
_logger = logger;
|
||||
_measureService = measureService;
|
||||
_orderService = orderService;
|
||||
_productAttributeParser = productAttributeParser;
|
||||
_productService = productService;
|
||||
_shipmentService = shipmentService;
|
||||
_shippingService = shippingService;
|
||||
_stateProvinceService = stateProvinceService;
|
||||
_staticCacheManager = staticCacheManager;
|
||||
_storeContext = storeContext;
|
||||
_shipStationSettings = shipStationSettings;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task<string> SendGetRequestAsync(string apiUrl)
|
||||
{
|
||||
using var handler = new HttpClientHandler { Credentials = new NetworkCredential(_shipStationSettings.ApiKey, _shipStationSettings.ApiSecret) };
|
||||
using var client = new HttpClient(handler);
|
||||
using var rs = await client.GetStreamAsync(apiUrl);
|
||||
|
||||
if (rs == null) return string.Empty;
|
||||
using var sr = new StreamReader(rs);
|
||||
|
||||
return await sr.ReadToEndAsync();
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
private async Task<int> ConvertFromPrimaryMeasureDimensionAsync(decimal quantity, MeasureDimension usedMeasureDimension)
|
||||
{
|
||||
return Convert.ToInt32(Math.Ceiling(await _measureService.ConvertFromPrimaryMeasureDimensionAsync(quantity, usedMeasureDimension)));
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task<bool> TryGetError(string data)
|
||||
{
|
||||
var flag = false;
|
||||
try
|
||||
{
|
||||
var rez = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
|
||||
|
||||
if (rez.ContainsKey("message"))
|
||||
{
|
||||
flag = true;
|
||||
|
||||
await _logger.ErrorAsync(rez["message"]);
|
||||
}
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
{
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task<IList<ShipStationServiceRate>> GetRatesAsync(GetShippingOptionRequest getShippingOptionRequest, string carrierCode)
|
||||
{
|
||||
var usedWeight = await _measureService.GetMeasureWeightBySystemKeywordAsync(Weight.Units);
|
||||
if (usedWeight == null)
|
||||
throw new NopException("ShipStatio shipping service. Could not load \"{0}\" measure weight", Weight.Units);
|
||||
|
||||
var usedMeasureDimension = await _measureService.GetMeasureDimensionBySystemKeywordAsync(Dimensions.Units);
|
||||
if (usedMeasureDimension == null)
|
||||
throw new NopException("ShipStatio shipping service. Could not load \"{0}\" measure dimension", Dimensions.Units);
|
||||
|
||||
var weight = Convert.ToInt32(Math.Ceiling(await _measureService.ConvertFromPrimaryMeasureWeightAsync(await _shippingService.GetTotalWeightAsync(getShippingOptionRequest), usedWeight)));
|
||||
|
||||
var postData = new RatesRequest
|
||||
{
|
||||
CarrierCode = carrierCode,
|
||||
FromPostalCode = getShippingOptionRequest.ZipPostalCodeFrom ?? getShippingOptionRequest.ShippingAddress.ZipPostalCode,
|
||||
ToState = (await _stateProvinceService.GetStateProvinceByAddressAsync(getShippingOptionRequest.ShippingAddress)).Abbreviation,
|
||||
ToCountry = (await _countryService.GetCountryByAddressAsync(getShippingOptionRequest.ShippingAddress)).TwoLetterIsoCode,
|
||||
ToPostalCode = getShippingOptionRequest.ShippingAddress.ZipPostalCode,
|
||||
ToCity = getShippingOptionRequest.ShippingAddress.City,
|
||||
Weight = new Weight { Value = weight }
|
||||
};
|
||||
|
||||
if (_shipStationSettings.PassDimensions)
|
||||
{
|
||||
int length, height, width;
|
||||
|
||||
decimal lengthTmp, widthTmp, heightTmp;
|
||||
|
||||
switch (_shipStationSettings.PackingType)
|
||||
{
|
||||
case PackingType.PackByDimensions:
|
||||
(widthTmp, lengthTmp, heightTmp) = await _shippingService.GetDimensionsAsync(getShippingOptionRequest.Items);
|
||||
|
||||
length = await ConvertFromPrimaryMeasureDimensionAsync(lengthTmp, usedMeasureDimension);
|
||||
height = await ConvertFromPrimaryMeasureDimensionAsync(heightTmp, usedMeasureDimension);
|
||||
width = await ConvertFromPrimaryMeasureDimensionAsync(widthTmp, usedMeasureDimension);
|
||||
break;
|
||||
case PackingType.PackByVolume:
|
||||
if (getShippingOptionRequest.Items.Count == 1 &&
|
||||
getShippingOptionRequest.Items[0].GetQuantity() == 1)
|
||||
{
|
||||
var sci = getShippingOptionRequest.Items[0].ShoppingCartItem;
|
||||
var product = getShippingOptionRequest.Items[0].Product;
|
||||
|
||||
(widthTmp, lengthTmp, heightTmp) = await _shippingService.GetDimensionsAsync(new List<GetShippingOptionRequest.PackageItem>
|
||||
{
|
||||
new GetShippingOptionRequest.PackageItem(sci, product, 1)
|
||||
});
|
||||
|
||||
length = await ConvertFromPrimaryMeasureDimensionAsync(lengthTmp, usedMeasureDimension);
|
||||
height = await ConvertFromPrimaryMeasureDimensionAsync(lengthTmp, usedMeasureDimension);
|
||||
width = await ConvertFromPrimaryMeasureDimensionAsync(widthTmp, usedMeasureDimension);
|
||||
}
|
||||
else
|
||||
{
|
||||
decimal totalVolume = 0;
|
||||
foreach (var item in getShippingOptionRequest.Items)
|
||||
{
|
||||
var sci = item.ShoppingCartItem;
|
||||
var product = item.Product;
|
||||
|
||||
(widthTmp, lengthTmp, heightTmp) = await _shippingService.GetDimensionsAsync(new List<GetShippingOptionRequest.PackageItem>
|
||||
{
|
||||
new GetShippingOptionRequest.PackageItem(sci, product, 1)
|
||||
});
|
||||
|
||||
var productLength = await ConvertFromPrimaryMeasureDimensionAsync(lengthTmp, usedMeasureDimension);
|
||||
var productHeight = await ConvertFromPrimaryMeasureDimensionAsync(heightTmp, usedMeasureDimension);
|
||||
var productWidth = await ConvertFromPrimaryMeasureDimensionAsync(widthTmp, usedMeasureDimension);
|
||||
totalVolume += item.GetQuantity() * (productHeight * productWidth * productLength);
|
||||
}
|
||||
|
||||
int dimension;
|
||||
if (totalVolume == 0)
|
||||
{
|
||||
dimension = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cubic inches
|
||||
var packageVolume = _shipStationSettings.PackingPackageVolume;
|
||||
if (packageVolume <= 0)
|
||||
packageVolume = 5184;
|
||||
|
||||
// cube root (floor)
|
||||
dimension = Convert.ToInt32(Math.Floor(Math.Pow(Convert.ToDouble(packageVolume),
|
||||
1.0 / 3.0)));
|
||||
}
|
||||
|
||||
length = width = height = dimension;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
length = height = width = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (length < 1)
|
||||
length = 1;
|
||||
if (height < 1)
|
||||
height = 1;
|
||||
if (width < 1)
|
||||
width = 1;
|
||||
|
||||
postData.Dimensions = new Dimensions
|
||||
{
|
||||
Length = length,
|
||||
Height = height,
|
||||
Width = width
|
||||
};
|
||||
}
|
||||
|
||||
using var handler = new HttpClientHandler { Credentials = new NetworkCredential(_shipStationSettings.ApiKey, _shipStationSettings.ApiSecret) };
|
||||
using var client = new HttpClient(handler);
|
||||
|
||||
client.DefaultRequestHeaders.Add("Content-Type", CONTENT_TYPE);
|
||||
var responseData = await client.PostAsync($"{API_URL}{LIST_RATES_CMD}", new StringContent(JsonConvert.SerializeObject(postData)));
|
||||
var data = await responseData.Content.ReadAsStringAsync();
|
||||
|
||||
return (await TryGetError(data)) ? new List<ShipStationServiceRate>() : JsonConvert.DeserializeObject<List<ShipStationServiceRate>>(data);
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task<IList<Carrier>> GetCarriersAsync()
|
||||
{
|
||||
var rez = await _staticCacheManager.GetAsync(_staticCacheManager.PrepareKeyForShortTermCache(_carriersCacheKey), async () =>
|
||||
{
|
||||
var data = await SendGetRequestAsync($"{API_URL}{LIST_CARRIERS_CMD}");
|
||||
|
||||
return (await TryGetError(data)) ? new List<Carrier>() : JsonConvert.DeserializeObject<List<Carrier>>(data);
|
||||
});
|
||||
|
||||
if (!rez.Any())
|
||||
await _staticCacheManager.RemoveAsync(_carriersCacheKey);
|
||||
|
||||
return rez;
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task<IList<Service>> GetServicesAsync()
|
||||
{
|
||||
var services = await (await GetCarriersAsync()).SelectManyAwait(async carrier =>
|
||||
{
|
||||
var cacheKey = _staticCacheManager.PrepareKeyForShortTermCache(_serviceCacheKey, carrier.Code);
|
||||
|
||||
var data = await _staticCacheManager.GetAsync(cacheKey, async () => await SendGetRequestAsync(string.Format($"{API_URL}{LIST_SERVICES_CMD}", carrier.Code)));
|
||||
|
||||
if (!data.Any())
|
||||
await _staticCacheManager.RemoveAsync(cacheKey);
|
||||
|
||||
var serviceList = JsonConvert.DeserializeObject<IList<Service>>(data);
|
||||
|
||||
return serviceList;
|
||||
}).ToListAsync();
|
||||
|
||||
return services.ToList();
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task WriteAddressToXmlAsync(XmlWriter writer, bool isBillingAddress, Address address)
|
||||
{
|
||||
await writer.WriteElementStringAsync("Name", $"{address.FirstName} {address.LastName}");
|
||||
|
||||
await writer.WriteElementStringAsync("Company", address.Company);
|
||||
await writer.WriteElementStringAsync("Phone", address.PhoneNumber);
|
||||
|
||||
if (isBillingAddress)
|
||||
return;
|
||||
|
||||
await writer.WriteElementStringAsync("Address1", address.Address1);
|
||||
await writer.WriteElementStringAsync("Address2", address.Address2);
|
||||
await writer.WriteElementStringAsync("City", address.City);
|
||||
await writer.WriteElementStringAsync("State", (await _stateProvinceService.GetStateProvinceByAddressAsync(address))?.Name ?? string.Empty);
|
||||
await writer.WriteElementStringAsync("PostalCode", address.ZipPostalCode);
|
||||
await writer.WriteElementStringAsync("Country", (await _countryService.GetCountryByAddressAsync(address)).TwoLetterIsoCode);
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task WriteOrderItemsToXmlAsync(XmlWriter writer, ICollection<OrderItem> orderItems)
|
||||
{
|
||||
await writer.WriteStartElementAsync("Items");
|
||||
|
||||
foreach (var orderItem in orderItems)
|
||||
{
|
||||
var product = await _productService.GetProductByIdAsync(orderItem.ProductId);
|
||||
var order = await _orderService.GetOrderByIdAsync(orderItem.OrderId);
|
||||
|
||||
//is shippable
|
||||
if (!product.IsShipEnabled)
|
||||
continue;
|
||||
|
||||
await writer.WriteStartElementAsync("Item");
|
||||
|
||||
var sku = product.Sku;
|
||||
|
||||
if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes)
|
||||
{
|
||||
var attributesXml = orderItem.AttributesXml;
|
||||
|
||||
if (!string.IsNullOrEmpty(attributesXml) && product.ManageInventoryMethod ==
|
||||
ManageInventoryMethod.ManageStockByAttributes)
|
||||
{
|
||||
var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, attributesXml);
|
||||
if (combination != null && !string.IsNullOrEmpty(combination.Sku))
|
||||
sku = combination.Sku;
|
||||
}
|
||||
}
|
||||
|
||||
await writer.WriteElementStringAsync("SKU", string.IsNullOrEmpty(sku) ? product.Id.ToString() : sku);
|
||||
await writer.WriteElementStringAsync("Name", product.Name);
|
||||
await writer.WriteElementStringAsync("Quantity", orderItem.Quantity.ToString());
|
||||
await writer.WriteElementStringAsync("UnitPrice", (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax ? orderItem.UnitPriceInclTax : orderItem.UnitPriceExclTax).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.FlushAsync();
|
||||
}
|
||||
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.FlushAsync();
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task WriteCustomerToXmlAsync(XmlWriter writer, Order order, Core.Domain.Customers.Customer customer)
|
||||
{
|
||||
await writer.WriteStartElementAsync("Customer");
|
||||
|
||||
await writer.WriteElementStringAsync("CustomerCode", customer.Email);
|
||||
await writer.WriteStartElementAsync("BillTo");
|
||||
await WriteAddressToXmlAsync(writer, true, await _addressService.GetAddressByIdAsync(order.BillingAddressId));
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.WriteStartElementAsync("ShipTo");
|
||||
await WriteAddressToXmlAsync(writer, false, await _addressService.GetAddressByIdAsync(order.ShippingAddressId ?? order.BillingAddressId));
|
||||
await writer.WriteEndElementAsync();
|
||||
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.FlushAsync();
|
||||
}
|
||||
|
||||
protected virtual string GetOrderStatus(Order order)
|
||||
{
|
||||
return order.OrderStatus switch
|
||||
{
|
||||
OrderStatus.Pending => "unpaid",
|
||||
OrderStatus.Processing => "paid",
|
||||
OrderStatus.Complete => "shipped",
|
||||
OrderStatus.Cancelled => "cancelled",
|
||||
_ => "on_hold",
|
||||
};
|
||||
}
|
||||
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual async Task WriteOrderToXmlAsync(XmlWriter writer, Order order)
|
||||
{
|
||||
await writer.WriteStartElementAsync("Order");
|
||||
await writer.WriteElementStringAsync("OrderID", order.Id.ToString());
|
||||
await writer.WriteElementStringAsync("OrderNumber", order.OrderGuid.ToString());
|
||||
await writer.WriteElementStringAsync("OrderDate", order.CreatedOnUtc.ToString(DATE_FORMAT));
|
||||
await writer.WriteElementStringAsync("OrderStatus", GetOrderStatus(order));
|
||||
await writer.WriteElementStringAsync("LastModified", DateTime.Now.ToString(DATE_FORMAT));
|
||||
await writer.WriteElementStringAsync("OrderTotal", order.OrderTotal.ToString(CultureInfo.InvariantCulture));
|
||||
await writer.WriteElementStringAsync("ShippingAmount", (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax ? order.OrderShippingInclTax : order.OrderShippingExclTax).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
await WriteCustomerToXmlAsync(writer, order, await _customerService.GetCustomerByIdAsync(order.CustomerId));
|
||||
await WriteOrderItemsToXmlAsync(writer, await _orderService.GetOrderItemsAsync(order.Id));
|
||||
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.FlushAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets all rates
|
||||
/// </summary>
|
||||
/// <param name="shippingOptionRequest"></param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the
|
||||
/// </returns>
|
||||
public virtual async Task<IList<ShipStationServiceRate>> GetAllRatesAsync(GetShippingOptionRequest shippingOptionRequest)
|
||||
{
|
||||
var services = await GetServicesAsync();
|
||||
|
||||
var carrierFilter = services.Select(s => s.CarrierCode).Distinct().ToList();
|
||||
var serviceFilter = services.Select(s => s.Code).Distinct().ToList();
|
||||
var carriers = (await GetCarriersAsync()).Where(c => carrierFilter.Contains(c.Code));
|
||||
|
||||
return await carriers.SelectManyAwait(async carrier =>
|
||||
(await GetRatesAsync(shippingOptionRequest, carrier.Code)).Where(r => serviceFilter.Contains(r.ServiceCode))).ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create or update shipping
|
||||
/// </summary>
|
||||
/// <param name="orderNumber">Order number</param>
|
||||
/// <param name="carrier">Carrier</param>
|
||||
/// <param name="service">Service</param>
|
||||
/// <param name="trackingNumber">Tracking number</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task CreateOrUpdateShippingAsync(string orderNumber, string carrier, string service, string trackingNumber)
|
||||
{
|
||||
try
|
||||
{
|
||||
var order = await _orderService.GetOrderByGuidAsync(Guid.Parse(orderNumber));
|
||||
|
||||
if (order == null)
|
||||
return;
|
||||
|
||||
var shipments = await _shipmentService.GetShipmentsByOrderIdAsync(order.Id);
|
||||
|
||||
if (!shipments.Any())
|
||||
{
|
||||
var shipment = new Shipment
|
||||
{
|
||||
CreatedOnUtc = DateTime.UtcNow,
|
||||
ShippedDateUtc = DateTime.UtcNow,
|
||||
OrderId = order.Id,
|
||||
TrackingNumber = trackingNumber
|
||||
};
|
||||
|
||||
decimal totalWeight = 0;
|
||||
|
||||
await _shipmentService.InsertShipmentAsync(shipment);
|
||||
|
||||
foreach (var orderItem in await _orderService.GetOrderItemsAsync(order.Id))
|
||||
{
|
||||
var product = await _productService.GetProductByIdAsync(orderItem.ProductId);
|
||||
|
||||
//is shippable
|
||||
if (!product.IsShipEnabled)
|
||||
continue;
|
||||
|
||||
//ensure that this product can be shipped (have at least one item to ship)
|
||||
var maxQtyToAdd = await _orderService.GetTotalNumberOfItemsCanBeAddedToShipmentAsync(orderItem);
|
||||
if (maxQtyToAdd <= 0)
|
||||
continue;
|
||||
|
||||
var warehouseId = product.WarehouseId;
|
||||
|
||||
//ok. we have at least one item. let's create a shipment (if it does not exist)
|
||||
|
||||
var orderItemTotalWeight = orderItem.ItemWeight * orderItem.Quantity;
|
||||
if (orderItemTotalWeight.HasValue)
|
||||
totalWeight += orderItemTotalWeight.Value;
|
||||
|
||||
//create a shipment item
|
||||
var shipmentItem = new ShipmentItem
|
||||
{
|
||||
OrderItemId = orderItem.Id,
|
||||
Quantity = orderItem.Quantity,
|
||||
WarehouseId = warehouseId,
|
||||
ShipmentId = shipment.Id
|
||||
};
|
||||
|
||||
await _shipmentService.InsertShipmentItemAsync(shipmentItem);
|
||||
}
|
||||
|
||||
shipment.TotalWeight = totalWeight;
|
||||
|
||||
await _shipmentService.UpdateShipmentAsync(shipment);
|
||||
}
|
||||
else
|
||||
{
|
||||
var shipment = shipments.FirstOrDefault();
|
||||
|
||||
if (shipment == null)
|
||||
return;
|
||||
|
||||
shipment.TrackingNumber = trackingNumber;
|
||||
|
||||
await _shipmentService.UpdateShipmentAsync(shipment);
|
||||
}
|
||||
|
||||
order.ShippingStatus = ShippingStatus.Shipped;
|
||||
order.ShippingMethod = string.IsNullOrEmpty(service) ? carrier : service;
|
||||
|
||||
await _orderService.UpdateOrderAsync(order);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _logger.ErrorAsync(e.Message, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get XML view of orders to sending to the ShipStation service
|
||||
/// </summary>
|
||||
/// <param name="startDate">Created date from (UTC); null to load all records</param>
|
||||
/// <param name="endDate">Created date to (UTC); null to load all records</param>
|
||||
/// <param name="pageIndex">Page index</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the xML view of orders
|
||||
/// </returns>
|
||||
public async Task<string> GetXmlOrdersAsync(DateTime? startDate, DateTime? endDate, int pageIndex, int pageSize)
|
||||
{
|
||||
string xml;
|
||||
|
||||
var settings = new XmlWriterSettings
|
||||
{
|
||||
Async = true,
|
||||
Encoding = Encoding.UTF8,
|
||||
Indent = true,
|
||||
ConformanceLevel = ConformanceLevel.Auto
|
||||
};
|
||||
|
||||
await using var stream = new MemoryStream();
|
||||
await using var writer = XmlWriter.Create(stream, settings);
|
||||
|
||||
await writer.WriteStartDocumentAsync();
|
||||
await writer.WriteStartElementAsync("Orders");
|
||||
|
||||
var store = await _storeContext.GetCurrentStoreAsync();
|
||||
|
||||
foreach (var order in await _orderService.SearchOrdersAsync(createdFromUtc: startDate, createdToUtc: endDate, storeId: store.Id, pageIndex: pageIndex, pageSize: 200))
|
||||
{
|
||||
await WriteOrderToXmlAsync(writer, order);
|
||||
}
|
||||
|
||||
await writer.WriteEndElementAsync();
|
||||
await writer.WriteEndDocumentAsync();
|
||||
await writer.FlushAsync();
|
||||
|
||||
xml = Encoding.UTF8.GetString(stream.ToArray());
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Date format
|
||||
/// </summary>
|
||||
public string DateFormat => DATE_FORMAT;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested classes
|
||||
|
||||
protected class Carrier
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
}
|
||||
|
||||
protected class Service : IEqualityComparer<Service>
|
||||
{
|
||||
public string CarrierCode { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool Domestic { get; set; }
|
||||
|
||||
public bool International { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified objects are equal
|
||||
/// </summary>
|
||||
/// <param name="first">The first object of type T to compare</param>
|
||||
/// <param name="second">The second object of type T to compare</param>
|
||||
/// <returns>true if the specified objects are equal; otherwise, false</returns>
|
||||
public bool Equals(Service first, Service second)
|
||||
{
|
||||
if (first == null && second == null)
|
||||
return true;
|
||||
|
||||
if (first == null)
|
||||
return false;
|
||||
|
||||
return first.Code.Equals(second?.Code);
|
||||
}
|
||||
|
||||
public int GetHashCode(Service obj)
|
||||
{
|
||||
return Code.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
protected class RatesRequest
|
||||
{
|
||||
public string CarrierCode { get; set; }
|
||||
|
||||
public string FromPostalCode { get; set; }
|
||||
|
||||
public string ToState { get; set; }
|
||||
|
||||
public string ToCountry { get; set; }
|
||||
|
||||
public string ToPostalCode { get; set; }
|
||||
|
||||
public string ToCity { get; set; }
|
||||
|
||||
public Weight Weight { get; set; }
|
||||
|
||||
public Dimensions Dimensions { get; set; }
|
||||
}
|
||||
|
||||
protected class Weight
|
||||
{
|
||||
public static string Units => "ounce";
|
||||
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
protected class Dimensions
|
||||
{
|
||||
public static string Units => "inches";
|
||||
|
||||
public int Length { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Plugin.Shipping.ShipStation.Services;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Plugins;
|
||||
using Nop.Services.Shipping;
|
||||
using Nop.Services.Shipping.Tracking;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed rate or by weight shipping computation method
|
||||
/// </summary>
|
||||
public class ShipStationComputationMethod : BasePlugin, IShippingRateComputationMethod, IMiscPlugin
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ISettingService _settingService;
|
||||
private readonly IShipStationService _shipStationService;
|
||||
private readonly IWebHelper _webHelper;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public ShipStationComputationMethod(ILocalizationService localizationService,
|
||||
ISettingService settingService,
|
||||
IShipStationService shipStationService,
|
||||
IWebHelper webHelper)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_settingService = settingService;
|
||||
_shipStationService = shipStationService;
|
||||
_webHelper = webHelper;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets available shipping options
|
||||
/// </summary>
|
||||
/// <param name="getShippingOptionRequest">A request for getting shipping options</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the represents a response of getting shipping rate options
|
||||
/// </returns>
|
||||
public async Task<GetShippingOptionResponse> GetShippingOptionsAsync(GetShippingOptionRequest getShippingOptionRequest)
|
||||
{
|
||||
if (getShippingOptionRequest == null)
|
||||
throw new ArgumentNullException(nameof(getShippingOptionRequest));
|
||||
|
||||
var response = new GetShippingOptionResponse();
|
||||
|
||||
if (getShippingOptionRequest.Items == null)
|
||||
response.AddError("No shipment items");
|
||||
|
||||
if (getShippingOptionRequest.ShippingAddress == null)
|
||||
response.AddError("Shipping address is not set");
|
||||
|
||||
if ((getShippingOptionRequest.ShippingAddress?.CountryId ?? 0) == 0)
|
||||
response.AddError("Shipping country is not set");
|
||||
|
||||
if (!response.Success)
|
||||
return response;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var rate in await _shipStationService.GetAllRatesAsync(getShippingOptionRequest))
|
||||
{
|
||||
response.ShippingOptions.Add(new ShippingOption
|
||||
{
|
||||
Description = rate.ServiceCode,
|
||||
Name = rate.ServiceName,
|
||||
Rate = rate.TotalCost
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
response.Errors.Add(e.Message);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets fixed shipping rate (if shipping rate computation method allows it and the rate can be calculated before checkout).
|
||||
/// </summary>
|
||||
/// <param name="getShippingOptionRequest">A request for getting shipping options</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the fixed shipping rate; or null in case there's no fixed shipping rate
|
||||
/// </returns>
|
||||
public Task<decimal?> GetFixedRateAsync(GetShippingOptionRequest getShippingOptionRequest)
|
||||
{
|
||||
if (getShippingOptionRequest == null)
|
||||
throw new ArgumentNullException(nameof(getShippingOptionRequest));
|
||||
|
||||
return Task.FromResult<decimal?>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get associated shipment tracker
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the shipment tracker
|
||||
/// </returns>
|
||||
public Task<IShipmentTracker> GetShipmentTrackerAsync()
|
||||
{
|
||||
return Task.FromResult<IShipmentTracker>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a configuration page URL
|
||||
/// </summary>
|
||||
public override string GetConfigurationPageUrl()
|
||||
{
|
||||
return $"{_webHelper.GetStoreLocation()}Admin/ShipStation/Configure";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Install plugin
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public override async Task InstallAsync()
|
||||
{
|
||||
//settings
|
||||
var settings = new ShipStationSettings
|
||||
{
|
||||
PackingPackageVolume = 5184
|
||||
};
|
||||
await _settingService.SaveSettingAsync(settings);
|
||||
|
||||
//locales
|
||||
await _localizationService.AddOrUpdateLocaleResourceAsync(new Dictionary<string, string>
|
||||
{
|
||||
["Enums.Nop.Plugin.Shipping.ShipStation.PackingType.PackByDimensions"] = "Pack by dimensions",
|
||||
["Enums.Nop.Plugin.Shipping.ShipStation.PackingType.PackByVolume"] = "Pack by volume",
|
||||
["Plugins.Shipping.ShipStation.Fields.ApiKey.Hint"] = "Specify ShipStation API key.",
|
||||
["Plugins.Shipping.ShipStation.Fields.ApiKey"] = "API key",
|
||||
["Plugins.Shipping.ShipStation.Fields.ApiSecret.Hint"] = "Specify ShipStation API secret.",
|
||||
["Plugins.Shipping.ShipStation.Fields.ApiSecret"] = "API secret",
|
||||
["Plugins.Shipping.ShipStation.Fields.PackingPackageVolume.Hint"] = "Enter your package volume.",
|
||||
["Plugins.Shipping.ShipStation.Fields.PackingPackageVolume"] = "Package volume",
|
||||
["Plugins.Shipping.ShipStation.Fields.PackingType.Hint"] = "Choose preferred packing type.",
|
||||
["Plugins.Shipping.ShipStation.Fields.PackingType"] = "Packing type",
|
||||
["Plugins.Shipping.ShipStation.Fields.Password.Hint"] = "Specify ShipStation password",
|
||||
["Plugins.Shipping.ShipStation.Fields.Password"] = "Password",
|
||||
["Plugins.Shipping.ShipStation.Fields.PassDimensions.Hint"] = "Check if need send dimensions to the ShipStation server",
|
||||
["Plugins.Shipping.ShipStation.Fields.PassDimensions"] = "Pass dimensions",
|
||||
["Plugins.Shipping.ShipStation.Fields.UserName"] = "User name",
|
||||
["Plugins.Shipping.ShipStation.Fields.UserName.Hint"] = "Specify ShipStation user name"
|
||||
});
|
||||
|
||||
await base.InstallAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninstall plugin
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public override async Task UninstallAsync()
|
||||
{
|
||||
//settings
|
||||
await _settingService.DeleteSettingAsync<ShipStationSettings>();
|
||||
|
||||
//locales
|
||||
await _localizationService.DeleteLocaleResourcesAsync("Enums.Nop.Plugin.Shipping.ShipStation");
|
||||
await _localizationService.DeleteLocaleResourcesAsync("Plugins.Shipping.ShipStation");
|
||||
|
||||
await base.UninstallAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
public class ShipStationServiceRate
|
||||
{
|
||||
/// <summary>
|
||||
/// Service name
|
||||
/// </summary>
|
||||
public string ServiceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Service code
|
||||
/// </summary>
|
||||
public string ServiceCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Service code
|
||||
/// </summary>
|
||||
public string ShipmentCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Other cost
|
||||
/// </summary>
|
||||
public string OtherCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total cost
|
||||
/// </summary>
|
||||
public decimal TotalCost => Convert.ToDecimal(ShipmentCost, new CultureInfo("en-US")) + Convert.ToDecimal(OtherCost, new CultureInfo("en-US"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Plugin.Shipping.ShipStation
|
||||
{
|
||||
public class ShipStationSettings : ISettings
|
||||
{
|
||||
/// <summary>
|
||||
/// API key
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API secret
|
||||
/// </summary>
|
||||
public string ApiSecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if need pass dimensions to the ShipStation server
|
||||
/// </summary>
|
||||
public bool PassDimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Packing type
|
||||
/// </summary>
|
||||
public PackingType PackingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Package volume
|
||||
/// </summary>
|
||||
public int PackingPackageVolume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ShipStation user name
|
||||
/// </summary>
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ShipStation password
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
@{
|
||||
Layout = "_ConfigurePlugin";
|
||||
}
|
||||
|
||||
@using Nop.Plugin.Shipping.ShipStation
|
||||
@model Nop.Plugin.Shipping.ShipStation.Models.ShipStationModel
|
||||
|
||||
@await Component.InvokeAsync("StoreScopeConfiguration")
|
||||
|
||||
<form asp-controller="ShipStation" asp-action="Configure" method="post">
|
||||
<div class="cards-group">
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
|
||||
<h3>Setup Instructions</h3>
|
||||
<ul>
|
||||
<li>Register or login on the <strong><a href="https://www.shipstation.com/?ref=partner-nopcommerce&utm_campaign=partner-referrals&utm_source=nopcommerce&utm_medium=partner-referral" target="_blank">ShipStation</a></strong> site</li>
|
||||
<li>Go to the "Settings » API Settings" page and get the <strong>API Key</strong> and the <strong>API Secret</strong> and copy them on the plugin settings</li>
|
||||
<li>Select <strong>Selling Channels</strong> from the left-hand sidebar, then choose <strong>Store Setup</strong>.</li>
|
||||
<li>Click <strong>+ Connect a Store or Marketplace</strong>.</li>
|
||||
<li>Choose the <strong>Custom Store</strong> option</li>
|
||||
<li>Enter the "@Model.WebhookURL" to the <strong>URL to Custom XML Page</strong> setting</li>
|
||||
<li>Create a <strong>Username</strong> and <strong>Password</strong>, enter them into the settings forms (the ShipStation form and the nopCommerce form). <em style="color: red">Do not use the ShipStation or nopCommerce user credentials for this</em>.</li>
|
||||
<li>Don't change the <strong>Statuses</strong> section</li>
|
||||
<li>Save the nopCommerce settings by pressing the <strong>Save</strong> button.</li>
|
||||
<li>On the ShipStation form press Test your connection using the <strong>Test Connection</strong> button</li>
|
||||
<li>Save the settings using the <strong>Connect</strong> button</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="ApiKey_OverrideForStore" asp-input="ApiKey" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="ApiKey" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="ApiKey" />
|
||||
<span asp-validation-for="ApiKey"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="ApiSecret_OverrideForStore" asp-input="ApiSecret" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="ApiSecret" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="ApiSecret" />
|
||||
<span asp-validation-for="ApiSecret"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="UserName_OverrideForStore" asp-input="UserName" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="UserName" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="UserName" />
|
||||
<span asp-validation-for="UserName"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="Password_OverrideForStore" asp-input="Password" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="Password" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="Password" html-attributes="@(new { value = Model.Password })" />
|
||||
<span asp-validation-for="Password"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="PassDimensions_OverrideForStore" asp-input="PassDimensions" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="PassDimensions" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="PassDimensions" />
|
||||
<span asp-validation-for="PassDimensions"></span>
|
||||
</div>
|
||||
</div>
|
||||
<nop-nested-setting asp-for="PassDimensions" disable-auto-generation="true">
|
||||
<div class="form-group row" id="pnlPackingType">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="PackingType_OverrideForStore" asp-input="PackingType" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="PackingTypeValues" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-select asp-for="PackingType" asp-items="Model.PackingTypeValues" />
|
||||
</div>
|
||||
</div>
|
||||
<nop-nested-setting asp-for="PackingTypeValues" disable-auto-generation="true">
|
||||
<div class="form-group row" id="pnlPackingPackageVolume">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="PackingPackageVolume_OverrideForStore" asp-input="PackingPackageVolume" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="PackingPackageVolume" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="PackingPackageVolume" />
|
||||
<span asp-validation-for="PackingPackageVolume"></span>
|
||||
</div>
|
||||
</div>
|
||||
</nop-nested-setting>
|
||||
</nop-nested-setting>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<button type="submit" name="save" class="btn btn-primary">@T("Admin.Common.Save")</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
$("#PackingType").change(togglePackingType);
|
||||
|
||||
$(@Html.IdFor(model => model.PassDimensions)).change(togglePassDimensions);
|
||||
|
||||
togglePassDimensions();
|
||||
});
|
||||
|
||||
function togglePackingType() {
|
||||
var selectedPackingTypeId = $("#PackingType").val();
|
||||
if (selectedPackingTypeId == @(((int) PackingType.PackByDimensions).ToString())) {
|
||||
$('#pnlPackingPackageVolume').hideElement();
|
||||
} else if (selectedPackingTypeId == @(((int) PackingType.PackByVolume).ToString())) {
|
||||
$('#pnlPackingPackageVolume').showElement();
|
||||
}
|
||||
}
|
||||
|
||||
function togglePassDimensions() {
|
||||
togglePackingType();
|
||||
if ($(@Html.IdFor(model => model.PassDimensions)).is(':checked')) {
|
||||
$('#pnlPackingType').showElement();
|
||||
} else {
|
||||
$('#pnlPackingType').hideElement();
|
||||
$('#pnlPackingPackageVolume').hideElement();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage<TModel>
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Nop.Web.Framework
|
||||
|
||||
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
@using Nop.Web.Framework.UI
|
||||
@using Nop.Web.Framework.Extensions
|
||||
@using System.Text.Encodings.Web
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"Group": "Shipping rate computation",
|
||||
"FriendlyName": "ShipStation",
|
||||
"SystemName": "Shipping.ShipStation",
|
||||
"Version": "1.23",
|
||||
"SupportedVersions": [ "4.50" ],
|
||||
"Author": "nopCommerce team",
|
||||
"DisplayOrder": 1,
|
||||
"FileName": "Nop.Plugin.Shipping.ShipStation.dll",
|
||||
"Description": "This plugin offers shipping rates with ShipStation"
|
||||
}
|
||||
|
|
@ -517,7 +517,7 @@ namespace Nop.Plugin.Widgets.FacebookPixel.Services
|
|||
return await HandleFunctionAsync(async () =>
|
||||
{
|
||||
//get the enabled configurations
|
||||
var store = await _storeContext.GetCurrentStoreAsync();
|
||||
var store = _storeContext.GetCurrentStoreAsync();
|
||||
var configurations = await (await GetConfigurationsAsync(store.Id)).WhereAwait(async configuration =>
|
||||
{
|
||||
if (!configuration.Enabled)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"Group": "Widgets",
|
||||
"FriendlyName": "Facebook Pixel",
|
||||
"SystemName": "Widgets.FacebookPixel",
|
||||
"Version": "1.13.1",
|
||||
"Version": "1.13",
|
||||
"SupportedVersions": [ "4.50" ],
|
||||
"Author": "nopCommerce team",
|
||||
"DisplayOrder": 1,
|
||||
|
|
|
|||
|
|
@ -63,11 +63,7 @@ namespace Nop.Web.Framework.Mvc.Routing
|
|||
|
||||
var uri = new Uri(isLocalUrl ? $"{_webHelper.GetStoreLocation().TrimEnd('/')}{url}" : url, UriKind.Absolute);
|
||||
|
||||
//Allowlist redirect URI schemes to http and https
|
||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||
result.Url = isLocalUrl ? uri.PathAndQuery : $"{uri.GetLeftPart(UriPartial.Query)}{uri.Fragment}";
|
||||
else
|
||||
result.Url = urlHelper.RouteUrl("Homepage");
|
||||
result.Url = isLocalUrl ? uri.PathAndQuery : $"{uri.GetLeftPart(UriPartial.Query)}{uri.Fragment}";
|
||||
}
|
||||
|
||||
return base.ExecuteAsync(context, result);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ namespace Nop.Web.Framework.TagHelpers.Admin
|
|||
throw new ArgumentNullException(nameof(output));
|
||||
|
||||
var parentSettingName = For.Name;
|
||||
var jsConsistentParentSettingName = parentSettingName.Replace('.', '_');
|
||||
|
||||
var random = CommonHelper.GenerateRandomInteger();
|
||||
var nestedSettingId = $"nestedSetting{random}";
|
||||
|
|
@ -106,16 +105,16 @@ namespace Nop.Web.Framework.TagHelpers.Admin
|
|||
|
||||
if (!DisableAutoGeneration)
|
||||
script.InnerHtml.AppendHtml(
|
||||
$"$('#{jsConsistentParentSettingName}').click(toggle_{jsConsistentParentSettingName});" +
|
||||
$"toggle_{jsConsistentParentSettingName}();"
|
||||
$"$('#{parentSettingName}').click(toggle_{parentSettingName});" +
|
||||
$"toggle_{parentSettingName}();"
|
||||
);
|
||||
|
||||
script.InnerHtml.AppendHtml("});");
|
||||
|
||||
if (!DisableAutoGeneration)
|
||||
script.InnerHtml.AppendHtml(
|
||||
$"function toggle_{jsConsistentParentSettingName}() " + "{" +
|
||||
$"if ({isNot}$('#{jsConsistentParentSettingName}').is(':checked')) " + "{" +
|
||||
$"function toggle_{parentSettingName}() " + "{" +
|
||||
$"if ({isNot}$('#{parentSettingName}').is(':checked')) " + "{" +
|
||||
$"$('#{nestedSettingId}').showElement();" +
|
||||
"} else {" +
|
||||
$"$('#{nestedSettingId}').hideElement();" +
|
||||
|
|
|
|||
|
|
@ -262,8 +262,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
var action = Request.Form["action"];
|
||||
|
||||
var fileName = Request.Form["backupFileName"];
|
||||
fileName = _fileProvider.GetFileName(_fileProvider.GetAbsolutePath(fileName));
|
||||
|
||||
var backupPath = _maintenanceService.GetBackupPath(fileName);
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
|
||||
public virtual async Task<IActionResult> SalesSummary()
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.SalesSummaryReport))
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageOrders))
|
||||
return AccessDeniedView();
|
||||
|
||||
//prepare model
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
{
|
||||
#region Fields
|
||||
|
||||
private readonly IAddressAttributeParser _addressAttributeParser;
|
||||
private readonly IAddressService _addressService;
|
||||
private readonly ICustomerActivityService _customerActivityService;
|
||||
private readonly ICustomerService _customerService;
|
||||
|
|
@ -49,8 +48,7 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
|
||||
#region Ctor
|
||||
|
||||
public VendorController(IAddressAttributeParser addressAttributeParser,
|
||||
IAddressService addressService,
|
||||
public VendorController(IAddressService addressService,
|
||||
ICustomerActivityService customerActivityService,
|
||||
ICustomerService customerService,
|
||||
IGenericAttributeService genericAttributeService,
|
||||
|
|
@ -65,7 +63,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
IVendorModelFactory vendorModelFactory,
|
||||
IVendorService vendorService)
|
||||
{
|
||||
_addressAttributeParser = addressAttributeParser;
|
||||
_addressService = addressService;
|
||||
_customerActivityService = customerActivityService;
|
||||
_customerService = customerService;
|
||||
|
|
@ -341,14 +338,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
(await _vendorAttributeParser.GetAttributeWarningsAsync(vendorAttributesXml)).ToList()
|
||||
.ForEach(warning => ModelState.AddModelError(string.Empty, warning));
|
||||
|
||||
//custom address attributes
|
||||
var customAttributes = await _addressAttributeParser.ParseCustomAddressAttributesAsync(form);
|
||||
var customAttributeWarnings = await _addressAttributeParser.GetAttributeWarningsAsync(customAttributes);
|
||||
foreach (var error in customAttributeWarnings)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error);
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var prevPictureId = vendor.PictureId;
|
||||
|
|
@ -371,7 +360,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
if (address == null)
|
||||
{
|
||||
address = model.Address.ToEntity<Address>();
|
||||
address.CustomAttributes = customAttributes;
|
||||
address.CreatedOnUtc = DateTime.UtcNow;
|
||||
|
||||
//some validation
|
||||
|
|
@ -387,7 +375,6 @@ namespace Nop.Web.Areas.Admin.Controllers
|
|||
else
|
||||
{
|
||||
address = model.Address.ToEntity(address);
|
||||
address.CustomAttributes = customAttributes;
|
||||
|
||||
//some validation
|
||||
if (address.CountryId == 0)
|
||||
|
|
|
|||
|
|
@ -77,9 +77,6 @@ namespace Nop.Web.Areas.Admin.Factories
|
|||
//get parameters to filter orders
|
||||
var orderStatus = searchModel.OrderStatusId > 0 ? (OrderStatus?)searchModel.OrderStatusId : null;
|
||||
var paymentStatus = searchModel.PaymentStatusId > 0 ? (PaymentStatus?)searchModel.PaymentStatusId : null;
|
||||
|
||||
var currentVendor = await _workContext.GetCurrentVendorAsync();
|
||||
|
||||
var startDateValue = !searchModel.StartDate.HasValue ? null
|
||||
: (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync());
|
||||
var endDateValue = !searchModel.EndDate.HasValue ? null
|
||||
|
|
@ -96,7 +93,6 @@ namespace Nop.Web.Areas.Admin.Factories
|
|||
categoryId: searchModel.CategoryId,
|
||||
productId: searchModel.ProductId,
|
||||
manufacturerId: searchModel.ManufacturerId,
|
||||
vendorId: currentVendor?.Id ?? 0,
|
||||
storeId: searchModel.StoreId,
|
||||
pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize);
|
||||
|
||||
|
|
@ -177,7 +173,7 @@ namespace Nop.Web.Areas.Admin.Factories
|
|||
|
||||
//prepare "group by" filter
|
||||
searchModel.GroupByOptions = (await GroupByOptions.Day.ToSelectListAsync()).ToList();
|
||||
|
||||
|
||||
//prepare page parameters
|
||||
searchModel.SetGridPageSize();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Web.Areas.Admin.Models.Settings
|
||||
|
|
@ -27,7 +26,6 @@ namespace Nop.Web.Areas.Admin.Models.Settings
|
|||
public bool MiniProfilerEnabled { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Admin.Configuration.AppSettings.Common.ScheduleTaskRunTimeout")]
|
||||
[UIHint("Int32Nullable")]
|
||||
public int? ScheduleTaskRunTimeout { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Admin.Configuration.AppSettings.Common.StaticFilesCacheControl")]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
|
|
@ -17,7 +16,6 @@ namespace Nop.Web.Areas.Admin.Models.Settings
|
|||
public SelectList DataProviderTypeValues { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Admin.Configuration.AppSettings.Data.SQLCommandTimeout")]
|
||||
[UIHint("Int32Nullable")]
|
||||
public int? SQLCommandTimeout { get; set; }
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
<nop-card asp-name="app-settings-cache" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.Cache")" asp-hide-block-attribute-name="@hideCacheBlockAttributeName" asp-hide="@hideCacheBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.Cache", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-distributed-cache" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.DistributedCache")" asp-hide-block-attribute-name="@hideDistributedCacheBlockAttributeName" asp-hide="@hideDistributedCacheBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.DistributedCache", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-hosting" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.Hosting")" asp-hide-block-attribute-name="@hideHostingBlockAttributeName" asp-hide="@hideHostingBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.Hosting", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-azure-blob" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.AzureBlob")" asp-hide-block-attribute-name="@hideAzureBlobBlockAttributeName" asp-hide="@hideAzureBlobBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.AzureBlob", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-azure-blob" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.AzureBlob")" asp-hide-block-attribute-name="@hideAzureBlobBlockAttributeName" asp-hide="@hideAzureBlobBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.AzureBlob", Model.AzureBlobConfigModel)</nop-card>
|
||||
<nop-card asp-name="app-settings-installation" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.Installation")" asp-hide-block-attribute-name="@hideInstallationBlockAttributeName" asp-hide="@hideInstallationBlock" asp-advanced="true">@await Html.PartialAsync("_AppSettings.Installation", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-plugin" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.Plugin")" asp-hide-block-attribute-name="@hidePluginBlockAttributeName" asp-hide="@hidePluginBlock" asp-advanced="true">@await Html.PartialAsync("_AppSettings.Plugin", Model)</nop-card>
|
||||
<nop-card asp-name="app-settings-common" asp-icon="fas fa-cogs" asp-title="@T("Admin.Configuration.AppSettings.Common")" asp-hide-block-attribute-name="@hideCommonBlockAttributeName" asp-hide="@hideCommonBlock" asp-advanced="false">@await Html.PartialAsync("_AppSettings.Common", Model)</nop-card>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
<nop-card asp-name="generalcommon-footeritems" asp-icon="fas fa-level-down-alt" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.FooterItems")" asp-hide-block-attribute-name="@hideFooterItemsBlockAttributeName" asp-hide="@hideFooterItemsBlock" asp-advanced="true">@await Html.PartialAsync("_GeneralCommon.FooterItems", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-socialmedia" asp-icon="fas fa-share-square" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.SocialMedia")" asp-hide-block-attribute-name="@hideSocialMediaBlockAttributeName" asp-hide="@hideSocialMediaBlock" asp-advanced="false">@await Html.PartialAsync("_GeneralCommon.SocialMedia", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-favicon" asp-icon="far fa-images" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.FaviconAndAppIcons")" asp-hide-block-attribute-name="@hideFaviconBlockAttributeName" asp-hide="@hideFaviconBlock" asp-advanced="false">@await Html.PartialAsync("_GeneralCommon.Favicon", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-sitemap" asp-icon="fas fa-sitemap" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.Sitemap")" asp-hide-block-attribute-name="@hideSitemapBlockAttributeName" asp-hide="@hideSitemapBlock" asp-advanced="true">@await Html.PartialAsync("_GeneralCommon.Sitemap", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-sitemap" asp-icon="fas fa-sitemap" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.Sitemap")" asp-hide-block-attribute-name="@hideSitemapBlockAttributeName" asp-hide="@hideSitemapBlock" asp-advanced="true">@await Html.PartialAsync("_GeneralCommon.Sitemap", Model.SitemapSettings)</nop-card>
|
||||
<nop-card asp-name="generalcommon-seo" asp-icon="fas fa-search-plus" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.SEO")" asp-hide-block-attribute-name="@hideSEOBlockAttributeName" asp-hide="@hideSEOBlock" asp-advanced="false">@await Html.PartialAsync("_GeneralCommon.Seo", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-minification" asp-icon="fas fa-compress" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.Minification")" asp-hide-block-attribute-name="@hideMinificationBlockAttributeName" asp-hide="@hideMinificationBlock" asp-advanced="true">@await Html.PartialAsync("_GeneralCommon.Minification", Model)</nop-card>
|
||||
<nop-card asp-name="generalcommon-security" asp-icon="fas fa-shield-alt" asp-title="@T("Admin.Configuration.Settings.GeneralCommon.BlockTitle.Security")" asp-hide-block-attribute-name="@hideSecurityBlockAttributeName" asp-hide="@hideSecurityBlock" asp-advanced="true">@await Html.PartialAsync("_GeneralCommon.Security", Model)</nop-card>
|
||||
|
|
|
|||
|
|
@ -1,68 +1,68 @@
|
|||
@model AppSettingsModel
|
||||
@model AzureBlobConfigModel
|
||||
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.ConnectionString" />
|
||||
<nop-label asp-for="ConnectionString" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.ConnectionString" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.ConnectionString"></span>
|
||||
<nop-editor asp-for="ConnectionString" />
|
||||
<span asp-validation-for="ConnectionString"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.ContainerName" />
|
||||
<nop-label asp-for="ContainerName" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.ContainerName" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.ContainerName"></span>
|
||||
<nop-editor asp-for="ContainerName" />
|
||||
<span asp-validation-for="ContainerName"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.EndPoint" />
|
||||
<nop-label asp-for="EndPoint" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.EndPoint" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.EndPoint"></span>
|
||||
<nop-editor asp-for="EndPoint" />
|
||||
<span asp-validation-for="EndPoint"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.AppendContainerName" />
|
||||
<nop-label asp-for="AppendContainerName" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.AppendContainerName" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.AppendContainerName"></span>
|
||||
<nop-editor asp-for="AppendContainerName" />
|
||||
<span asp-validation-for="AppendContainerName"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.StoreDataProtectionKeys" />
|
||||
<nop-label asp-for="StoreDataProtectionKeys" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.StoreDataProtectionKeys" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.StoreDataProtectionKeys"></span>
|
||||
<nop-editor asp-for="StoreDataProtectionKeys" />
|
||||
<span asp-validation-for="StoreDataProtectionKeys"></span>
|
||||
</div>
|
||||
</div>
|
||||
<nop-nested-setting asp-for="AzureBlobConfigModel.StoreDataProtectionKeys">
|
||||
<div class="form-group row">
|
||||
<nop-nested-setting asp-for="StoreDataProtectionKeys">
|
||||
<div class="form-group row" id="azure-data-protection-container">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.DataProtectionKeysContainerName" />
|
||||
<nop-label asp-for="DataProtectionKeysContainerName" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.DataProtectionKeysContainerName" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.DataProtectionKeysContainerName"></span>
|
||||
<nop-editor asp-for="DataProtectionKeysContainerName" />
|
||||
<span asp-validation-for="DataProtectionKeysContainerName"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="form-group row" id="azure-data-protection-vault">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="AzureBlobConfigModel.DataProtectionKeysVaultId" />
|
||||
<nop-label asp-for="DataProtectionKeysVaultId" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="AzureBlobConfigModel.DataProtectionKeysVaultId" />
|
||||
<span asp-validation-for="AzureBlobConfigModel.DataProtectionKeysVaultId"></span>
|
||||
<nop-editor asp-for="DataProtectionKeysVaultId" />
|
||||
<span asp-validation-for="DataProtectionKeysVaultId"></span>
|
||||
</div>
|
||||
</div>
|
||||
</nop-nested-setting>
|
||||
|
|
|
|||
|
|
@ -1,96 +1,96 @@
|
|||
@model GeneralCommonSettingsModel
|
||||
@model SitemapSettingsModel
|
||||
|
||||
<div class="card-body">
|
||||
@(Html.Raw(string.Format(T("Admin.Configuration.Settings.GeneralCommon.Sitemap.Instructions").Text, Url.Action("AllSettings", "Setting", new { settingName = "sitemapxml" }))))
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapEnabled_OverrideForStore" asp-input="SitemapSettings.SitemapEnabled" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapEnabled" />
|
||||
<nop-override-store-checkbox asp-for="SitemapEnabled_OverrideForStore" asp-input="SitemapEnabled" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapEnabled" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapEnabled" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapEnabled"></span>
|
||||
<nop-editor asp-for="SitemapEnabled" />
|
||||
<span asp-validation-for="SitemapEnabled"></span>
|
||||
</div>
|
||||
</div>
|
||||
<nop-nested-setting asp-for="SitemapSettings.SitemapEnabled">
|
||||
<nop-nested-setting asp-for="SitemapEnabled">
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapPageSize_OverrideForStore" asp-input="SitemapSettings.SitemapPageSize" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapPageSize" />
|
||||
<nop-override-store-checkbox asp-for="SitemapPageSize_OverrideForStore" asp-input="SitemapPageSize" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapPageSize" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapPageSize" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapPageSize"></span>
|
||||
<nop-editor asp-for="SitemapPageSize" />
|
||||
<span asp-validation-for="SitemapPageSize"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeCategories_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeCategories" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeCategories" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeCategories_OverrideForStore" asp-input="SitemapIncludeCategories" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeCategories" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeCategories" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeCategories"></span>
|
||||
<nop-editor asp-for="SitemapIncludeCategories" />
|
||||
<span asp-validation-for="SitemapIncludeCategories"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeManufacturers_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeManufacturers" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeManufacturers" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeManufacturers_OverrideForStore" asp-input="SitemapIncludeManufacturers" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeManufacturers" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeManufacturers" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeManufacturers"></span>
|
||||
<nop-editor asp-for="SitemapIncludeManufacturers" />
|
||||
<span asp-validation-for="SitemapIncludeManufacturers"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeProducts_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeProducts" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeProducts" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeProducts_OverrideForStore" asp-input="SitemapIncludeProducts" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeProducts" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeProducts" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeProducts"></span>
|
||||
<nop-editor asp-for="SitemapIncludeProducts" />
|
||||
<span asp-validation-for="SitemapIncludeProducts"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeProductTags_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeProductTags" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeProductTags" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeProductTags_OverrideForStore" asp-input="SitemapIncludeProductTags" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeProductTags" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeProductTags" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeProductTags"></span>
|
||||
<nop-editor asp-for="SitemapIncludeProductTags" />
|
||||
<span asp-validation-for="SitemapIncludeProductTags"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeTopics_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeTopics" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeTopics" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeTopics_OverrideForStore" asp-input="SitemapIncludeTopics" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeTopics" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeTopics" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeTopics"></span>
|
||||
<nop-editor asp-for="SitemapIncludeTopics" />
|
||||
<span asp-validation-for="SitemapIncludeTopics"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeBlogPosts_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeBlogPosts" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeBlogPosts" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeBlogPosts_OverrideForStore" asp-input="SitemapIncludeBlogPosts" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeBlogPosts" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeBlogPosts" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeBlogPosts"></span>
|
||||
<nop-editor asp-for="SitemapIncludeBlogPosts" />
|
||||
<span asp-validation-for="SitemapIncludeBlogPosts"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row advanced-setting">
|
||||
<div class="col-md-3">
|
||||
<nop-override-store-checkbox asp-for="SitemapSettings.SitemapIncludeNews_OverrideForStore" asp-input="SitemapSettings.SitemapIncludeNews" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapSettings.SitemapIncludeNews" />
|
||||
<nop-override-store-checkbox asp-for="SitemapIncludeNews_OverrideForStore" asp-input="SitemapIncludeNews" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />
|
||||
<nop-label asp-for="SitemapIncludeNews" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<nop-editor asp-for="SitemapSettings.SitemapIncludeNews" />
|
||||
<span asp-validation-for="SitemapSettings.SitemapIncludeNews"></span>
|
||||
<nop-editor asp-for="SitemapIncludeNews" />
|
||||
<span asp-validation-for="SitemapIncludeNews"></span>
|
||||
</div>
|
||||
</div>
|
||||
</nop-nested-setting>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
max: @decimal.MaxValue.ToString(culture),
|
||||
decimals: 4,
|
||||
restrictDecimals: true,
|
||||
format: "#.0000 @Html.Raw(postfix)"
|
||||
format: "#.0000 @postfix"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
max: @decimal.MaxValue.ToString(culture),
|
||||
decimals: 4,
|
||||
restrictDecimals: true,
|
||||
format: "#.0000 @Html.Raw(postfix)"
|
||||
format: "#.0000 @postfix"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
max: @double.MaxValue.ToString(culture),
|
||||
decimals: 4,
|
||||
restrictDecimals: true,
|
||||
format: "#.0000 @Html.Raw(postfix)"
|
||||
format: "#.0000 @postfix"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
max: @int.MaxValue.ToString(culture),
|
||||
decimals: 0,
|
||||
restrictDecimals: true,
|
||||
format: "# @Html.Raw(postfix)"
|
||||
format: "# @postfix"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
max: @int.MaxValue.ToString(culture),
|
||||
decimals: 0,
|
||||
restrictDecimals: true,
|
||||
format: "# @Html.Raw(postfix)"
|
||||
format: "# @postfix"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
<siteMapNode SystemName="Templates" nopResource="Admin.System.Templates" PermissionNames="ManageMaintenance" controller="Template" action="List" IconClass="far fa-dot-circle"/>
|
||||
</siteMapNode>
|
||||
<siteMapNode SystemName="Reports" nopResource="Admin.Reports" PermissionNames="ManageProducts,ManageOrders,OrderCountryReport,ManageCustomers" IconClass="fas fa-chart-line">
|
||||
<siteMapNode SystemName="Sales summary" nopResource="Admin.Reports.SalesSummary" PermissionNames="SalesSummaryReport" controller="Report" action="SalesSummary" IconClass="far fa-dot-circle"/>
|
||||
<siteMapNode SystemName="Sales summary" nopResource="Admin.Reports.SalesSummary" PermissionNames="ManageOrders" controller="Report" action="SalesSummary" IconClass="far fa-dot-circle"/>
|
||||
<siteMapNode SystemName="Low stock" nopResource="Admin.Reports.LowStock" PermissionNames="ManageProducts" controller="Report" action="LowStock" IconClass="far fa-dot-circle"/>
|
||||
<siteMapNode SystemName="Bestsellers" nopResource="Admin.Reports.Sales.Bestsellers" PermissionNames="ManageOrders" controller="Report" action="Bestsellers" IconClass="far fa-dot-circle"/>
|
||||
<siteMapNode SystemName="Products never purchased" nopResource="Admin.Reports.Sales.NeverSold" PermissionNames="ManageOrders" controller="Report" action="NeverSold" IconClass="far fa-dot-circle"/>
|
||||
|
|
@ -127,3 +127,4 @@
|
|||
<siteMapNode SystemName="Third party plugins" nopResource="Admin.Plugins" IconClass="fas fa-bars" />
|
||||
</siteMapNode>
|
||||
</siteMap>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ using Nop.Web.Extensions;
|
|||
using Nop.Web.Factories;
|
||||
using Nop.Web.Framework.Controllers;
|
||||
using Nop.Web.Models.Checkout;
|
||||
using Nop.Web.Models.Common;
|
||||
|
||||
namespace Nop.Web.Controllers
|
||||
{
|
||||
|
|
@ -36,8 +35,7 @@ namespace Nop.Web.Controllers
|
|||
|
||||
private readonly AddressSettings _addressSettings;
|
||||
private readonly CustomerSettings _customerSettings;
|
||||
private readonly IAddressAttributeParser _addressAttributeParser;
|
||||
private readonly IAddressModelFactory _addressModelFactory;
|
||||
private readonly IAddressAttributeParser _addressAttributeParser;
|
||||
private readonly IAddressService _addressService;
|
||||
private readonly ICheckoutModelFactory _checkoutModelFactory;
|
||||
private readonly ICountryService _countryService;
|
||||
|
|
@ -67,7 +65,6 @@ namespace Nop.Web.Controllers
|
|||
public CheckoutController(AddressSettings addressSettings,
|
||||
CustomerSettings customerSettings,
|
||||
IAddressAttributeParser addressAttributeParser,
|
||||
IAddressModelFactory addressModelFactory,
|
||||
IAddressService addressService,
|
||||
ICheckoutModelFactory checkoutModelFactory,
|
||||
ICountryService countryService,
|
||||
|
|
@ -93,7 +90,6 @@ namespace Nop.Web.Controllers
|
|||
_addressSettings = addressSettings;
|
||||
_customerSettings = customerSettings;
|
||||
_addressAttributeParser = addressAttributeParser;
|
||||
_addressModelFactory = addressModelFactory;
|
||||
_addressService = addressService;
|
||||
_checkoutModelFactory = checkoutModelFactory;
|
||||
_countryService = countryService;
|
||||
|
|
@ -324,16 +320,7 @@ namespace Nop.Web.Controllers
|
|||
if (address == null)
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
|
||||
var addressModel = new AddressModel();
|
||||
|
||||
await _addressModelFactory.PrepareAddressModelAsync(addressModel,
|
||||
address: address,
|
||||
excludeProperties: false,
|
||||
addressSettings: _addressSettings,
|
||||
prePopulateWithCustomerFields: true,
|
||||
customer: customer);
|
||||
|
||||
var json = JsonConvert.SerializeObject(addressModel, Formatting.Indented,
|
||||
var json = JsonConvert.SerializeObject(address, Formatting.Indented,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
|
|
@ -348,7 +335,7 @@ namespace Nop.Web.Controllers
|
|||
/// <param name="model"></param>
|
||||
/// <param name="opc"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<IActionResult> SaveEditAddress(CheckoutBillingAddressModel model, IFormCollection form, bool opc = false)
|
||||
public virtual async Task<IActionResult> SaveEditAddress(CheckoutBillingAddressModel model, bool opc = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -363,18 +350,7 @@ namespace Nop.Web.Controllers
|
|||
if (address == null)
|
||||
throw new Exception("Address can't be loaded");
|
||||
|
||||
//custom address attributes
|
||||
var customAttributes = await _addressAttributeParser.ParseCustomAddressAttributesAsync(form);
|
||||
var customAttributeWarnings = await _addressAttributeParser.GetAttributeWarningsAsync(customAttributes);
|
||||
|
||||
if(customAttributeWarnings.Any())
|
||||
{
|
||||
return Json(new { error = 1, message = customAttributeWarnings });
|
||||
}
|
||||
|
||||
address = model.BillingNewAddress.ToEntity(address);
|
||||
address.CustomAttributes = customAttributes;
|
||||
|
||||
await _addressService.UpdateAddressAsync(address);
|
||||
|
||||
customer.BillingAddressId = address.Id;
|
||||
|
|
@ -1776,7 +1752,6 @@ namespace Nop.Web.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public virtual async Task<IActionResult> OpcSavePaymentInfo(IFormCollection form)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -56,9 +55,9 @@ namespace Nop.Web.Controllers
|
|||
private readonly CaptchaSettings _captchaSettings;
|
||||
private readonly CustomerSettings _customerSettings;
|
||||
private readonly DateTimeSettings _dateTimeSettings;
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly ForumSettings _forumSettings;
|
||||
private readonly GdprSettings _gdprSettings;
|
||||
private readonly HtmlEncoder _htmlEncoder;
|
||||
private readonly IAddressAttributeParser _addressAttributeParser;
|
||||
private readonly IAddressModelFactory _addressModelFactory;
|
||||
private readonly IAddressService _addressService;
|
||||
|
|
@ -71,7 +70,6 @@ namespace Nop.Web.Controllers
|
|||
private readonly ICustomerModelFactory _customerModelFactory;
|
||||
private readonly ICustomerRegistrationService _customerRegistrationService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private readonly IExportManager _exportManager;
|
||||
private readonly IExternalAuthenticationService _externalAuthenticationService;
|
||||
|
|
@ -106,9 +104,9 @@ namespace Nop.Web.Controllers
|
|||
CaptchaSettings captchaSettings,
|
||||
CustomerSettings customerSettings,
|
||||
DateTimeSettings dateTimeSettings,
|
||||
IDownloadService downloadService,
|
||||
ForumSettings forumSettings,
|
||||
GdprSettings gdprSettings,
|
||||
HtmlEncoder htmlEncoder,
|
||||
IAddressAttributeParser addressAttributeParser,
|
||||
IAddressModelFactory addressModelFactory,
|
||||
IAddressService addressService,
|
||||
|
|
@ -121,7 +119,6 @@ namespace Nop.Web.Controllers
|
|||
ICustomerModelFactory customerModelFactory,
|
||||
ICustomerRegistrationService customerRegistrationService,
|
||||
ICustomerService customerService,
|
||||
IDownloadService downloadService,
|
||||
IEventPublisher eventPublisher,
|
||||
IExportManager exportManager,
|
||||
IExternalAuthenticationService externalAuthenticationService,
|
||||
|
|
@ -152,9 +149,9 @@ namespace Nop.Web.Controllers
|
|||
_captchaSettings = captchaSettings;
|
||||
_customerSettings = customerSettings;
|
||||
_dateTimeSettings = dateTimeSettings;
|
||||
_downloadService = downloadService;
|
||||
_forumSettings = forumSettings;
|
||||
_gdprSettings = gdprSettings;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
_addressAttributeParser = addressAttributeParser;
|
||||
_addressModelFactory = addressModelFactory;
|
||||
_addressService = addressService;
|
||||
|
|
@ -167,7 +164,6 @@ namespace Nop.Web.Controllers
|
|||
_customerModelFactory = customerModelFactory;
|
||||
_customerRegistrationService = customerRegistrationService;
|
||||
_customerService = customerService;
|
||||
_downloadService = downloadService;
|
||||
_eventPublisher = eventPublisher;
|
||||
_exportManager = exportManager;
|
||||
_externalAuthenticationService = externalAuthenticationService;
|
||||
|
|
@ -429,7 +425,7 @@ namespace Nop.Web.Controllers
|
|||
{
|
||||
var fullName = await _customerService.GetCustomerFullNameAsync(customer);
|
||||
var message = await _localizationService.GetResourceAsync("Account.Login.AlreadyLogin");
|
||||
_notificationService.SuccessNotification(string.Format(message, _htmlEncoder.Encode(fullName)));
|
||||
_notificationService.SuccessNotification(string.Format(message, fullName));
|
||||
}
|
||||
|
||||
return View(model);
|
||||
|
|
@ -1539,14 +1535,14 @@ namespace Nop.Web.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
public virtual async Task<IActionResult> AddressEdit(CustomerAddressEditModel model, IFormCollection form)
|
||||
public virtual async Task<IActionResult> AddressEdit(CustomerAddressEditModel model, int addressId, IFormCollection form)
|
||||
{
|
||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||
if (!await _customerService.IsRegisteredAsync(customer))
|
||||
return Challenge();
|
||||
|
||||
//find address (ensure that it belongs to the current customer)
|
||||
var address = await _customerService.GetCustomerAddressAsync(customer.Id, model.Address.Id);
|
||||
var address = await _customerService.GetCustomerAddressAsync(customer.Id, addressId);
|
||||
if (address == null)
|
||||
//address is not found
|
||||
return RedirectToRoute("CustomerAddresses");
|
||||
|
|
@ -1648,15 +1644,7 @@ namespace Nop.Web.Controllers
|
|||
if (changePasswordResult.Success)
|
||||
{
|
||||
_notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Account.ChangePassword.Success"));
|
||||
|
||||
if (string.IsNullOrEmpty(returnUrl))
|
||||
return View(model);
|
||||
|
||||
//prevent open redirection attack
|
||||
if (!Url.IsLocalUrl(returnUrl))
|
||||
returnUrl = Url.RouteUrl("Homepage");
|
||||
|
||||
return new RedirectResult(returnUrl);
|
||||
return string.IsNullOrEmpty(returnUrl) ? View(model) : new RedirectResult(returnUrl);
|
||||
}
|
||||
|
||||
//errors
|
||||
|
|
@ -1697,11 +1685,6 @@ namespace Nop.Web.Controllers
|
|||
if (!_customerSettings.AllowCustomersToUploadAvatars)
|
||||
return RedirectToRoute("CustomerInfo");
|
||||
|
||||
var contentType = uploadedFile.ContentType.ToLowerInvariant();
|
||||
|
||||
if (!contentType.Equals("image/jpeg") && !contentType.Equals("image/gif"))
|
||||
ModelState.AddModelError("", await _localizationService.GetResourceAsync("Account.Avatar.UploadRules"));
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
|
|
@ -1715,9 +1698,9 @@ namespace Nop.Web.Controllers
|
|||
|
||||
var customerPictureBinary = await _downloadService.GetDownloadBitsAsync(uploadedFile);
|
||||
if (customerAvatar != null)
|
||||
customerAvatar = await _pictureService.UpdatePictureAsync(customerAvatar.Id, customerPictureBinary, contentType, null);
|
||||
customerAvatar = await _pictureService.UpdatePictureAsync(customerAvatar.Id, customerPictureBinary, uploadedFile.ContentType, null);
|
||||
else
|
||||
customerAvatar = await _pictureService.InsertPictureAsync(customerPictureBinary, contentType, null);
|
||||
customerAvatar = await _pictureService.InsertPictureAsync(customerPictureBinary, uploadedFile.ContentType, null);
|
||||
}
|
||||
|
||||
var customerAvatarId = 0;
|
||||
|
|
|
|||
|
|
@ -220,18 +220,12 @@ namespace Nop.Web.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var contentType = uploadedFile.ContentType.ToLowerInvariant();
|
||||
var contentType = uploadedFile.ContentType;
|
||||
var vendorPictureBinary = await _downloadService.GetDownloadBitsAsync(uploadedFile);
|
||||
var picture = await _pictureService.InsertPictureAsync(vendorPictureBinary, contentType, null);
|
||||
|
||||
if(!contentType.StartsWith("image/"))
|
||||
ModelState.AddModelError("", await _localizationService.GetResourceAsync("Vendors.ApplyAccount.Picture.ErrorMessage"));
|
||||
else
|
||||
{
|
||||
var vendorPictureBinary = await _downloadService.GetDownloadBitsAsync(uploadedFile);
|
||||
var picture = await _pictureService.InsertPictureAsync(vendorPictureBinary, contentType, null);
|
||||
|
||||
if (picture != null)
|
||||
pictureId = picture.Id;
|
||||
}
|
||||
if (picture != null)
|
||||
pictureId = picture.Id;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
|
@ -320,15 +314,9 @@ namespace Nop.Web.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var contentType = uploadedFile.ContentType.ToLowerInvariant();
|
||||
|
||||
if (!contentType.StartsWith("image/"))
|
||||
ModelState.AddModelError("", await _localizationService.GetResourceAsync("Account.VendorInfo.Picture.ErrorMessage"));
|
||||
else
|
||||
{
|
||||
var vendorPictureBinary = await _downloadService.GetDownloadBitsAsync(uploadedFile);
|
||||
picture = await _pictureService.InsertPictureAsync(vendorPictureBinary, contentType, null);
|
||||
}
|
||||
var contentType = uploadedFile.ContentType;
|
||||
var vendorPictureBinary = await _downloadService.GetDownloadBitsAsync(uploadedFile);
|
||||
picture = await _pictureService.InsertPictureAsync(vendorPictureBinary, contentType, null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ namespace Nop.Web.Factories
|
|||
var attributeModel = new AddressAttributeModel
|
||||
{
|
||||
Id = attribute.Id,
|
||||
ControlId = string.Format(NopCommonDefaults.AddressAttributeControlName, attribute.Id),
|
||||
Name = await _localizationService.GetLocalizedAsync(attribute, x => x.Name),
|
||||
IsRequired = attribute.IsRequired,
|
||||
AttributeControlType = attribute.AttributeControlType,
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ namespace Nop.Web.Factories
|
|||
{
|
||||
//calculate price for the maximum quantity if we have tier prices, and choose minimal
|
||||
tmpMinPossiblePrice = Math.Min(tmpMinPossiblePrice,
|
||||
(await _priceCalculationService.GetFinalPriceAsync(associatedProduct, customer, quantity: int.MaxValue)).finalPrice);
|
||||
(await _priceCalculationService.GetFinalPriceAsync(associatedProduct, customer, quantity: int.MaxValue)).priceWithoutDiscounts);
|
||||
}
|
||||
|
||||
if (minPossiblePrice.HasValue && tmpMinPossiblePrice >= minPossiblePrice.Value)
|
||||
|
|
@ -1065,7 +1065,7 @@ namespace Nop.Web.Factories
|
|||
{
|
||||
var priceBase = (await _taxService.GetProductPriceAsync(product, (await _priceCalculationService.GetFinalPriceAsync(product,
|
||||
customer, decimal.Zero, _catalogSettings.DisplayTierPricesWithDiscounts,
|
||||
tierPrice.Quantity)).finalPrice)).price;
|
||||
tierPrice.Quantity)).priceWithoutDiscounts)).price;
|
||||
|
||||
var price = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(priceBase, await _workContext.GetWorkingCurrencyAsync());
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ namespace Nop.Web.Models.Common
|
|||
Values = new List<AddressAttributeValueModel>();
|
||||
}
|
||||
|
||||
public string ControlId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsRequired { get; set; }
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
<PackageProjectUrl>https://www.nopcommerce.com/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<!--Starting with the .NET 6 SDK, the [Appname].runtimesettings.dev.json file is no longer generated by default at compile time. If you still want this file to be generated, set the GenerateRuntimeConfigDevFile property to true.-->
|
||||
<GenerateRuntimeConfigDevFile>true</GenerateRuntimeConfigDevFile>
|
||||
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project-->
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ var builder = WebApplication.CreateBuilder(args);
|
|||
|
||||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
builder.Configuration.AddJsonFile(NopConfigurationDefaults.AppSettingsFilePath, true, true);
|
||||
if (!string.IsNullOrEmpty(builder.Environment?.EnvironmentName))
|
||||
{
|
||||
var path = string.Format(NopConfigurationDefaults.AppSettingsEnvironmentFilePath, builder.Environment.EnvironmentName);
|
||||
builder.Configuration.AddJsonFile(path, true, true);
|
||||
}
|
||||
builder.Configuration.AddEnvironmentVariables();
|
||||
|
||||
//Add services to the application and configure service provider
|
||||
|
|
|
|||
|
|
@ -6467,6 +6467,12 @@ label, label + * {
|
|||
|
||||
/* BB codes */
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.csharpcode {
|
||||
margin: 10px 0;
|
||||
border: 1px dashed #ccc;
|
||||
|
|
@ -6474,9 +6480,6 @@ label, label + * {
|
|||
padding: 10px;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
color: #000;
|
||||
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.csharpcode .rem {
|
||||
|
|
|
|||
|
|
@ -6490,6 +6490,12 @@ label, label + * {
|
|||
|
||||
/* BB codes */
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.csharpcode {
|
||||
margin: 10px 0;
|
||||
border: 1px dashed #ccc;
|
||||
|
|
@ -6497,9 +6503,6 @@ label, label + * {
|
|||
padding: 10px;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
color: #000;
|
||||
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.csharpcode .rem {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
@await Component.InvokeAsync("ForumBreadcrumb", new { forumId = Model.Id })
|
||||
@await Html.PartialAsync("_ForumHeader")
|
||||
@await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.BoardsForumAfterHeader, additionalData = Model })
|
||||
<nop-antiforgery-token />
|
||||
<div class="forum-info">
|
||||
<div class="forum-name">
|
||||
@if (Model.ForumFeedsEnabled)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
@await Component.InvokeAsync("ForumBreadcrumb", new { forumTopicId = Model.Id })
|
||||
@await Html.PartialAsync("_ForumHeader")
|
||||
@await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.BoardsTopicAfterHeader, additionalData = Model })
|
||||
<nop-antiforgery-token />
|
||||
<div class="topic-name">
|
||||
<h1>@Model.Subject</h1>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
<h1>@T("Account.MyAccount") - @T("Account.CustomerAddresses")</h1>
|
||||
</div>
|
||||
<div class="page-body">
|
||||
<nop-antiforgery-token />
|
||||
@if (Model.Addresses.Count > 0)
|
||||
{
|
||||
<div class="address-list">
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<img src="@(Model.AvatarUrl)" alt="avatar" />
|
||||
</div>
|
||||
}
|
||||
<input name="uploadedFile" accept="image/jpeg, image/gif" type="file" />
|
||||
<input name="uploadedFile" type="file" />
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" name="upload-avatar" class="button-1 upload-avatar-button">@T("Common.Upload")</button>
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@
|
|||
cityEl: '#@Html.IdFor(model => model.City)',
|
||||
requestDelay: @Model.RequestDelay,
|
||||
localizedData: {
|
||||
noShippingOptionsMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShippingPopUp.NoShippingOptions").Text)',
|
||||
countryErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.Country.Required").Text)',
|
||||
zipPostalCodeErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.ZipPostalCode.Required").Text)',
|
||||
cityErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.City.Required").Text)',
|
||||
noShippingOptionsMessage: '@T("Shipping.EstimateShippingPopUp.NoShippingOptions")',
|
||||
countryErrorMessage: '@T("Shipping.EstimateShipping.Country.Required")',
|
||||
zipPostalCodeErrorMessage: '@T("Shipping.EstimateShipping.ZipPostalCode.Required")',
|
||||
cityErrorMessage: '@T("Shipping.EstimateShipping.City.Required")',
|
||||
},
|
||||
urlFactory: function (address) {
|
||||
var params = $.param({
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
load: function () {
|
||||
if (!$.magnificPopup.instance.isOpen) {
|
||||
var shippingTitle = $('<div/>').addClass('shipping-title')
|
||||
.append($('<span/>').addClass('shipping-price-title').text('@JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.PriceTitle").Text)'))
|
||||
.append($('<span/>').addClass('shipping-price-title').text('@T("Products.EstimateShipping.PriceTitle")'))
|
||||
.append($('<span/>').addClass('shipping-loading'));
|
||||
$('#open-estimate-shipping-popup-@Model.ProductId').html(shippingTitle);
|
||||
}
|
||||
|
|
@ -83,22 +83,22 @@
|
|||
var shippingContent = $('#open-estimate-shipping-popup-@Model.ProductId');
|
||||
|
||||
var shippingTitle = $('<div/>').addClass('shipping-title')
|
||||
.append($('<span/>').addClass('shipping-price-title').text('@JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.PriceTitle").Text)'))
|
||||
.append($('<span/>').addClass('shipping-price-title').text('@T("Products.EstimateShipping.PriceTitle")'))
|
||||
.append($('<span/>').addClass('shipping-price').text(option.price));
|
||||
shippingContent.html(shippingTitle);
|
||||
|
||||
var estimatedDelivery = $('<div/>').addClass('estimated-delivery')
|
||||
.append($('<div/>').addClass('shipping-address')
|
||||
.append($('<span/>').text('@JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.ToAddress").Text) ' + option.address.countryName + ', ' + (option.address.stateProvinceName ? option.address.stateProvinceName + ', ' : '') + (popUp.settings.useCity ? option.address.city : option.address.zipPostalCode) + ' @JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.ViaProvider").Text) ' + option.provider))
|
||||
.append($('<span/>').text('@T("Products.EstimateShipping.ToAddress") ' + option.address.countryName + ', ' + (option.address.stateProvinceName ? option.address.stateProvinceName + ', ' : '') + (popUp.settings.useCity ? option.address.city : option.address.zipPostalCode) + ' @T("Products.EstimateShipping.ViaProvider") ' + option.provider))
|
||||
.append($('<i/>').addClass('arrow-down')));
|
||||
|
||||
if (option.deliveryDate && option.deliveryDate !== '-')
|
||||
estimatedDelivery.append($('<div/>').addClass('shipping-date').text('@JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.EstimatedDeliveryPrefix").Text) ' + option.deliveryDate));
|
||||
estimatedDelivery.append($('<div/>').addClass('shipping-date').text('@T("Products.EstimateShipping.EstimatedDeliveryPrefix") ' + option.deliveryDate));
|
||||
|
||||
shippingContent.append(estimatedDelivery);
|
||||
} else {
|
||||
$('#open-estimate-shipping-popup-@Model.ProductId')
|
||||
.html($('<span/>').text('@JavaScriptEncoder.Default.Encode(T("Products.EstimateShipping.NoSelectedShippingOption").Text)'))
|
||||
.html($('<span/>').text('@T("Products.EstimateShipping.NoSelectedShippingOption")'))
|
||||
.append($('<i/>').addClass('arrow-down'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script asp-location="Footer">
|
||||
<nop-antiforgery-token />
|
||||
<script asp-location="Footer">
|
||||
$(document).ready(function () {
|
||||
$('#eu-cookie-bar-notification').show();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="newsletter-result" id="newsletter-result-block"></div>
|
||||
<nop-antiforgery-token />
|
||||
<script asp-location="Footer">
|
||||
function newsletter_subscribe(subscribe) {
|
||||
var subscribeProgress = $("#subscribe-loading-progress");
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
cityEl: '#@Html.IdFor(model => model.City)',
|
||||
requestDelay: @Model.RequestDelay,
|
||||
localizedData: {
|
||||
noShippingOptionsMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShippingPopUp.NoShippingOptions").Text)',
|
||||
countryErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.Country.Required").Text)',
|
||||
zipPostalCodeErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.ZipPostalCode.Required").Text)',
|
||||
cityErrorMessage: '@JavaScriptEncoder.Default.Encode(T("Shipping.EstimateShipping.City.Required").Text)',
|
||||
noShippingOptionsMessage: '@T("Shipping.EstimateShippingPopUp.NoShippingOptions")',
|
||||
countryErrorMessage: '@T("Shipping.EstimateShipping.Country.Required")',
|
||||
zipPostalCodeErrorMessage: '@T("Shipping.EstimateShipping.ZipPostalCode.Required")',
|
||||
cityErrorMessage: '@T("Shipping.EstimateShipping.City.Required")',
|
||||
},
|
||||
urlFactory: function (address) {
|
||||
var params = $.param({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
@using Nop.Core.Domain.Catalog;
|
||||
@foreach (var attribute in Model)
|
||||
{
|
||||
var controlId = attribute.ControlId;
|
||||
var controlId = $"address_attribute_{attribute.Id}";
|
||||
var textPrompt = attribute.Name;
|
||||
|
||||
<div class="inputs custom-attributes">
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
</div>
|
||||
<div class="poll-vote-error" id="block-poll-vote-error-@(Model.Id)" style="display:none">
|
||||
</div>
|
||||
<nop-antiforgery-token />
|
||||
<script asp-location="Footer">
|
||||
$(document).ready(function () {
|
||||
$('#vote-poll-@(Model.Id)').on('click', function () {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@
|
|||
<!--Powered by nopCommerce - https://www.nopCommerce.com-->
|
||||
</head>
|
||||
<body>
|
||||
<nop-antiforgery-token />
|
||||
@RenderBody()
|
||||
|
||||
@NopHtml.GenerateScripts(ResourceLocation.Footer)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
</div>
|
||||
<div class="inputs">
|
||||
<label>@T("Vendors.ApplyAccount.Picture"):</label>
|
||||
<input name="uploadedFile" type="file" accept="image/*"/>
|
||||
<input name="uploadedFile" type="file"/>
|
||||
</div>
|
||||
@await Html.PartialAsync("_VendorAttributes", Model.VendorAttributes)
|
||||
@if (Model.DisplayCaptcha)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<div class="inputs">
|
||||
<label asp-for="PictureUrl" asp-postfix=":"></label>
|
||||
<input name="uploadedFile" type="file" accept="image/*" />
|
||||
<input name="uploadedFile" type="file" />
|
||||
@if (!string.IsNullOrEmpty(Model.PictureUrl))
|
||||
{
|
||||
<div class="vendor-picture">
|
||||
|
|
|
|||
|
|
@ -20,27 +20,7 @@
|
|||
success: function(data, textStatus, jqXHR) {
|
||||
$.each(data,
|
||||
function(id, value) {
|
||||
if (id.indexOf("CustomAddressAttributes") >= 0 && Array.isArray(value)) {
|
||||
$.each(value, function (i, customAttribute) {
|
||||
if (customAttribute.DefaultValue) {
|
||||
$(`#${customAttribute.ControlId}`).val(
|
||||
customAttribute.DefaultValue
|
||||
);
|
||||
} else {
|
||||
$.each(customAttribute.Values, function (j, attributeValue) {
|
||||
if (attributeValue.IsPreSelected) {
|
||||
$(`#${customAttribute.ControlId}`).val(attributeValue.Id);
|
||||
$(
|
||||
`#${customAttribute.ControlId}_${attributeValue.Id}`
|
||||
).prop("checked", attributeValue.Id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log("id:" + id + "\nvalue:" + value);
|
||||
if (value !== null) {
|
||||
var val = $(`#${prefix}${id}`).val(value);
|
||||
if (id.indexOf('CountryId') >= 0) {
|
||||
|
|
|
|||
|
|
@ -210,50 +210,30 @@ var Billing = {
|
|||
type: "GET",
|
||||
url: url,
|
||||
data: {
|
||||
addressId: selectedItem,
|
||||
"addressId": selectedItem
|
||||
},
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
$.each(data, function (id, value) {
|
||||
if (value === null)
|
||||
return;
|
||||
|
||||
if (id.indexOf("CustomAddressAttributes") >= 0 && Array.isArray(value)) {
|
||||
$.each(value, function (i, customAttribute) {
|
||||
if (customAttribute.DefaultValue) {
|
||||
$(`#${customAttribute.ControlId}`).val(
|
||||
customAttribute.DefaultValue
|
||||
);
|
||||
} else {
|
||||
$.each(customAttribute.Values, function (j, attributeValue) {
|
||||
if (attributeValue.IsPreSelected) {
|
||||
$(`#${customAttribute.ControlId}`).val(attributeValue.Id);
|
||||
$(
|
||||
`#${customAttribute.ControlId}_${attributeValue.Id}`
|
||||
).prop("checked", attributeValue.Id);
|
||||
}
|
||||
});
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$.each(data,
|
||||
function(id, value) {
|
||||
//console.log("id:" + id + "\nvalue:" + value);
|
||||
if (value !== null) {
|
||||
var val = $(`#${prefix}${id}`).val(value);
|
||||
if (id.indexOf('CountryId') >= 0) {
|
||||
val.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var val = $(`#${prefix}${id}`).val(value);
|
||||
if (id.indexOf("CountryId") >= 0) {
|
||||
val.trigger("change");
|
||||
}
|
||||
if (id.indexOf("StateProvinceId") >= 0) {
|
||||
Billing.setSelectedStateId(value);
|
||||
}
|
||||
});
|
||||
if (id.indexOf('StateProvinceId') >= 0) {
|
||||
Billing.setSelectedStateId(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
$("#billing-new-address-form").show();
|
||||
$("#edit-address-button").hide();
|
||||
$("#delete-address-button").hide();
|
||||
$("#save-address-button").show();
|
||||
complete: function(jqXHR, textStatus) {
|
||||
$('#billing-new-address-form').show();
|
||||
$('#edit-address-button').hide();
|
||||
$('#delete-address-button').hide();
|
||||
$('#save-address-button').show();
|
||||
},
|
||||
error: Checkout.ajaxFailure,
|
||||
error: Checkout.ajaxFailure
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
|
@ -259,10 +258,6 @@ namespace Nop.Tests
|
|||
services.AddSingleton<IStaticCacheManager, MemoryCacheManager>();
|
||||
services.AddSingleton<ILocker, MemoryCacheManager>();
|
||||
|
||||
var distributedCache = new Mock<IDistributedCache>();
|
||||
services.AddSingleton(distributedCache.Object);
|
||||
services.AddSingleton<DistributedCacheManager>();
|
||||
|
||||
//services
|
||||
services.AddTransient<IBackInStockSubscriptionService, BackInStockSubscriptionService>();
|
||||
services.AddTransient<ICategoryService, CategoryService>();
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Moq;
|
||||
using Nop.Core.Caching;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nop.Tests.Nop.Core.Tests.Caching
|
||||
{
|
||||
[TestFixture]
|
||||
public class DistributedCacheManagerTests : BaseNopTest
|
||||
{
|
||||
private DistributedCacheManager _staticCacheManager;
|
||||
private Mock<IDistributedCache> _distributedCache;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_staticCacheManager = GetService<DistributedCacheManager>();
|
||||
_distributedCache = Mock.Get(GetService<IDistributedCache>());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanSetObjectInCacheAndWillTrackIfRemoved()
|
||||
{
|
||||
|
||||
await _staticCacheManager.SetAsync(new CacheKey("some_key_1"), 1);
|
||||
_distributedCache.Verify(x=> x.SetAsync("some_key_1", It.IsAny<byte[]>(), It.IsAny<DistributedCacheEntryOptions>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
await _staticCacheManager.RemoveByPrefixAsync("some_key_1");
|
||||
_distributedCache.Verify(x=> x.RemoveAsync("some_key_1", It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanGetAsyncFromCacheAndWillTrackInPerRequestCacheIfRemoved()
|
||||
{
|
||||
_distributedCache.Setup(x => x.GetAsync("some_key_2", It.IsAny<CancellationToken>())).ReturnsAsync(Encoding.UTF8.GetBytes("2"));
|
||||
await _staticCacheManager.GetAsync(new CacheKey("some_key_2"),()=> 2);
|
||||
await _staticCacheManager.RemoveByPrefixAsync("some_key_2");
|
||||
_distributedCache.Verify(x=> x.RemoveAsync("some_key_2", It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanGetFromCacheAndWillTrackInPerRequestCacheIfRemoved()
|
||||
{
|
||||
_distributedCache.Setup(x => x.Get("some_key_3")).Returns(Encoding.UTF8.GetBytes("3"));
|
||||
_staticCacheManager.Get(new CacheKey("some_key_3"),()=> 3);
|
||||
await _staticCacheManager.RemoveByPrefixAsync("some_key_3");
|
||||
_distributedCache.Verify(x=> x.RemoveAsync("some_key_3", It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue