Initial commit
This commit is contained in:
commit
2311842089
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
|
||||||
|
<Options Language="Neutral">
|
||||||
|
<Groups />
|
||||||
|
</Options>
|
||||||
|
</Root>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[config]
|
||||||
|
command = deploy.cmd
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
#################
|
||||||
|
## Visual Studio
|
||||||
|
#################
|
||||||
|
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
|
||||||
|
.vs/
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# Build scripts folder
|
||||||
|
[Aa]pp[Bb]uild/
|
||||||
|
[Oo]ut/
|
||||||
|
|
||||||
|
# Visual Studio Code files
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.log
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.Publish.xml
|
||||||
|
*.pubxml
|
||||||
|
|
||||||
|
#Launch settings
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
# NuGet Packages Directory
|
||||||
|
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||||
|
#packages/
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
sql/
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
App_Data/*.mdf
|
||||||
|
App_Data/*.ldf
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Windows detritus
|
||||||
|
#############
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Mac crap
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
|
||||||
|
#######################
|
||||||
|
## nopCommerce specific
|
||||||
|
###########
|
||||||
|
glob:*.user
|
||||||
|
*.patch
|
||||||
|
*.hg
|
||||||
|
Presentation/Nop.Web/.config/*
|
||||||
|
Presentation/Nop.Web/App_Data/Gmail/AuthStore/*
|
||||||
|
!Presentation/Nop.Web/App_Data/Gmail/AuthStore/Index.htm
|
||||||
|
Presentation/Nop.Web/App_Data/browscap.xml
|
||||||
|
Presentation/Nop.Web/App_Data/additional.crawlers.xml
|
||||||
|
Presentation/Nop.Web/App_Data/installedPlugins.json
|
||||||
|
Presentation/Nop.Web/App_Data/plugins.json
|
||||||
|
Presentation/Nop.Web/App_Data/dataSettings.json
|
||||||
|
Presentation/Nop.Web/App_Data/appsettings.json
|
||||||
|
Presentation/Nop.Web/App_Data/TempUploads/*
|
||||||
|
!Presentation/Nop.Web/App_Data/TempUploads/Index.htm
|
||||||
|
Presentation/Nop.Web/App_Data/DataProtectionKeys/*
|
||||||
|
!Presentation/Nop.Web/App_Data/DataProtectionKeys/Index.htm
|
||||||
|
Presentation/Nop.Web/node_modules/*
|
||||||
|
Presentation/Nop.Web/Plugins/*
|
||||||
|
Presentation/Nop.Web/[Bb]in/*
|
||||||
|
Presentation/Nop.Web/[Oo]bj/*
|
||||||
|
Presentation/Nop.Web.FrameWork/[Bb]in/*
|
||||||
|
Presentation/Nop.Web.FrameWork/[Oo]bj/*
|
||||||
|
!Presentation/Nop.Web/Plugins/Uploaded
|
||||||
|
Presentation/Nop.Web/Plugins/Uploaded/*
|
||||||
|
!Presentation/Nop.Web/Plugins/Uploaded/placeholder.txt
|
||||||
|
Presentation/Nop.Web/wwwroot/.well-known/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/.well-known/Index.htm
|
||||||
|
Presentation/Nop.Web/wwwroot/bundles/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/bundles/Index.htm
|
||||||
|
Presentation/Nop.Web/wwwroot/db_backups/*.bak
|
||||||
|
!Presentation/Nop.Web/wwwroot/db_backups/Index.htm
|
||||||
|
Presentation/Nop.Web/wwwroot/images/thumbs/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/images/thumbs/placeholder.txt
|
||||||
|
Presentation/Nop.Web/wwwroot/images/uploaded/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/images/uploaded/placeholder.txt
|
||||||
|
Presentation/Nop.Web/wwwroot/files/exportimport/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/files/exportimport/Index.htm
|
||||||
|
Presentation/Nop.Web/wwwroot/lib_npm/cldr-data/main/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/lib_npm/cldr-data/main/en/
|
||||||
|
!Presentation/Nop.Web/wwwroot/lib_npm/cldr-data/main/main.zip
|
||||||
|
Presentation/Nop.Web/wwwroot/sitemaps/*
|
||||||
|
!Presentation/Nop.Web/wwwroot/sitemaps/Index.htm
|
||||||
|
packages/*
|
||||||
|
Tests/Nop.Tests/Connections.resx
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Copyright>Copyright © Nop Solutions, Ltd</Copyright>
|
||||||
|
<Company>Nop Solutions, Ltd</Company>
|
||||||
|
<Authors>Nop Solutions, Ltd</Authors>
|
||||||
|
<PackageLicenseUrl>https://www.nopcommerce.com/license</PackageLicenseUrl>
|
||||||
|
<PackageProjectUrl>http://www.nopcommerce.com/</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
|
||||||
|
<RepositoryType>Git</RepositoryType>
|
||||||
|
|
||||||
|
<OutputPath>$(MSBuildProjectDirectory)\..\Presentation\Nop.Web\bin\$(Configuration)</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="NopClear">
|
||||||
|
|
||||||
|
<!-- When .NET Core builds a project, it copies all referenced libraries to the output folder.
|
||||||
|
For plugins it creates too many unnecessary files that just take up space.
|
||||||
|
At the moment you can't disable this behavior. That's why we have to manually delete all unnecessary libraries from plugin output directories. -->
|
||||||
|
<Exec Command='dotnet "ClearPluginAssemblies.dll" "OutputPath=$(OutputPath)|PluginPath=$(PluginPath)|SaveLocalesFolders=$(SaveLocalesFolders)"' />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net9.0",
|
||||||
|
"framework": {
|
||||||
|
"name": "Microsoft.NETCore.App",
|
||||||
|
"version": "9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
When .NET Core builds a project, it copies all referenced libraries to the output folder.
|
||||||
|
For plugins it creates too many unnecessary files that just take up space.
|
||||||
|
At the moment you can't disable this behavior. That's why we have to manually delete all unnecessary libraries from plugin output directories.
|
||||||
|
|
||||||
|
ClearPluginAssemblies.proj file contains preparation steps for delete unnecessary files.
|
||||||
|
ClearPluginAssemblies.dll file contains the code for deleting unnecessary files. This file is compiled from the project code from the src directory.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.26730.12
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClearPluginAssemblies", "ClearPluginAssemblies\ClearPluginAssemblies.csproj", "{98B26C68-16E7-4545-ACE7-1A6FBBFC2E92}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{98B26C68-16E7-4545-ACE7-1A6FBBFC2E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{98B26C68-16E7-4545-ACE7-1A6FBBFC2E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{98B26C68-16E7-4545-ACE7-1A6FBBFC2E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{98B26C68-16E7-4545-ACE7-1A6FBBFC2E92}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {3E029116-51FD-4D73-AA78-4E26C730BFEB}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ApplicationIcon />
|
||||||
|
<StartupObject />
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
namespace ClearPluginAssemblies;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
protected const string FILES_TO_DELETE = "dotnet-bundle.exe;Nop.Web.pdb;Nop.Web.exe;Nop.Web.exe.config";
|
||||||
|
|
||||||
|
protected static void Clear(string paths, IList<string> fileNames, bool saveLocalesFolders)
|
||||||
|
{
|
||||||
|
foreach (var pluginPath in paths.Split(';'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pluginDirectoryInfo = new DirectoryInfo(pluginPath);
|
||||||
|
var allDirectoryInfo = new List<DirectoryInfo> { pluginDirectoryInfo };
|
||||||
|
|
||||||
|
if (!saveLocalesFolders)
|
||||||
|
allDirectoryInfo.AddRange(pluginDirectoryInfo.GetDirectories());
|
||||||
|
|
||||||
|
foreach (var directoryInfo in allDirectoryInfo)
|
||||||
|
{
|
||||||
|
foreach (var fileName in fileNames)
|
||||||
|
{
|
||||||
|
//delete dll file if it exists in current path
|
||||||
|
var dllfilePath = Path.Combine(directoryInfo.FullName, fileName + ".dll");
|
||||||
|
if (File.Exists(dllfilePath))
|
||||||
|
File.Delete(dllfilePath);
|
||||||
|
//delete pdb file if it exists in current path
|
||||||
|
var pdbfilePath = Path.Combine(directoryInfo.FullName, fileName + ".pdb");
|
||||||
|
if (File.Exists(pdbfilePath))
|
||||||
|
File.Delete(pdbfilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fileName in FILES_TO_DELETE.Split(';'))
|
||||||
|
{
|
||||||
|
//delete file if it exists in current path
|
||||||
|
var pdbfilePath = Path.Combine(directoryInfo.FullName, fileName);
|
||||||
|
if (File.Exists(pdbfilePath))
|
||||||
|
File.Delete(pdbfilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (directoryInfo.GetFiles().Length == 0 && directoryInfo.GetDirectories().Length == 0 && !saveLocalesFolders)
|
||||||
|
directoryInfo.Delete(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var outputPath = string.Empty;
|
||||||
|
var pluginPaths = string.Empty;
|
||||||
|
var saveLocalesFolders = true;
|
||||||
|
|
||||||
|
var settings = args.FirstOrDefault(a => a.Contains('|')) ?? string.Empty;
|
||||||
|
if(string.IsNullOrEmpty(settings))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var arg in settings.Split('|'))
|
||||||
|
{
|
||||||
|
var data = arg.Split("=").Select(p => p.Trim()).ToList();
|
||||||
|
|
||||||
|
var name = data[0];
|
||||||
|
var value = data.Count > 1 ? data[1] : string.Empty;
|
||||||
|
|
||||||
|
switch (name)
|
||||||
|
{
|
||||||
|
case "OutputPath":
|
||||||
|
outputPath = value;
|
||||||
|
break;
|
||||||
|
case "PluginPath":
|
||||||
|
pluginPaths = value;
|
||||||
|
break;
|
||||||
|
case "SaveLocalesFolders":
|
||||||
|
_ = bool.TryParse(value, out saveLocalesFolders);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Directory.Exists(outputPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var di = new DirectoryInfo(outputPath);
|
||||||
|
var separator = Path.DirectorySeparatorChar;
|
||||||
|
var folderToIgnore = string.Concat(separator, "Plugins", separator);
|
||||||
|
var fileNames = di.GetFiles("*.dll", SearchOption.AllDirectories)
|
||||||
|
.Where(fi => !fi.FullName.Contains(folderToIgnore))
|
||||||
|
.Select(fi => fi.Name.Replace(fi.Extension, "")).ToList();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(pluginPaths) || fileNames.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clear(pluginPaths, fileNames, saveLocalesFolders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
1. Open the solution in Visual Studio
|
||||||
|
2. Re-build the entire solution
|
||||||
|
3. Publish the "Nop.Web" project from Visual Studio
|
||||||
|
|
||||||
|
P.S. When publishing ensure that configuration is set to "Release"
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RepositoryType>Git</RepositoryType>
|
||||||
|
<!--The Implicit usings feature automatically adds common global using directives for the type of project you are building. For more information, see this implicit usings article https://aka.ms/csharp-implicit-usings.-->
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Since we are constantly updating dependent packages, we decided to disable warnings: "A package restored for your project has a known vulnerability" (nu1901-nu1904). For more information, see this article https://learn.microsoft.com/nuget/reference/errors-and-warnings/nu1901-nu1904-->
|
||||||
|
<NoWarn>NU1901;NU1902;NU1903;NU1904</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Nop.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base class for entities
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the entity identifier
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents key for caching objects
|
||||||
|
/// </summary>
|
||||||
|
public partial class CacheKey
|
||||||
|
{
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new instance with key and prefixes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key</param>
|
||||||
|
/// <param name="prefixes">Prefixes for remove by prefix functionality</param>
|
||||||
|
public CacheKey(string key, params string[] prefixes)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Prefixes.AddRange(prefixes.Where(prefix => !string.IsNullOrEmpty(prefix)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance from the current one and fill it with passed parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="createCacheKeyParameters">Function to create parameters</param>
|
||||||
|
/// <param name="keyObjects">Objects to create parameters</param>
|
||||||
|
/// <returns>Cache key</returns>
|
||||||
|
public virtual CacheKey Create(Func<object, object> createCacheKeyParameters, params object[] keyObjects)
|
||||||
|
{
|
||||||
|
var cacheKey = new CacheKey(Key, Prefixes.ToArray());
|
||||||
|
|
||||||
|
if (!keyObjects.Any())
|
||||||
|
return cacheKey;
|
||||||
|
|
||||||
|
cacheKey.Key = string.Format(cacheKey.Key, keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||||
|
|
||||||
|
for (var i = 0; i < cacheKey.Prefixes.Count; i++)
|
||||||
|
cacheKey.Prefixes[i] = string.Format(cacheKey.Prefixes[i], keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||||
|
|
||||||
|
return cacheKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a cache key
|
||||||
|
/// </summary>
|
||||||
|
public string Key { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets prefixes for remove by prefix functionality
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Prefixes { get; protected set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a cache time in minutes
|
||||||
|
/// </summary>
|
||||||
|
public int CacheTime { get; set; } = Singleton<AppSettings>.Instance.Get<CacheConfig>().DefaultCacheTime;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cache key manager
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class should be registered on IoC as singleton instance
|
||||||
|
/// </remarks>
|
||||||
|
public partial class CacheKeyManager : ICacheKeyManager
|
||||||
|
{
|
||||||
|
protected readonly IConcurrentCollection<byte> _keys;
|
||||||
|
|
||||||
|
public CacheKeyManager(IConcurrentCollection<byte> keys)
|
||||||
|
{
|
||||||
|
_keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to add</param>
|
||||||
|
public void AddKey(string key)
|
||||||
|
{
|
||||||
|
_keys.Add(key, default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to remove</param>
|
||||||
|
public void RemoveKey(string key)
|
||||||
|
{
|
||||||
|
_keys.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove all keys
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_keys.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove keys by prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Prefix to delete keys</param>
|
||||||
|
/// <returns>The list of removed keys</returns>
|
||||||
|
public IEnumerable<string> RemoveByPrefix(string prefix)
|
||||||
|
{
|
||||||
|
if (!_keys.Prune(prefix, out var subtree) || subtree?.Keys == null)
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
return subtree.Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of keys
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> Keys => _keys.Keys;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the default cache key service implementation
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class CacheKeyService : ICacheKeyService
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected readonly AppSettings _appSettings;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
protected CacheKeyService(AppSettings appSettings)
|
||||||
|
{
|
||||||
|
_appSettings = appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare the cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
protected virtual string PrepareKeyPrefix(string prefix, params object[] prefixParameters)
|
||||||
|
{
|
||||||
|
return prefixParameters?.Any() ?? false
|
||||||
|
? string.Format(prefix, prefixParameters.Select(CreateCacheKeyParameters).ToArray())
|
||||||
|
: prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the hash value of the passed identifiers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ids">Collection of identifiers</param>
|
||||||
|
/// <returns>String hash value</returns>
|
||||||
|
protected virtual string CreateIdsHash(IEnumerable<int> ids)
|
||||||
|
{
|
||||||
|
var identifiers = ids.ToList();
|
||||||
|
|
||||||
|
if (!identifiers.Any())
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var identifiersString = string.Join(", ", identifiers.OrderBy(id => id));
|
||||||
|
return HashHelper.CreateHash(Encoding.UTF8.GetBytes(identifiersString), HashAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an object to cache parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">Object to convert</param>
|
||||||
|
/// <returns>Cache parameter</returns>
|
||||||
|
protected virtual object CreateCacheKeyParameters(object parameter)
|
||||||
|
{
|
||||||
|
return parameter switch
|
||||||
|
{
|
||||||
|
null => "null",
|
||||||
|
IEnumerable<int> ids => CreateIdsHash(ids),
|
||||||
|
IEnumerable<BaseEntity> entities => CreateIdsHash(entities.Select(entity => entity.Id)),
|
||||||
|
BaseEntity entity => entity.Id,
|
||||||
|
decimal param => param.ToString(CultureInfo.InvariantCulture),
|
||||||
|
_ => parameter
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of cache key and fills it by passed parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>Cache key</returns>
|
||||||
|
public virtual CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
return cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>Cache key</returns>
|
||||||
|
public virtual CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||||
|
|
||||||
|
key.CacheTime = _appSettings.Get<CacheConfig>().DefaultCacheTime;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an algorithm used to create the hash value of identifiers need to cache
|
||||||
|
/// </summary>
|
||||||
|
protected string HashAlgorithm => "SHA1";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
public static class CachingExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it.
|
||||||
|
/// NOTE: this method is only kept for backwards compatibility: the async overload is preferred!
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="cacheManager">Cache manager</param>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>The cached value associated with the specified key</returns>
|
||||||
|
public static T Get<T>(this IStaticCacheManager cacheManager, CacheKey key, Func<T> acquire)
|
||||||
|
{
|
||||||
|
return cacheManager.GetAsync(key, acquire).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheManager">Cache manager</param>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
public static void RemoveByPrefix(this IStaticCacheManager cacheManager, string prefix, params object[] prefixParameters)
|
||||||
|
{
|
||||||
|
cacheManager.RemoveByPrefixAsync(prefix, prefixParameters).Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
public partial class DistributedCacheLocker : ILocker
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected static readonly string _running = JsonConvert.SerializeObject(TaskStatus.Running);
|
||||||
|
protected readonly IDistributedCache _distributedCache;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public DistributedCacheLocker(IDistributedCache distributedCache)
|
||||||
|
{
|
||||||
|
_distributedCache = distributedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs some asynchronous task with exclusive lock
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resource">The key we are locking on</param>
|
||||||
|
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||||
|
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
public async Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action)
|
||||||
|
{
|
||||||
|
//ensure that lock is acquired
|
||||||
|
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(resource)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _distributedCache.SetStringAsync(resource, resource, new DistributedCacheEntryOptions
|
||||||
|
{
|
||||||
|
AbsoluteExpirationRelativeToNow = expirationTime
|
||||||
|
});
|
||||||
|
|
||||||
|
await action();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//release lock even if action fails
|
||||||
|
await _distributedCache.RemoveAsync(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||||
|
/// others that the task is running and stop them from starting the same task.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the background task</param>
|
||||||
|
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||||
|
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||||
|
/// <param name="action">Asynchronous background task to be performed</param>
|
||||||
|
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// run heartbeat early to minimize risk of multiple execution
|
||||||
|
await _distributedCache.SetStringAsync(
|
||||||
|
key,
|
||||||
|
_running,
|
||||||
|
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime },
|
||||||
|
token: tokenSource.Token);
|
||||||
|
|
||||||
|
await using var timer = new Timer(
|
||||||
|
callback: _ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
var status = _distributedCache.GetString(key);
|
||||||
|
if (!string.IsNullOrEmpty(status) && JsonConvert.DeserializeObject<TaskStatus>(status) ==
|
||||||
|
TaskStatus.Canceled)
|
||||||
|
{
|
||||||
|
tokenSource.Cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_distributedCache.SetString(
|
||||||
|
key,
|
||||||
|
_running,
|
||||||
|
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
|
},
|
||||||
|
state: null,
|
||||||
|
dueTime: 0,
|
||||||
|
period: (int)heartbeatInterval.TotalMilliseconds);
|
||||||
|
|
||||||
|
await action(tokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await _distributedCache.RemoveAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||||
|
/// even if not explicitly canceled.</param>
|
||||||
|
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||||
|
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||||
|
public async Task CancelTaskAsync(string key, TimeSpan expirationTime)
|
||||||
|
{
|
||||||
|
var status = await _distributedCache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(status) &&
|
||||||
|
JsonConvert.DeserializeObject<TaskStatus>(status) != TaskStatus.Canceled)
|
||||||
|
await _distributedCache.SetStringAsync(
|
||||||
|
key,
|
||||||
|
JsonConvert.SerializeObject(TaskStatus.Canceled),
|
||||||
|
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a background task is running.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||||
|
public async Task<bool> IsTaskRunningAsync(string key)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a base distributed cache
|
||||||
|
/// </summary>
|
||||||
|
public abstract class DistributedCacheManager : CacheKeyService, IStaticCacheManager
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the keys known by this nopCommerce instance
|
||||||
|
/// </summary>
|
||||||
|
protected readonly ICacheKeyManager _localKeyManager;
|
||||||
|
protected readonly IDistributedCache _distributedCache;
|
||||||
|
protected readonly IConcurrentCollection<object> _concurrentCollection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds ongoing acquisition tasks, used to avoid duplicating work
|
||||||
|
/// </summary>
|
||||||
|
protected readonly ConcurrentDictionary<string, Lazy<Task<object>>> _ongoing = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
protected DistributedCacheManager(AppSettings appSettings,
|
||||||
|
IDistributedCache distributedCache,
|
||||||
|
ICacheKeyManager cacheKeyManager,
|
||||||
|
IConcurrentCollection<object> concurrentCollection)
|
||||||
|
: base(appSettings)
|
||||||
|
{
|
||||||
|
_distributedCache = distributedCache;
|
||||||
|
_localKeyManager = cacheKeyManager;
|
||||||
|
_concurrentCollection = concurrentCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all data on this instance
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
protected virtual void ClearInstanceData()
|
||||||
|
{
|
||||||
|
_concurrentCollection.Clear();
|
||||||
|
_localKeyManager.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
/// <returns>The removed keys</returns>
|
||||||
|
protected virtual IEnumerable<string> RemoveByPrefixInstanceData(string prefix, params object[] prefixParameters)
|
||||||
|
{
|
||||||
|
var keyPrefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||||
|
_concurrentCollection.Prune(keyPrefix, out _);
|
||||||
|
|
||||||
|
return _localKeyManager.RemoveByPrefix(keyPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare cache entry options for the passed key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <returns>Cache entry options</returns>
|
||||||
|
protected virtual DistributedCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||||
|
{
|
||||||
|
//set expiration time for the passed cache key
|
||||||
|
return new DistributedCacheEntryOptions
|
||||||
|
{
|
||||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the specified key and object to the local cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of cached item</param>
|
||||||
|
/// <param name="value">Value for caching</param>
|
||||||
|
protected virtual void SetLocal(string key, object value)
|
||||||
|
{
|
||||||
|
_concurrentCollection.Add(key, value);
|
||||||
|
_localKeyManager.AddKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
protected virtual void RemoveLocal(string key)
|
||||||
|
{
|
||||||
|
_concurrentCollection.Remove(key);
|
||||||
|
_localKeyManager.RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try get a cached item. If it's not in the cache yet, then return default object
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
protected virtual async Task<(bool isSet, T item)> TryGetItemAsync<T>(string key)
|
||||||
|
{
|
||||||
|
var json = await _distributedCache.GetStringAsync(key);
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(json)
|
||||||
|
? (false, default)
|
||||||
|
: (true, item: JsonConvert.DeserializeObject<T>(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="removeFromInstance">Remove from instance</param>
|
||||||
|
protected virtual async Task RemoveAsync(string key, bool removeFromInstance = true)
|
||||||
|
{
|
||||||
|
_ongoing.TryRemove(key, out _);
|
||||||
|
await _distributedCache.RemoveAsync(key);
|
||||||
|
|
||||||
|
if (!removeFromInstance)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RemoveLocal(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public async Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
await RemoveAsync(PrepareKey(cacheKey, cacheKeyParameters).Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||||
|
{
|
||||||
|
if (_concurrentCollection.TryGetValue(key.Key, out var data))
|
||||||
|
return (T)data;
|
||||||
|
|
||||||
|
var lazy = _ongoing.GetOrAdd(key.Key, _ => new(async () => await acquire(), true));
|
||||||
|
var setTask = Task.CompletedTask;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lazy.IsValueCreated)
|
||||||
|
return (T)await lazy.Value;
|
||||||
|
|
||||||
|
var (isSet, item) = await TryGetItemAsync<T>(key.Key);
|
||||||
|
if (!isSet)
|
||||||
|
{
|
||||||
|
item = (T)await lazy.Value;
|
||||||
|
|
||||||
|
if (key.CacheTime == 0 || item == null)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
setTask = _distributedCache.SetStringAsync(
|
||||||
|
key.Key,
|
||||||
|
JsonConvert.SerializeObject(item),
|
||||||
|
PrepareEntryOptions(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLocal(key.Key, item);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ = setTask.ContinueWith(_ => _ongoing.TryRemove(new KeyValuePair<string, Lazy<Task<object>>>(key.Key, lazy)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
public Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||||
|
{
|
||||||
|
return GetAsync(key, () => Task.FromResult(acquire()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> GetAsync<T>(CacheKey key, T defaultValue = default)
|
||||||
|
{
|
||||||
|
var value = await _distributedCache.GetStringAsync(key.Key);
|
||||||
|
|
||||||
|
return value != null
|
||||||
|
? JsonConvert.DeserializeObject<T>(value)
|
||||||
|
: defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||||
|
/// </returns>
|
||||||
|
public async Task<object> GetAsync(CacheKey key)
|
||||||
|
{
|
||||||
|
return await GetAsync<object>(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the specified key and object to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of cached item</param>
|
||||||
|
/// <param name="data">Value for caching</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public async Task SetAsync<T>(CacheKey key, T data)
|
||||||
|
{
|
||||||
|
if (data == null || (key?.CacheTime ?? 0) <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lazy = new Lazy<Task<object>>(() => Task.FromResult(data as object), true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ongoing.TryAdd(key.Key, lazy);
|
||||||
|
// await the lazy task in order to force value creation instead of directly setting data
|
||||||
|
// this way, other cache manager instances can access it while it is being set
|
||||||
|
SetLocal(key.Key, await lazy.Value);
|
||||||
|
await _distributedCache.SetStringAsync(key.Key, JsonConvert.SerializeObject(data), PrepareEntryOptions(key));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ongoing.TryRemove(new KeyValuePair<string, Lazy<Task<object>>>(key.Key, lazy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public abstract Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all cache data
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public abstract Task ClearAsync();
|
||||||
|
|
||||||
|
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cache key manager
|
||||||
|
/// </summary>
|
||||||
|
public partial interface ICacheKeyManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add the key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to add</param>
|
||||||
|
void AddKey(string key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to remove</param>
|
||||||
|
void RemoveKey(string key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove all keys
|
||||||
|
/// </summary>
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove keys by prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Prefix to delete keys</param>
|
||||||
|
/// <returns>The list of removed keys</returns>
|
||||||
|
IEnumerable<string> RemoveByPrefix(string prefix);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of keys
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<string> Keys { get; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cache key service interface
|
||||||
|
/// </summary>
|
||||||
|
public partial interface ICacheKeyService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of cache key and fills it by passed parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>Cache key</returns>
|
||||||
|
CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>Cache key</returns>
|
||||||
|
CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
public partial interface ILocker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Performs some asynchronous task with exclusive lock
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resource">The key we are locking on</param>
|
||||||
|
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||||
|
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||||
|
/// others that the task is running and stop them from starting the same task.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the background task</param>
|
||||||
|
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||||
|
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||||
|
/// <param name="action">Asynchronous background task to be performed</param>
|
||||||
|
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||||
|
/// even if not explicitly canceled.</param>
|
||||||
|
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||||
|
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||||
|
|
||||||
|
Task CancelTaskAsync(string key, TimeSpan expirationTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a background task is running.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||||
|
Task<bool> IsTaskRunningAsync(string key);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a manager for caching during an HTTP request (short term caching)
|
||||||
|
/// </summary>
|
||||||
|
public partial interface IShortTermCacheManager : ICacheKeyService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
void RemoveByPrefix(string prefix, params object[] prefixParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
void Remove(string cacheKey, params object[] cacheKeyParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// /// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
Task<T> GetAsync<T>(Func<Task<T>> acquire, CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a manager for caching between HTTP requests (long term caching)
|
||||||
|
/// </summary>
|
||||||
|
public partial interface IStaticCacheManager : IDisposable, ICacheKeyService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
Task<T> GetAsync<T>(CacheKey key, Func<T> acquire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, return a default value
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="defaultValue">A default value to return if the key is not present in the cache</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key, or the default value if none was found
|
||||||
|
/// </returns>
|
||||||
|
Task<T> GetAsync<T>(CacheKey key, T defaultValue = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||||
|
/// </returns>
|
||||||
|
Task<object> GetAsync(CacheKey key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the specified key and object to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of cached item</param>
|
||||||
|
/// <param name="data">Value for caching</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
Task SetAsync<T>(CacheKey key, T data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all cache data
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
Task ClearAsync();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a local in-memory cache with distributed synchronization
|
||||||
|
/// </summary>
|
||||||
|
public partial interface ISynchronizedMemoryCache : IMemoryCache
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A distributed cache manager that locks the acquisition task
|
||||||
|
/// </summary>
|
||||||
|
public partial class MemoryCacheLocker : ILocker
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public MemoryCacheLocker(IMemoryCache memoryCache)
|
||||||
|
{
|
||||||
|
_memoryCache = memoryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run action
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the background task</param>
|
||||||
|
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||||
|
/// <param name="action">The action to perform</param>
|
||||||
|
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected virtual async Task<bool> RunAsync(string key, TimeSpan? expirationTime, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||||
|
{
|
||||||
|
var started = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tokenSource = _memoryCache.GetOrCreate(key, entry => new Lazy<CancellationTokenSource>(() =>
|
||||||
|
{
|
||||||
|
entry.AbsoluteExpirationRelativeToNow = expirationTime;
|
||||||
|
entry.SetPriority(CacheItemPriority.NeverRemove);
|
||||||
|
started = true;
|
||||||
|
return cancellationTokenSource ?? new CancellationTokenSource();
|
||||||
|
}, true))?.Value;
|
||||||
|
|
||||||
|
if (tokenSource != null && started)
|
||||||
|
await action(tokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (started)
|
||||||
|
_memoryCache.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs some asynchronous task with exclusive lock
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resource">The key we are locking on</param>
|
||||||
|
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||||
|
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
public async Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action)
|
||||||
|
{
|
||||||
|
return await RunAsync(resource, expirationTime, _ => action());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||||
|
/// others that the task is running and stop them from starting the same task.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the background task</param>
|
||||||
|
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||||
|
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||||
|
/// <param name="action">Asynchronous background task to be performed</param>
|
||||||
|
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||||
|
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||||
|
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||||
|
{
|
||||||
|
// We ignore expirationTime and heartbeatInterval here, as the cache is not shared with other instances,
|
||||||
|
// and will be cleared on system failure anyway. The task is guaranteed to still be running as long as it is in the cache.
|
||||||
|
await RunAsync(key, null, action, cancellationTokenSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||||
|
/// even if not explicitly canceled.</param>
|
||||||
|
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||||
|
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||||
|
public Task CancelTaskAsync(string key, TimeSpan expirationTime)
|
||||||
|
{
|
||||||
|
if (_memoryCache.TryGetValue(key, out Lazy<CancellationTokenSource> tokenSource))
|
||||||
|
tokenSource.Value.Cancel();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a background task is running.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The task's key</param>
|
||||||
|
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||||
|
public Task<bool> IsTaskRunningAsync(string key)
|
||||||
|
{
|
||||||
|
return Task.FromResult(_memoryCache.TryGetValue(key, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,292 @@
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a memory cache manager
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class should be registered on IoC as singleton instance
|
||||||
|
/// </remarks>
|
||||||
|
public partial class MemoryCacheManager : CacheKeyService, IStaticCacheManager
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
// Flag: Has Dispose already been called?
|
||||||
|
protected bool _disposed;
|
||||||
|
|
||||||
|
protected readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the keys known by this nopCommerce instance
|
||||||
|
/// </summary>
|
||||||
|
protected readonly ICacheKeyManager _keyManager;
|
||||||
|
|
||||||
|
protected static CancellationTokenSource _clearToken = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public MemoryCacheManager(AppSettings appSettings, IMemoryCache memoryCache, ICacheKeyManager cacheKeyManager)
|
||||||
|
: base(appSettings)
|
||||||
|
{
|
||||||
|
_memoryCache = memoryCache;
|
||||||
|
_keyManager = cacheKeyManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare cache entry options for the passed key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <returns>Cache entry options</returns>
|
||||||
|
protected virtual MemoryCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||||
|
{
|
||||||
|
//set expiration time for the passed cache key
|
||||||
|
var options = new MemoryCacheEntryOptions
|
||||||
|
{
|
||||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||||
|
};
|
||||||
|
|
||||||
|
//add token to clear cache entries
|
||||||
|
options.AddExpirationToken(new CancellationChangeToken(_clearToken.Token));
|
||||||
|
options.RegisterPostEvictionCallback(OnEviction);
|
||||||
|
_keyManager.AddKey(key.Key);
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The callback method which gets called when a cache entry expires.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the entry being evicted.</param>
|
||||||
|
/// <param name="value">The value of the entry being evicted.</param>
|
||||||
|
/// <param name="reason">The <see cref="EvictionReason"/>.</param>
|
||||||
|
/// <param name="state">The information that was passed when registering the callback.</param>
|
||||||
|
protected virtual void OnEviction(object key, object value, EvictionReason reason, object state)
|
||||||
|
{
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
// we clean up after ourselves elsewhere
|
||||||
|
case EvictionReason.Removed:
|
||||||
|
case EvictionReason.Replaced:
|
||||||
|
case EvictionReason.TokenExpired:
|
||||||
|
break;
|
||||||
|
//if the entry was evicted by the cache itself, we remove the key
|
||||||
|
default:
|
||||||
|
//checks if the eviction callback happens after the item is re-added to the cache to prevent the erroneously removing entry from the key manager
|
||||||
|
if (!_memoryCache.TryGetValue(key, out _))
|
||||||
|
_keyManager.RemoveKey(key as string);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
var key = PrepareKey(cacheKey, cacheKeyParameters).Key;
|
||||||
|
_memoryCache.Remove(key);
|
||||||
|
_keyManager.RemoveKey(key);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||||
|
{
|
||||||
|
if ((key?.CacheTime ?? 0) <= 0)
|
||||||
|
return await acquire();
|
||||||
|
|
||||||
|
var task = _memoryCache.GetOrCreate(
|
||||||
|
key.Key,
|
||||||
|
entry =>
|
||||||
|
{
|
||||||
|
entry.SetOptions(PrepareEntryOptions(key));
|
||||||
|
return new Lazy<Task<T>>(acquire, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = await task!.Value;
|
||||||
|
|
||||||
|
//if a cached function return null, remove it from the cache
|
||||||
|
if (data == null)
|
||||||
|
await RemoveAsync(key);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//if a cached function throws an exception, remove it from the cache
|
||||||
|
await RemoveAsync(key);
|
||||||
|
|
||||||
|
if (ex is NullReferenceException)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, return a default value
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="defaultValue">A default value to return if the key is not present in the cache</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key, or the default value if none was found
|
||||||
|
/// </returns>
|
||||||
|
public async Task<T> GetAsync<T>(CacheKey key, T defaultValue = default)
|
||||||
|
{
|
||||||
|
var value = _memoryCache.Get<Lazy<Task<T>>>(key.Key)?.Value;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return value != null ? await value : defaultValue;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//if a cached function throws an exception, remove it from the cache
|
||||||
|
await RemoveAsync(key);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
public async Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||||
|
{
|
||||||
|
return await GetAsync(key, () => Task.FromResult(acquire()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Cache key</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||||
|
/// </returns>
|
||||||
|
public async Task<object> GetAsync(CacheKey key)
|
||||||
|
{
|
||||||
|
var entry = _memoryCache.Get(key.Key);
|
||||||
|
if (entry == null)
|
||||||
|
return null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (entry.GetType().GetProperty("Value")?.GetValue(entry) is not Task task)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
await task;
|
||||||
|
|
||||||
|
return task.GetType().GetProperty("Result")!.GetValue(task);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//if a cached function throws an exception, remove it from the cache
|
||||||
|
await RemoveAsync(key);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the specified key and object to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of cached item</param>
|
||||||
|
/// <param name="data">Value for caching</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public Task SetAsync<T>(CacheKey key, T data)
|
||||||
|
{
|
||||||
|
if (data != null && (key?.CacheTime ?? 0) > 0)
|
||||||
|
_memoryCache.Set(
|
||||||
|
key.Key,
|
||||||
|
new Lazy<Task<T>>(() => Task.FromResult(data), true),
|
||||||
|
PrepareEntryOptions(key));
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)
|
||||||
|
{
|
||||||
|
foreach (var key in _keyManager.RemoveByPrefix(PrepareKeyPrefix(prefix, prefixParameters)))
|
||||||
|
_memoryCache.Remove(key);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all cache data
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task that represents the asynchronous operation</returns>
|
||||||
|
public Task ClearAsync()
|
||||||
|
{
|
||||||
|
_clearToken.Cancel();
|
||||||
|
_clearToken.Dispose();
|
||||||
|
_clearToken = new CancellationTokenSource();
|
||||||
|
_keyManager.Clear();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected implementation of Dispose pattern.
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
// don't dispose of the MemoryCache, as it is injected
|
||||||
|
_clearToken.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents default values related to caching entities
|
||||||
|
/// </summary>
|
||||||
|
public static partial class NopEntityCacheDefaults<TEntity> where TEntity : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an entity type name used in cache keys
|
||||||
|
/// </summary>
|
||||||
|
public static string EntityTypeName => typeof(TEntity).Name.ToLowerInvariant();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key for caching entity by identifier
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// {0} : entity id
|
||||||
|
/// </remarks>
|
||||||
|
public static CacheKey ByIdCacheKey => new($"Nop.{EntityTypeName}.byid.{{0}}", ByIdPrefix, Prefix);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key for caching entities by identifiers
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// {0} : entity ids
|
||||||
|
/// </remarks>
|
||||||
|
public static CacheKey ByIdsCacheKey => new($"Nop.{EntityTypeName}.byids.{{0}}", ByIdsPrefix, Prefix);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key for caching all entities
|
||||||
|
/// </summary>
|
||||||
|
public static CacheKey AllCacheKey => new($"Nop.{EntityTypeName}.all.", AllPrefix, Prefix);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key pattern to clear cache
|
||||||
|
/// </summary>
|
||||||
|
public static string Prefix => $"Nop.{EntityTypeName}.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key pattern to clear cache
|
||||||
|
/// </summary>
|
||||||
|
public static string ByIdPrefix => $"Nop.{EntityTypeName}.byid.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key pattern to clear cache
|
||||||
|
/// </summary>
|
||||||
|
public static string ByIdsPrefix => $"Nop.{EntityTypeName}.byids.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a key pattern to clear cache
|
||||||
|
/// </summary>
|
||||||
|
public static string AllPrefix => $"Nop.{EntityTypeName}.all.";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a per request cache manager
|
||||||
|
/// </summary>
|
||||||
|
public partial class PerRequestCacheManager : CacheKeyService, IShortTermCacheManager
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected readonly ConcurrentTrie<object> _concurrentCollection;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public PerRequestCacheManager(AppSettings appSettings) : base(appSettings)
|
||||||
|
{
|
||||||
|
_concurrentCollection = new ConcurrentTrie<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of cached item</typeparam>
|
||||||
|
/// /// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||||
|
/// <param name="cacheKey">Initial cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that represents the asynchronous operation
|
||||||
|
/// The task result contains the cached value associated with the specified key
|
||||||
|
/// </returns>
|
||||||
|
public async Task<T> GetAsync<T>(Func<Task<T>> acquire, CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters).Key;
|
||||||
|
|
||||||
|
if (_concurrentCollection.TryGetValue(key, out var data))
|
||||||
|
return (T)data;
|
||||||
|
|
||||||
|
var result = await acquire();
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
_concurrentCollection.Add(key, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove items by cache key prefix
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">Cache key prefix</param>
|
||||||
|
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||||
|
public virtual void RemoveByPrefix(string prefix, params object[] prefixParameters)
|
||||||
|
{
|
||||||
|
var keyPrefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||||
|
_concurrentCollection.Prune(keyPrefix, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the value with the specified key from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cacheKey">Cache key</param>
|
||||||
|
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||||
|
public virtual void Remove(string cacheKey, params object[] cacheKeyParameters)
|
||||||
|
{
|
||||||
|
_concurrentCollection.Remove(PrepareKey(new CacheKey(cacheKey), cacheKeyParameters).Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a memory cache manager with distributed synchronization
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class should be registered on IoC as singleton instance
|
||||||
|
/// </remarks>
|
||||||
|
public partial class SynchronizedMemoryCacheManager : MemoryCacheManager
|
||||||
|
{
|
||||||
|
public SynchronizedMemoryCacheManager(AppSettings appSettings,
|
||||||
|
ISynchronizedMemoryCache memoryCache,
|
||||||
|
ICacheKeyManager cacheKeyManager) : base(appSettings, memoryCache, cacheKeyManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a common helper
|
||||||
|
/// </summary>
|
||||||
|
public partial class CommonHelper
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
//we use regular expression based on RFC 5322 Official Standard (see https://emailregex.com/)
|
||||||
|
private const string EMAIL_EXPRESSION = @"^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|""(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*"")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get email validation regex
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Regular expression</returns>
|
||||||
|
[GeneratedRegex(EMAIL_EXPRESSION, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, "en-US")]
|
||||||
|
public static partial Regex GetEmailRegex();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures the subscriber email or throw.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">The email.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string EnsureSubscriberEmailOrThrow(string email)
|
||||||
|
{
|
||||||
|
var output = EnsureNotNull(email);
|
||||||
|
output = output.Trim();
|
||||||
|
output = EnsureMaximumLength(output, 255);
|
||||||
|
|
||||||
|
if (!IsValidEmail(output))
|
||||||
|
{
|
||||||
|
throw new NopException("Email is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a string is in valid e-mail format
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">Email to verify</param>
|
||||||
|
/// <returns>true if the string is a valid e-mail address and false if it's not</returns>
|
||||||
|
public static bool IsValidEmail(string email)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
email = email.Trim();
|
||||||
|
|
||||||
|
return GetEmailRegex().IsMatch(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that string is an valid IP-Address
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ipAddress">IPAddress to verify</param>
|
||||||
|
/// <returns>true if the string is a valid IpAddress and false if it's not</returns>
|
||||||
|
public static bool IsValidIpAddress(string ipAddress)
|
||||||
|
{
|
||||||
|
return IPAddress.TryParse(ipAddress, out var _);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate random digit code
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">Length</param>
|
||||||
|
/// <returns>Result string</returns>
|
||||||
|
public static string GenerateRandomDigitCode(int length)
|
||||||
|
{
|
||||||
|
using var random = new SecureRandomNumberGenerator();
|
||||||
|
var str = string.Empty;
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
str = string.Concat(str, random.Next(10).ToString());
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an random integer number within a specified rage
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="min">Minimum number</param>
|
||||||
|
/// <param name="max">Maximum number</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue)
|
||||||
|
{
|
||||||
|
using var random = new SecureRandomNumberGenerator();
|
||||||
|
return random.Next(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure that a string doesn't exceed maximum allowed length
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">Input string</param>
|
||||||
|
/// <param name="maxLength">Maximum length</param>
|
||||||
|
/// <param name="postfix">A string to add to the end if the original string was shorten</param>
|
||||||
|
/// <returns>Input string if its length is OK; otherwise, truncated input string</returns>
|
||||||
|
public static string EnsureMaximumLength(string str, int maxLength, string postfix = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
if (str.Length <= maxLength)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
var pLen = postfix?.Length ?? 0;
|
||||||
|
|
||||||
|
var result = str[0..(maxLength - pLen)];
|
||||||
|
if (!string.IsNullOrEmpty(postfix))
|
||||||
|
{
|
||||||
|
result += postfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that a string only contains numeric values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">Input string</param>
|
||||||
|
/// <returns>Input string with only numeric values, empty string if input is null/empty</returns>
|
||||||
|
public static string EnsureNumericOnly(string str)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(str) ? string.Empty : new string(str.Where(char.IsDigit).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure that a string is not null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">Input string</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public static string EnsureNotNull(string str)
|
||||||
|
{
|
||||||
|
return str ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the specified strings are null or empty strings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringsToValidate">Array of strings to validate</param>
|
||||||
|
/// <returns>Boolean</returns>
|
||||||
|
public static bool AreNullOrEmpty(params string[] stringsToValidate)
|
||||||
|
{
|
||||||
|
return stringsToValidate.Any(string.IsNullOrEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare two arrays
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type</typeparam>
|
||||||
|
/// <param name="a1">Array 1</param>
|
||||||
|
/// <param name="a2">Array 2</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public static bool ArraysEqual<T>(T[] a1, T[] a2)
|
||||||
|
{
|
||||||
|
//also see Enumerable.SequenceEqual(a1, a2);
|
||||||
|
if (ReferenceEquals(a1, a2))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a1 == null || a2 == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a1.Length != a2.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var comparer = EqualityComparer<T>.Default;
|
||||||
|
return !a1.Where((t, i) => !comparer.Equals(t, a2[i])).Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a property on an object to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The object whose property to set.</param>
|
||||||
|
/// <param name="propertyName">The name of the property to set.</param>
|
||||||
|
/// <param name="value">The value to set the property to.</param>
|
||||||
|
public static void SetProperty(object instance, string propertyName, object value)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(instance);
|
||||||
|
ArgumentNullException.ThrowIfNull(propertyName);
|
||||||
|
|
||||||
|
var instanceType = instance.GetType();
|
||||||
|
var pi = instanceType.GetProperty(propertyName)
|
||||||
|
?? throw new NopException("No property '{0}' found on the instance of type '{1}'.", propertyName, instanceType);
|
||||||
|
|
||||||
|
if (!pi.CanWrite)
|
||||||
|
throw new NopException("The property '{0}' on the instance of type '{1}' does not have a setter.", propertyName, instanceType);
|
||||||
|
if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType))
|
||||||
|
value = To(value, pi.PropertyType);
|
||||||
|
pi.SetValue(instance, value, Array.Empty<object>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value to a destination type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="destinationType">The type to convert the value to.</param>
|
||||||
|
/// <returns>The converted value.</returns>
|
||||||
|
public static object To(object value, Type destinationType)
|
||||||
|
{
|
||||||
|
return To(value, destinationType, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value to a destination type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="destinationType">The type to convert the value to.</param>
|
||||||
|
/// <param name="culture">Culture</param>
|
||||||
|
/// <returns>The converted value.</returns>
|
||||||
|
public static object To(object value, Type destinationType, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var sourceType = value.GetType();
|
||||||
|
|
||||||
|
var destinationConverter = TypeDescriptor.GetConverter(destinationType);
|
||||||
|
if (destinationConverter.CanConvertFrom(value.GetType()))
|
||||||
|
return destinationConverter.ConvertFrom(null, culture, value);
|
||||||
|
|
||||||
|
var sourceConverter = TypeDescriptor.GetConverter(sourceType);
|
||||||
|
if (sourceConverter.CanConvertTo(destinationType))
|
||||||
|
return sourceConverter.ConvertTo(null, culture, value, destinationType);
|
||||||
|
|
||||||
|
if (destinationType.IsEnum && value is int)
|
||||||
|
return Enum.ToObject(destinationType, (int)value);
|
||||||
|
|
||||||
|
if (!destinationType.IsInstanceOfType(value))
|
||||||
|
return Convert.ChangeType(value, destinationType, culture);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value to a destination type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
||||||
|
/// <returns>The converted value.</returns>
|
||||||
|
public static T To<T>(object value)
|
||||||
|
{
|
||||||
|
//return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
|
||||||
|
return (T)To(value, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Splits the camel-case word into separate one
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">Input string</param>
|
||||||
|
/// <returns>Splitted string</returns>
|
||||||
|
public static string SplitCamelCaseWord(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var result = str.ToCharArray()
|
||||||
|
.Select(p => p.ToString())
|
||||||
|
.Aggregate(string.Empty, (current, c) => current + (c == c.ToUpperInvariant() ? $" {c}" : c));
|
||||||
|
|
||||||
|
//ensure no spaces (e.g. when the first letter is upper case)
|
||||||
|
result = result.TrimStart();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get difference in years
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startDate"></param>
|
||||||
|
/// <param name="endDate"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetDifferenceInYears(DateTime startDate, DateTime endDate)
|
||||||
|
{
|
||||||
|
//source: http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
|
||||||
|
//this assumes you are looking for the western idea of age and not using East Asian reckoning.
|
||||||
|
var age = endDate.Year - startDate.Year;
|
||||||
|
if (startDate > endDate.AddYears(-age))
|
||||||
|
age--;
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get DateTime to the specified year, month, and day using the conventions of the current thread culture
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="year">The year</param>
|
||||||
|
/// <param name="month">The month</param>
|
||||||
|
/// <param name="day">The day</param>
|
||||||
|
/// <returns>An instance of the Nullable<System.DateTime></returns>
|
||||||
|
public static DateTime? ParseDate(int? year, int? month, int? day)
|
||||||
|
{
|
||||||
|
if (!year.HasValue || !month.HasValue || !day.HasValue)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
DateTime? date = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
date = new DateTime(year.Value, month.Value, day.Value, CultureInfo.CurrentCulture.Calendar);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default file provider
|
||||||
|
/// </summary>
|
||||||
|
public static INopFileProvider DefaultFileProvider { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Nop.Core.ComponentModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generic Dictionary type converted
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="K">Key type (simple)</typeparam>
|
||||||
|
/// <typeparam name="V">Value type (simple)</typeparam>
|
||||||
|
public partial class GenericDictionaryTypeConverter<K, V> : TypeConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Type converter
|
||||||
|
/// </summary>
|
||||||
|
protected readonly TypeConverter _typeConverterKey;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type converter
|
||||||
|
/// </summary>
|
||||||
|
protected readonly TypeConverter _typeConverterValue;
|
||||||
|
|
||||||
|
public GenericDictionaryTypeConverter()
|
||||||
|
{
|
||||||
|
_typeConverterKey = TypeDescriptor.GetConverter(typeof(K));
|
||||||
|
if (_typeConverterKey == null)
|
||||||
|
throw new InvalidOperationException("No type converter exists for type " + typeof(K).FullName);
|
||||||
|
_typeConverterValue = TypeDescriptor.GetConverter(typeof(V));
|
||||||
|
if (_typeConverterValue == null)
|
||||||
|
throw new InvalidOperationException("No type converter exists for type " + typeof(V).FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this converter can
|
||||||
|
/// convert an object in the given source type to the native type of the converter
|
||||||
|
/// using the context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="sourceType">Source type</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||||
|
{
|
||||||
|
if (sourceType == typeof(string))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return base.CanConvertFrom(context, sourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the given object to the converter's native type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="culture">Culture</param>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||||
|
{
|
||||||
|
if (value is not string)
|
||||||
|
return base.ConvertFrom(context, culture, value);
|
||||||
|
|
||||||
|
var input = (string)value;
|
||||||
|
var items = string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(';').Select(x => x.Trim()).ToArray();
|
||||||
|
|
||||||
|
var result = new Dictionary<K, V>();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
var keyValueStr = string.IsNullOrEmpty(item) ? Array.Empty<string>() : item.Split(',').Select(x => x.Trim()).ToArray();
|
||||||
|
if (keyValueStr.Length != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
object dictionaryKey = (K)_typeConverterKey.ConvertFromInvariantString(keyValueStr[0]);
|
||||||
|
object dictionaryValue = (V)_typeConverterValue.ConvertFromInvariantString(keyValueStr[1]);
|
||||||
|
if (dictionaryKey == null || dictionaryValue == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!result.ContainsKey((K)dictionaryKey))
|
||||||
|
result.Add((K)dictionaryKey, (V)dictionaryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="culture">Culture</param>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
/// <param name="destinationType">Destination type</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||||
|
{
|
||||||
|
if (destinationType != typeof(string))
|
||||||
|
return base.ConvertTo(context, culture, value, destinationType);
|
||||||
|
|
||||||
|
var result = string.Empty;
|
||||||
|
if (value == null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
//we don't use string.Join() because it doesn't support invariant culture
|
||||||
|
var counter = 0;
|
||||||
|
var dictionary = (IDictionary<K, V>)value;
|
||||||
|
foreach (var keyValue in dictionary)
|
||||||
|
{
|
||||||
|
result += $"{Convert.ToString(keyValue.Key, CultureInfo.InvariantCulture)}, {Convert.ToString(keyValue.Value, CultureInfo.InvariantCulture)}";
|
||||||
|
//don't add ; after the last element
|
||||||
|
if (counter != dictionary.Count - 1)
|
||||||
|
result += ";";
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Nop.Core.ComponentModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generic List type converted
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type</typeparam>
|
||||||
|
public partial class GenericListTypeConverter<T> : TypeConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Type converter
|
||||||
|
/// </summary>
|
||||||
|
protected readonly TypeConverter typeConverter;
|
||||||
|
|
||||||
|
public GenericListTypeConverter()
|
||||||
|
{
|
||||||
|
typeConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||||
|
if (typeConverter == null)
|
||||||
|
throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get string array from a comma-separate string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Input</param>
|
||||||
|
/// <returns>Array</returns>
|
||||||
|
protected virtual string[] GetStringArray(string input)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(',').Select(x => x.Trim()).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this converter can
|
||||||
|
/// convert an object in the given source type to the native type of the converter
|
||||||
|
/// using the context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="sourceType">Source type</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||||
|
{
|
||||||
|
if (sourceType != typeof(string))
|
||||||
|
return base.CanConvertFrom(context, sourceType);
|
||||||
|
|
||||||
|
var items = GetStringArray(sourceType.ToString());
|
||||||
|
return items.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the given object to the converter's native type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="culture">Culture</param>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||||
|
{
|
||||||
|
if (value is not string && value != null)
|
||||||
|
return base.ConvertFrom(context, culture, value);
|
||||||
|
|
||||||
|
var items = GetStringArray((string)value);
|
||||||
|
|
||||||
|
return items.Select(typeConverter.ConvertFromInvariantString)
|
||||||
|
.Where(item => item != null)
|
||||||
|
.Cast<T>()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context</param>
|
||||||
|
/// <param name="culture">Culture</param>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
/// <param name="destinationType">Destination type</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||||
|
{
|
||||||
|
if (destinationType != typeof(string))
|
||||||
|
return base.ConvertTo(context, culture, value, destinationType);
|
||||||
|
|
||||||
|
var result = string.Empty;
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
var cultureInvariantStrings = ((IList<T>)value)
|
||||||
|
.Select(o => Convert.ToString(o, CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
result = string.Join(',', cultureInvariantStrings);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
namespace Nop.Core.ComponentModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a convenience methodology for implementing locked access to resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Intended as an infrastructure class.
|
||||||
|
/// </remarks>
|
||||||
|
public partial class ReaderWriteLockDisposable : IDisposable
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected bool _disposed;
|
||||||
|
protected readonly ReaderWriterLockSlim _rwLock;
|
||||||
|
protected readonly ReaderWriteLockType _readerWriteLockType;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ReaderWriteLockDisposable"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rwLock">The readers–writer lock</param>
|
||||||
|
/// <param name="readerWriteLockType">Lock type</param>
|
||||||
|
public ReaderWriteLockDisposable(ReaderWriterLockSlim rwLock, ReaderWriteLockType readerWriteLockType = ReaderWriteLockType.Write)
|
||||||
|
{
|
||||||
|
_rwLock = rwLock;
|
||||||
|
_readerWriteLockType = readerWriteLockType;
|
||||||
|
|
||||||
|
switch (_readerWriteLockType)
|
||||||
|
{
|
||||||
|
case ReaderWriteLockType.Read:
|
||||||
|
_rwLock.EnterReadLock();
|
||||||
|
break;
|
||||||
|
case ReaderWriteLockType.Write:
|
||||||
|
_rwLock.EnterWriteLock();
|
||||||
|
break;
|
||||||
|
case ReaderWriteLockType.UpgradeableRead:
|
||||||
|
_rwLock.EnterUpgradeableReadLock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Protected implementation of Dispose pattern.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">Specifies whether to disposing resources</param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
switch (_readerWriteLockType)
|
||||||
|
{
|
||||||
|
case ReaderWriteLockType.Read:
|
||||||
|
_rwLock.ExitReadLock();
|
||||||
|
break;
|
||||||
|
case ReaderWriteLockType.Write:
|
||||||
|
_rwLock.ExitWriteLock();
|
||||||
|
break;
|
||||||
|
case ReaderWriteLockType.UpgradeableRead:
|
||||||
|
_rwLock.ExitUpgradeableReadLock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public implementation of Dispose pattern callable by consumers.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Nop.Core.ComponentModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reader/Write locker type
|
||||||
|
/// </summary>
|
||||||
|
public enum ReaderWriteLockType
|
||||||
|
{
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
UpgradeableRead
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the app settings
|
||||||
|
/// </summary>
|
||||||
|
public partial class AppSettings
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected readonly Dictionary<Type, IConfig> _configurations;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public AppSettings(IList<IConfig> configurations = null)
|
||||||
|
{
|
||||||
|
_configurations = configurations
|
||||||
|
?.OrderBy(config => config.GetOrder())
|
||||||
|
?.ToDictionary(config => config.GetType(), config => config)
|
||||||
|
?? new Dictionary<Type, IConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get configuration parameters by type
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TConfig">Configuration type</typeparam>
|
||||||
|
/// <returns>Configuration parameters</returns>
|
||||||
|
public TConfig Get<TConfig>() where TConfig : class, IConfig
|
||||||
|
{
|
||||||
|
if (_configurations[typeof(TConfig)] is not TConfig config)
|
||||||
|
throw new NopException($"No configuration with type '{typeof(TConfig)}' found");
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update app settings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configurations">Configurations to update</param>
|
||||||
|
public void Update(IList<IConfig> configurations)
|
||||||
|
{
|
||||||
|
foreach (var config in configurations)
|
||||||
|
{
|
||||||
|
_configurations[config.GetType()] = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets raw configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
[JsonExtensionData]
|
||||||
|
public Dictionary<string, JToken> Configuration { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Nop.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the app settings helper
|
||||||
|
/// </summary>
|
||||||
|
public partial class AppSettingsHelper
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
protected static Dictionary<string, int> _configurationOrder;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create app settings with the passed configurations and save it to the file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configurations">Configurations to save</param>
|
||||||
|
/// <param name="fileProvider">File provider</param>
|
||||||
|
/// <param name="overwrite">Whether to overwrite appsettings file</param>
|
||||||
|
/// <returns>App settings</returns>
|
||||||
|
public static AppSettings SaveAppSettings(IList<IConfig> configurations, INopFileProvider fileProvider, bool overwrite = true)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(configurations);
|
||||||
|
|
||||||
|
_configurationOrder = configurations.ToDictionary(config => config.Name, config => config.GetOrder());
|
||||||
|
|
||||||
|
//create app settings
|
||||||
|
var appSettings = Singleton<AppSettings>.Instance ?? new AppSettings();
|
||||||
|
appSettings.Update(configurations);
|
||||||
|
Singleton<AppSettings>.Instance = appSettings;
|
||||||
|
|
||||||
|
//create file if not exists
|
||||||
|
var filePath = fileProvider.MapPath(NopConfigurationDefaults.AppSettingsFilePath);
|
||||||
|
var fileExists = fileProvider.FileExists(filePath);
|
||||||
|
fileProvider.CreateFile(filePath);
|
||||||
|
|
||||||
|
//get raw configuration parameters
|
||||||
|
var configuration = JsonConvert.DeserializeObject<AppSettings>(fileProvider.ReadAllText(filePath, Encoding.UTF8))
|
||||||
|
?.Configuration
|
||||||
|
?? new();
|
||||||
|
foreach (var config in configurations)
|
||||||
|
{
|
||||||
|
configuration[config.Name] = JToken.FromObject(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort configurations for display by order (e.g. data configuration with 0 will be the first)
|
||||||
|
appSettings.Configuration = configuration
|
||||||
|
.SelectMany(outConfig => _configurationOrder.Where(inConfig => inConfig.Key == outConfig.Key).DefaultIfEmpty(),
|
||||||
|
(outConfig, inConfig) => new { OutConfig = outConfig, InConfig = inConfig })
|
||||||
|
.OrderBy(config => config.InConfig.Value)
|
||||||
|
.Select(config => config.OutConfig)
|
||||||
|
.ToDictionary(config => config.Key, config => config.Value);
|
||||||
|
|
||||||
|
//save app settings to the file
|
||||||
|
if (!fileExists || overwrite)
|
||||||
|
{
|
||||||
|
var text = JsonConvert.SerializeObject(appSettings, Formatting.Indented);
|
||||||
|
fileProvider.WriteAllText(filePath, text, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the event that is raised when App Settings are saving
|
||||||
|
/// </summary>
|
||||||
|
public partial class AppSettingsSavingEvent
|
||||||
|
{
|
||||||
|
#region Ctor
|
||||||
|
|
||||||
|
public AppSettingsSavingEvent(IList<IConfig> configurations)
|
||||||
|
{
|
||||||
|
Configurations = configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add configuration to save
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">Configuration to save</param>
|
||||||
|
public void AddConfig<TConfig>(TConfig config) where TConfig : class, IConfig
|
||||||
|
{
|
||||||
|
if (Configurations.OfType<TConfig>().FirstOrDefault() is TConfig currentConfig)
|
||||||
|
Configurations[Configurations.IndexOf(currentConfig)] = config;
|
||||||
|
else
|
||||||
|
Configurations.Add(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets configurations to save
|
||||||
|
/// </summary>
|
||||||
|
public IList<IConfig> Configurations { get; protected set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents Azure Blob storage configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class AzureBlobConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets connection string for Azure Blob storage
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectionString { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets container name for Azure Blob storage
|
||||||
|
/// </summary>
|
||||||
|
public string ContainerName { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets end point for Azure Blob storage
|
||||||
|
/// </summary>
|
||||||
|
public string EndPoint { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether or the Container Name is appended to the AzureBlobStorageEndPoint when constructing the url
|
||||||
|
/// </summary>
|
||||||
|
public bool AppendContainerName { get; protected set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to store Data Protection Keys in Azure Blob Storage
|
||||||
|
/// </summary>
|
||||||
|
public bool StoreDataProtectionKeys { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Azure container name for storing Data Prtection Keys (this container should be separate from the container used for media and should be Private)
|
||||||
|
/// </summary>
|
||||||
|
public string DataProtectionKeysContainerName { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Azure key vault ID used to encrypt the Data Protection Keys. (this is optional)
|
||||||
|
/// </summary>
|
||||||
|
public string DataProtectionKeysVaultId { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether we should use Azure Blob storage
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool Enabled => !string.IsNullOrEmpty(ConnectionString);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to use an Azure Key Vault to encrypt the Data Protection Keys
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool DataProtectionKeysEncryptWithVault => !string.IsNullOrEmpty(DataProtectionKeysVaultId);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents cache configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class CacheConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default cache time in minutes
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultCacheTime { get; protected set; } = 60;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to disable linq2db query cache
|
||||||
|
/// </summary>
|
||||||
|
public bool LinqDisableQueryCache { get; protected set; } = false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents common configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class CommonConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display the full error in production environment. It's ignored (always enabled) in development environment
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayFullErrorStack { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets path to database with user agent strings
|
||||||
|
/// </summary>
|
||||||
|
public string UserAgentStringsPath { get; protected set; } = "~/App_Data/browscap.xml";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets path to database with crawler only user agent strings
|
||||||
|
/// </summary>
|
||||||
|
public string CrawlerOnlyUserAgentStringsPath { get; protected set; } = "~/App_Data/browscap.crawlersonly.xml";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets path to additional database with crawler only user agent strings
|
||||||
|
/// </summary>
|
||||||
|
public string CrawlerOnlyAdditionalUserAgentStringsPath { get; protected set; } = "~/App_Data/additional.crawlers.xml";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to store TempData in the session state. By default the cookie-based TempData provider is used to store TempData in cookies.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseSessionStateTempDataProvider { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length of time, in milliseconds, before the running schedule task times out. Set null to use default value
|
||||||
|
/// </summary>
|
||||||
|
public int? ScheduleTaskRunTimeout { get; protected set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value of "Cache-Control" header value for static content (in seconds)
|
||||||
|
/// </summary>
|
||||||
|
public string StaticFilesCacheControl { get; protected set; } = "public,max-age=31536000";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the blacklist of static file extension for plugin directories
|
||||||
|
/// </summary>
|
||||||
|
public string PluginStaticFileExtensionsBlacklist { get; protected set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether to serve files that don't have a recognized content-type
|
||||||
|
/// </summary>
|
||||||
|
public bool ServeUnknownFileTypes { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether to use Autofac IoC container
|
||||||
|
///
|
||||||
|
/// If value is set to false then the default .Net IoC container will be use
|
||||||
|
/// </summary>
|
||||||
|
public bool UseAutofac { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum number of permit counters that can be allowed in a window (1 minute).
|
||||||
|
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
|
||||||
|
/// If set to 0 than limitation is off
|
||||||
|
/// </summary>
|
||||||
|
public int PermitLimit { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum cumulative permit count of queued acquisition requests.
|
||||||
|
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
|
||||||
|
/// If set to 0 than Queue is off
|
||||||
|
/// </summary>
|
||||||
|
public int QueueCount { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default status code to set on the response when a request is rejected.
|
||||||
|
/// </summary>
|
||||||
|
public int RejectionStatusCode { get; set; } = 503;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents distributed cache configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class DistributedCacheConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a distributed cache type
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DistributedCacheType DistributedCacheType { get; protected set; } = DistributedCacheType.RedisSynchronizedMemory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether we should use distributed cache
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets connection string. Used when distributed cache is enabled
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectionString { get; protected set; } = "127.0.0.1:6379,ssl=False";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets schema name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||||
|
/// </summary>
|
||||||
|
public string SchemaName { get; protected set; } = "dbo";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets table name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||||
|
/// </summary>
|
||||||
|
public string TableName { get; protected set; } = "DistributedCache";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets instance name. Used when distributed cache is enabled and DistributedCacheType property is set as Redis or RedisSynchronizedMemory.
|
||||||
|
/// Useful when one wants to partition a single Redis server for use with multiple apps, e.g. by setting InstanceName to "development" and "production".
|
||||||
|
/// </summary>
|
||||||
|
public string InstanceName { get; protected set; } = "nopCommerce";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Redis event publish interval in milliseconds.
|
||||||
|
/// Used when distributed cache is enabled and DistributedCacheType property is set as RedisSynchronizedMemory.
|
||||||
|
/// If greater than zero, events will be buffered for this long before being published in batch, in order to reduce server load.
|
||||||
|
/// If zero, events are published when they are raised, without buffering.
|
||||||
|
/// </summary>
|
||||||
|
public int PublishIntervalMs { get; protected set; } = 500;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents distributed cache types enumeration
|
||||||
|
/// </summary>
|
||||||
|
public enum DistributedCacheType
|
||||||
|
{
|
||||||
|
[EnumMember(Value = "memory")]
|
||||||
|
Memory,
|
||||||
|
[EnumMember(Value = "sqlserver")]
|
||||||
|
SqlServer,
|
||||||
|
[EnumMember(Value = "redis")]
|
||||||
|
Redis,
|
||||||
|
[EnumMember(Value = "redissynchronizedmemory")]
|
||||||
|
RedisSynchronizedMemory
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents hosting configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class HostingConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to use proxy servers and load balancers
|
||||||
|
/// </summary>
|
||||||
|
public bool UseProxy { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the header used to retrieve the value for the originating scheme (HTTP/HTTPS)
|
||||||
|
/// </summary>
|
||||||
|
public string ForwardedProtoHeaderName { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the header used to retrieve the originating client IP
|
||||||
|
/// </summary>
|
||||||
|
public string ForwardedForHeaderName { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets addresses of known proxies to accept forwarded headers from
|
||||||
|
/// </summary>
|
||||||
|
public string KnownProxies { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets addresses of known networks to accept forwarded headers from
|
||||||
|
/// </summary>
|
||||||
|
public string KnownNetworks { get; protected set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a configuration from app settings
|
||||||
|
/// </summary>
|
||||||
|
public partial interface IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a section name to load configuration
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
string Name => GetType().Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an order of configuration
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Order</returns>
|
||||||
|
public int GetOrder() => 1;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting interface
|
||||||
|
/// </summary>
|
||||||
|
public partial interface ISettings
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents installation configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class InstallationConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether a store owner can install sample data during installation
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableSampleData { get; protected set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a list of plugins ignored during nopCommerce installation
|
||||||
|
/// </summary>
|
||||||
|
public string DisabledPlugins { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to download and setup the regional language pack during installation
|
||||||
|
/// </summary>
|
||||||
|
public bool InstallRegionalResources { get; protected set; } = true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents default values related to configuration services
|
||||||
|
/// </summary>
|
||||||
|
public static partial class NopConfigurationDefaults
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Nop.Core.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents plugin configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
public partial class PluginConfig : IConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to load an assembly into the load-from context, bypassing some security checks.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseUnsafeLoadAssembly { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using Nop.Core.Domain.Common;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Affiliates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an affiliate
|
||||||
|
/// </summary>
|
||||||
|
public partial class Affiliate : BaseEntity, ISoftDeletedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the address identifier
|
||||||
|
/// </summary>
|
||||||
|
public int AddressId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the admin comment
|
||||||
|
/// </summary>
|
||||||
|
public string AdminComment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the friendly name for generated affiliate URL (by default affiliate ID is used)
|
||||||
|
/// </summary>
|
||||||
|
public string FriendlyUrlName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity has been deleted
|
||||||
|
/// </summary>
|
||||||
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is active
|
||||||
|
/// </summary>
|
||||||
|
public bool Active { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
using Nop.Core.Domain.Catalog;
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base class for attributes
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class BaseAttribute : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the attribute is required
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRequired { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute control type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int AttributeControlTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute control type
|
||||||
|
/// </summary>
|
||||||
|
public AttributeControlType AttributeControlType
|
||||||
|
{
|
||||||
|
get => (AttributeControlType)AttributeControlTypeId;
|
||||||
|
set => AttributeControlTypeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value indicating whether this attribute should have values
|
||||||
|
/// </summary>
|
||||||
|
public bool ShouldHaveValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (AttributeControlType == AttributeControlType.TextBox ||
|
||||||
|
AttributeControlType == AttributeControlType.MultilineTextbox ||
|
||||||
|
AttributeControlType == AttributeControlType.Datepicker ||
|
||||||
|
AttributeControlType == AttributeControlType.FileUpload)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//other attribute control types support values
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value indicating whether this attribute can be used as condition for some other attribute
|
||||||
|
/// </summary>
|
||||||
|
public bool CanBeUsedAsCondition
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (AttributeControlType == AttributeControlType.ReadonlyCheckboxes ||
|
||||||
|
AttributeControlType == AttributeControlType.TextBox ||
|
||||||
|
AttributeControlType == AttributeControlType.MultilineTextbox ||
|
||||||
|
AttributeControlType == AttributeControlType.Datepicker ||
|
||||||
|
AttributeControlType == AttributeControlType.FileUpload)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//other attribute control types support it
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base class for attribute values
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class BaseAttributeValue : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the value is pre-selected
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPreSelected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute identifier
|
||||||
|
/// </summary>
|
||||||
|
public int AttributeId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
namespace Nop.Core.Domain.Blogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a blog comment
|
||||||
|
/// </summary>
|
||||||
|
public partial class BlogComment : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the customer identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the comment text
|
||||||
|
/// </summary>
|
||||||
|
public string CommentText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the comment is approved
|
||||||
|
/// </summary>
|
||||||
|
public bool IsApproved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the store identifier
|
||||||
|
/// </summary>
|
||||||
|
public int StoreId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post identifier
|
||||||
|
/// </summary>
|
||||||
|
public int BlogPostId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
using Nop.Core.Domain.Seo;
|
||||||
|
using Nop.Core.Domain.Stores;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Blogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a blog post
|
||||||
|
/// </summary>
|
||||||
|
public partial class BlogPost : BaseEntity, ISlugSupported, IStoreMappingSupported
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the language identifier
|
||||||
|
/// </summary>
|
||||||
|
public int LanguageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value indicating whether this blog post should be included in sitemap
|
||||||
|
/// </summary>
|
||||||
|
public bool IncludeInSitemap { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post title
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post body
|
||||||
|
/// </summary>
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post overview. If specified, then it's used on the blog page instead of the "Body"
|
||||||
|
/// </summary>
|
||||||
|
public string BodyOverview { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the blog post comments are allowed
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowComments { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog tags
|
||||||
|
/// </summary>
|
||||||
|
public string Tags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post start date and time
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? StartDateUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the blog post end date and time
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? EndDateUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta keywords
|
||||||
|
/// </summary>
|
||||||
|
public string MetaKeywords { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta description
|
||||||
|
/// </summary>
|
||||||
|
public string MetaDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta title
|
||||||
|
/// </summary>
|
||||||
|
public string MetaTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool LimitedToStores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of entity creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Blogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a blog post tag
|
||||||
|
/// </summary>
|
||||||
|
public partial class BlogPostTag
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the tagged product count
|
||||||
|
/// </summary>
|
||||||
|
public int BlogPostCount { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Blogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blog settings
|
||||||
|
/// </summary>
|
||||||
|
public partial class BlogSettings : ISettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether blog is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the page size for posts
|
||||||
|
/// </summary>
|
||||||
|
public int PostsPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether not registered user can leave comments
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowNotRegisteredUsersToLeaveComments { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to notify about new blog comments
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyAboutNewBlogComments { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of blog tags that appear in the tag cloud
|
||||||
|
/// </summary>
|
||||||
|
public int NumberOfTags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable the blog RSS feed link in customers browser address bar
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowHeaderRssUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether blog comments must be approved
|
||||||
|
/// </summary>
|
||||||
|
public bool BlogCommentsMustBeApproved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether blog comments will be filtered per store
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowBlogCommentsPerStore { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Nop.Core.Domain.Blogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blog post comment approved event
|
||||||
|
/// </summary>
|
||||||
|
public partial class BlogCommentApprovedEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="blogComment">Blog comment</param>
|
||||||
|
public BlogCommentApprovedEvent(BlogComment blogComment)
|
||||||
|
{
|
||||||
|
BlogComment = blogComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blog post comment
|
||||||
|
/// </summary>
|
||||||
|
public BlogComment BlogComment { get; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an attribute control type
|
||||||
|
/// </summary>
|
||||||
|
public enum AttributeControlType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dropdown list
|
||||||
|
/// </summary>
|
||||||
|
DropdownList = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Radio list
|
||||||
|
/// </summary>
|
||||||
|
RadioList = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checkboxes
|
||||||
|
/// </summary>
|
||||||
|
Checkboxes = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TextBox
|
||||||
|
/// </summary>
|
||||||
|
TextBox = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiline textbox
|
||||||
|
/// </summary>
|
||||||
|
MultilineTextbox = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Datepicker
|
||||||
|
/// </summary>
|
||||||
|
Datepicker = 20,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// File upload control
|
||||||
|
/// </summary>
|
||||||
|
FileUpload = 30,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color squares
|
||||||
|
/// </summary>
|
||||||
|
ColorSquares = 40,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Image squares
|
||||||
|
/// </summary>
|
||||||
|
ImageSquares = 45,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read-only checkboxes
|
||||||
|
/// </summary>
|
||||||
|
ReadonlyCheckboxes = 50
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an attribute value display type when out of stock
|
||||||
|
/// </summary>
|
||||||
|
public enum AttributeValueOutOfStockDisplayType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute value is visible, but cannot be interacted
|
||||||
|
/// </summary>
|
||||||
|
Disable,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute value is display always
|
||||||
|
/// </summary>
|
||||||
|
AlwaysDisplay
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an attribute value type
|
||||||
|
/// </summary>
|
||||||
|
public enum AttributeValueType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simple attribute value
|
||||||
|
/// </summary>
|
||||||
|
Simple = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Associated to a product (used when configuring bundled products)
|
||||||
|
/// </summary>
|
||||||
|
AssociatedToProduct = 10,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a back in stock subscription
|
||||||
|
/// </summary>
|
||||||
|
public partial class BackInStockSubscription : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the store identifier
|
||||||
|
/// </summary>
|
||||||
|
public int StoreId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the customer identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a backorder mode
|
||||||
|
/// </summary>
|
||||||
|
public enum BackorderMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No backorders
|
||||||
|
/// </summary>
|
||||||
|
NoBackorders = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allow qty below 0
|
||||||
|
/// </summary>
|
||||||
|
AllowQtyBelow0 = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allow qty below 0 and notify customer
|
||||||
|
/// </summary>
|
||||||
|
AllowQtyBelow0AndNotifyCustomer = 2,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,607 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Catalog settings
|
||||||
|
/// </summary>
|
||||||
|
public partial class CatalogSettings : ISettings
|
||||||
|
{
|
||||||
|
public CatalogSettings()
|
||||||
|
{
|
||||||
|
ProductSortingEnumDisabled = new List<int>();
|
||||||
|
ProductSortingEnumDisplayOrder = new Dictionary<int, int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating details pages of unpublished product details pages could be open (for SEO optimization)
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowViewUnpublishedProductPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating customers should see "discontinued" message when visiting details pages of unpublished products (if "AllowViewUnpublishedProductPage" is "true)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayDiscontinuedMessageForUnpublishedProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "Published" or "Disable buy/wishlist buttons" flags should be updated after order cancellation (deletion).
|
||||||
|
/// Of course, when qty > configured minimum stock level
|
||||||
|
/// </summary>
|
||||||
|
public bool PublishBackProductWhenCancellingOrders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display product SKU on the product details page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowSkuOnProductDetailsPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display product SKU on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowSkuOnCatalogPages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display manufacturer part number of a product
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowManufacturerPartNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display GTIN of a product
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowGtin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "Free shipping" icon should be displayed for products
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowFreeShippingNotification { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether short description should be displayed in product box
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowShortDescriptionOnCatalogPages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether product sorting is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowProductSorting { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers are allowed to change product view mode
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowProductViewModeChanging { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a default view mode
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultViewMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether a category details page should include products from subcategories
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowProductsFromSubcategories { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether number of products should be displayed beside each category
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCategoryProductNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether we include subcategories (when 'ShowCategoryProductNumber' is 'true')
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCategoryProductNumberIncludingSubcategories { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether category breadcrumb is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool CategoryBreadcrumbEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether a 'Share button' is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowShareButton { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a share code (e.g. ShareThis button code)
|
||||||
|
/// </summary>
|
||||||
|
public string PageShareCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating product reviews must be approved
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductReviewsMustBeApproved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating that customer can add only one review per product
|
||||||
|
/// </summary>
|
||||||
|
public bool OneReviewPerProductFromCustomer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating the default rating value of the product reviews
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultProductRatingValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to allow anonymous users write product reviews.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowAnonymousUsersToReviewProduct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether product can be reviewed only by customer who have already ordered it
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductReviewPossibleOnlyAfterPurchasing { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether notification of a store owner about new product reviews is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyStoreOwnerAboutNewProductReviews { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customer notification about product review reply is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyCustomerAboutProductReviewReply { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product reviews will be filtered per store
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowProductReviewsPerStore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a show product reviews tab on account page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowProductReviewsTabOnAccountPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the page size for product reviews in account page
|
||||||
|
/// </summary>
|
||||||
|
public int ProductReviewsPageSizeOnAccountPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product reviews should be sorted by creation date as ascending
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductReviewsSortByCreatedDateAscending { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether product 'Email a friend' feature is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool EmailAFriendEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to allow anonymous users to email a friend.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowAnonymousUsersToEmailAFriend { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of "Recently viewed products"
|
||||||
|
/// </summary>
|
||||||
|
public int RecentlyViewedProductsNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "Recently viewed products" feature is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool RecentlyViewedProductsEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "New products" page is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool NewProductsEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of products on the "New products" page
|
||||||
|
/// </summary>
|
||||||
|
public int NewProductsPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers are allowed to select page size on the "New products" page
|
||||||
|
/// </summary>
|
||||||
|
public bool NewProductsAllowCustomersToSelectPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available customer selectable page size options on the "New products" page
|
||||||
|
/// </summary>
|
||||||
|
public string NewProductsPageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "Compare products" feature is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool CompareProductsEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an allowed number of products to be compared
|
||||||
|
/// </summary>
|
||||||
|
public int CompareProductsNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether autocomplete is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductSearchAutoCompleteEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the search box is displayed
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductSearchEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of products to return when using "autocomplete" feature
|
||||||
|
/// </summary>
|
||||||
|
public int ProductSearchAutoCompleteNumberOfProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show product images in the auto complete search
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowProductImagesInSearchAutoComplete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show link to all result in the auto complete search
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowLinkToAllResultInSearchAutoComplete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a minimum search term length
|
||||||
|
/// </summary>
|
||||||
|
public int ProductSearchTermMinimumLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show bestsellers on home page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowBestsellersOnHomepage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of bestsellers on home page
|
||||||
|
/// </summary>
|
||||||
|
public int NumberOfBestsellersOnHomepage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the category dropdown is displayed with the search box
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowSearchBoxCategories { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of products per page on the search products page
|
||||||
|
/// </summary>
|
||||||
|
public int SearchPageProductsPerPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers are allowed to select page size on the search products page
|
||||||
|
/// </summary>
|
||||||
|
public bool SearchPageAllowCustomersToSelectPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available customer selectable page size options on the search products page
|
||||||
|
/// </summary>
|
||||||
|
public string SearchPagePageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range filtering is enabled on search page
|
||||||
|
/// </summary>
|
||||||
|
public bool SearchPagePriceRangeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "from" price on search page
|
||||||
|
/// </summary>
|
||||||
|
public decimal SearchPagePriceFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "to" price on search page
|
||||||
|
/// </summary>
|
||||||
|
public decimal SearchPagePriceTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range should be entered manually on search page
|
||||||
|
/// </summary>
|
||||||
|
public bool SearchPageManuallyPriceRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets "List of products purchased by other customers who purchased the above" option is enable
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductsAlsoPurchasedEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of products also purchased by other customers to display
|
||||||
|
/// </summary>
|
||||||
|
public int ProductsAlsoPurchasedNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether we should process attribute change using AJAX. It's used for dynamical attribute change, SKU/GTIN update of combinations, conditional attributes
|
||||||
|
/// </summary>
|
||||||
|
public bool AjaxProcessAttributeChange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of product tags that appear in the tag cloud
|
||||||
|
/// </summary>
|
||||||
|
public int NumberOfProductTags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of products per page on 'products by tag' page
|
||||||
|
/// </summary>
|
||||||
|
public int ProductsByTagPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers can select the page size for 'products by tag'
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductsByTagAllowCustomersToSelectPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available customer selectable page size options for 'products by tag'
|
||||||
|
/// </summary>
|
||||||
|
public string ProductsByTagPageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range filtering is enabled on 'products by tag' page
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductsByTagPriceRangeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "from" price on 'products by tag' page
|
||||||
|
/// </summary>
|
||||||
|
public decimal ProductsByTagPriceFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "to" price on 'products by tag' page
|
||||||
|
/// </summary>
|
||||||
|
public decimal ProductsByTagPriceTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range should be entered manually on 'products by tag' page
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductsByTagManuallyPriceRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to include "Short description" in compare products
|
||||||
|
/// </summary>
|
||||||
|
public bool IncludeShortDescriptionInCompareProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to include "Full description" in compare products
|
||||||
|
/// </summary>
|
||||||
|
public bool IncludeFullDescriptionInCompareProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An option indicating whether products on category and manufacturer pages should include featured products as well
|
||||||
|
/// </summary>
|
||||||
|
public bool IncludeFeaturedProductsInNormalLists { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to render link to required products in "Require other products added to the cart" warning
|
||||||
|
/// </summary>
|
||||||
|
public bool UseLinksInRequiredProductWarnings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether tier prices should be displayed with applied discounts (if available)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTierPricesWithDiscounts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to ignore discounts (side-wide). It can significantly improve performance when enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreDiscounts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to ignore featured products (side-wide). It can significantly improve performance when enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreFeaturedProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to ignore ACL rules (side-wide). It can significantly improve performance when enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreAcl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to ignore "limit per store" rules (side-wide). It can significantly improve performance when enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IgnoreStoreLimitations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to cache product prices. It can significantly improve performance when enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool CacheProductPrices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating maximum number of 'back in stock' subscription
|
||||||
|
/// </summary>
|
||||||
|
public int MaximumBackInStockSubscriptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value indicating how many manufacturers to display in manufacturers block
|
||||||
|
/// </summary>
|
||||||
|
public int ManufacturersBlockItemsToDisplay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax in the footer (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoFooter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax on product details pages (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoProductDetailsPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax in product boxes (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoProductBoxes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax on shopping cart page (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoShoppingCart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax on wishlist page (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoWishlist { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display information about shipping and tax on order details page (used in Germany)
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTaxShippingInfoOrderDetailsPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default value to use for Category page size options (for new categories)
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultCategoryPageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default value to use for Category page size (for new categories)
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultCategoryPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default value to use for Manufacturer page size options (for new manufacturers)
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultManufacturerPageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default value to use for Manufacturer page size (for new manufacturers)
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultManufacturerPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a list of disabled values of ProductSortingEnum
|
||||||
|
/// </summary>
|
||||||
|
public List<int> ProductSortingEnumDisabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a display order of ProductSortingEnum values
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<int, int> ProductSortingEnumDisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the products need to be exported/imported with their attributes
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportProductAttributes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether need to use "limited to stores" property for exported/imported products
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportProductUseLimitedToStores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the products need to be exported/imported with their specification attributes
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportProductSpecificationAttributes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the products need to be exported/imported with their tier prices
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportTierPrices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether need create dropdown list for export
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportUseDropdownlistsForAssociatedEntities { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the products should be exported/imported with a full category name including names of all its parents
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportProductCategoryBreadcrumb { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the categories need to be exported/imported using name of category
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportCategoriesUsingCategoryName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the images can be downloaded from remote server
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportAllowDownloadImages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether products must be importing by separated files
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportSplitProductsFile { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value of max products count in one file
|
||||||
|
/// </summary>
|
||||||
|
public int ExportImportProductsCountInOneFile { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to remove required products from the cart if the main one is removed
|
||||||
|
/// </summary>
|
||||||
|
public bool RemoveRequiredProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the related entities need to be exported/imported using name
|
||||||
|
/// </summary>
|
||||||
|
public bool ExportImportRelatedEntitiesByName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets count of displayed years for datepicker
|
||||||
|
/// </summary>
|
||||||
|
public int CountDisplayedYearsDatePicker { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether it's necessary to show the date for pre-order availability in a public store
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayDatePreOrderAvailability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether to use a standard menu in public store or use Ajax to load a menu
|
||||||
|
/// </summary>
|
||||||
|
public bool UseAjaxLoadMenu { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether to use standard or AJAX products loading (applicable to 'paging', 'filtering', 'view modes') in catalog
|
||||||
|
/// </summary>
|
||||||
|
public bool UseAjaxCatalogProductsLoading { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether the manufacturer filtering is enabled on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableManufacturerFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether the price range filtering is enabled on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool EnablePriceRangeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether the specification attribute filtering is enabled on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableSpecificationAttributeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set a value indicating whether the "From" prices (based on price adjustments of combinations and attributes) are displayed on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayFromPrices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute value display type when out of stock
|
||||||
|
/// </summary>
|
||||||
|
public AttributeValueOutOfStockDisplayType AttributeValueOutOfStockDisplayType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customer can search with manufacturer name
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomersToSearchWithManufacturerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customer can search with category name
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomersToSearchWithCategoryName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether all pictures will be displayed on catalog pages
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayAllPicturesOnCatalogPages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the identifier of product URL structure type (e.g. '/category-seo-name/product-seo-name' or '/product-seo-name')
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>We have ProductUrlStructureType enum, but we use int value here so that it can be overridden in third-party plugins</remarks>
|
||||||
|
public int ProductUrlStructureTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an system name of active search provider
|
||||||
|
/// </summary>
|
||||||
|
public string ActiveSearchProviderSystemName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether standard search will be used when the search provider throws an exception
|
||||||
|
/// </summary>
|
||||||
|
public bool UseStandardSearchWhenSearchProviderThrowsException { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a number of reviews per page on vendor reviews page
|
||||||
|
/// </summary>
|
||||||
|
public int VendorProductReviewsPageSize { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
using Nop.Core.Domain.Common;
|
||||||
|
using Nop.Core.Domain.Discounts;
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
using Nop.Core.Domain.Security;
|
||||||
|
using Nop.Core.Domain.Seo;
|
||||||
|
using Nop.Core.Domain.Stores;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a category
|
||||||
|
/// </summary>
|
||||||
|
public partial class Category : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountCategoryMapping>, ISoftDeletedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value of used category template identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CategoryTemplateId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta keywords
|
||||||
|
/// </summary>
|
||||||
|
public string MetaKeywords { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta description
|
||||||
|
/// </summary>
|
||||||
|
public string MetaDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta title
|
||||||
|
/// </summary>
|
||||||
|
public string MetaTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parent category identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ParentCategoryId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the picture identifier
|
||||||
|
/// </summary>
|
||||||
|
public int PictureId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the page size
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers can select the page size
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available customer selectable page size options
|
||||||
|
/// </summary>
|
||||||
|
public string PageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show the category on home page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowOnHomepage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to include this category in the top menu
|
||||||
|
/// </summary>
|
||||||
|
public bool IncludeInTopMenu { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||||
|
/// </summary>
|
||||||
|
public bool SubjectToAcl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||||
|
/// </summary>
|
||||||
|
public bool LimitedToStores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is published
|
||||||
|
/// </summary>
|
||||||
|
public bool Published { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity has been deleted
|
||||||
|
/// </summary>
|
||||||
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance update
|
||||||
|
/// </summary>
|
||||||
|
public DateTime UpdatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool PriceRangeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "from" price
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "to" price
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||||
|
/// </summary>
|
||||||
|
public bool ManuallyPriceRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the vendor cannot add products to the category
|
||||||
|
/// </summary>
|
||||||
|
public bool RestrictFromVendors { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a category template
|
||||||
|
/// </summary>
|
||||||
|
public partial class CategoryTemplate : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the template name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the view path
|
||||||
|
/// </summary>
|
||||||
|
public string ViewPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cross-sell product
|
||||||
|
/// </summary>
|
||||||
|
public partial class CrossSellProduct : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the first product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId1 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the second product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId2 { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a download activation type
|
||||||
|
/// </summary>
|
||||||
|
public enum DownloadActivationType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// When order is paid
|
||||||
|
/// </summary>
|
||||||
|
WhenOrderIsPaid = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manually
|
||||||
|
/// </summary>
|
||||||
|
Manually = 10,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Product review approved event
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductReviewApprovedEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="productReview">Product review</param>
|
||||||
|
public ProductReviewApprovedEvent(ProductReview productReview)
|
||||||
|
{
|
||||||
|
ProductReview = productReview;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Product review
|
||||||
|
/// </summary>
|
||||||
|
public ProductReview ProductReview { get; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a gift card type
|
||||||
|
/// </summary>
|
||||||
|
public enum GiftCardType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Virtual
|
||||||
|
/// </summary>
|
||||||
|
Virtual = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Physical
|
||||||
|
/// </summary>
|
||||||
|
Physical = 1,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a low stock activity
|
||||||
|
/// </summary>
|
||||||
|
public enum LowStockActivity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nothing
|
||||||
|
/// </summary>
|
||||||
|
Nothing = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disable buy button
|
||||||
|
/// </summary>
|
||||||
|
DisableBuyButton = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpublish
|
||||||
|
/// </summary>
|
||||||
|
Unpublish = 2,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a method of inventory management
|
||||||
|
/// </summary>
|
||||||
|
public enum ManageInventoryMethod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Don't track inventory for product
|
||||||
|
/// </summary>
|
||||||
|
DontManageStock = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Track inventory for product
|
||||||
|
/// </summary>
|
||||||
|
ManageStock = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Track inventory for product by product attributes
|
||||||
|
/// </summary>
|
||||||
|
ManageStockByAttributes = 2,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
using Nop.Core.Domain.Common;
|
||||||
|
using Nop.Core.Domain.Discounts;
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
using Nop.Core.Domain.Security;
|
||||||
|
using Nop.Core.Domain.Seo;
|
||||||
|
using Nop.Core.Domain.Stores;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a manufacturer
|
||||||
|
/// </summary>
|
||||||
|
public partial class Manufacturer : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountManufacturerMapping>, ISoftDeletedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value of used manufacturer template identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ManufacturerTemplateId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta keywords
|
||||||
|
/// </summary>
|
||||||
|
public string MetaKeywords { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta description
|
||||||
|
/// </summary>
|
||||||
|
public string MetaDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta title
|
||||||
|
/// </summary>
|
||||||
|
public string MetaTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parent picture identifier
|
||||||
|
/// </summary>
|
||||||
|
public int PictureId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the page size
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether customers can select the page size
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available customer selectable page size options
|
||||||
|
/// </summary>
|
||||||
|
public string PageSizeOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||||
|
/// </summary>
|
||||||
|
public bool SubjectToAcl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||||
|
/// </summary>
|
||||||
|
public bool LimitedToStores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is published
|
||||||
|
/// </summary>
|
||||||
|
public bool Published { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity has been deleted
|
||||||
|
/// </summary>
|
||||||
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance update
|
||||||
|
/// </summary>
|
||||||
|
public DateTime UpdatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool PriceRangeFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "from" price
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the "to" price
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||||
|
/// </summary>
|
||||||
|
public bool ManuallyPriceRange { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a manufacturer template
|
||||||
|
/// </summary>
|
||||||
|
public partial class ManufacturerTemplate : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the template name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the view path
|
||||||
|
/// </summary>
|
||||||
|
public string ViewPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a predefined (default) product attribute value
|
||||||
|
/// </summary>
|
||||||
|
public partial class PredefinedProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAttributeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the price adjustment
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceAdjustment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage
|
||||||
|
/// </summary>
|
||||||
|
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the weight adjustment
|
||||||
|
/// </summary>
|
||||||
|
public decimal WeightAdjustment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute value cost
|
||||||
|
/// </summary>
|
||||||
|
public decimal Cost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the value is pre-selected
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPreSelected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,596 @@
|
||||||
|
using Nop.Core.Domain.Common;
|
||||||
|
using Nop.Core.Domain.Discounts;
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
using Nop.Core.Domain.Security;
|
||||||
|
using Nop.Core.Domain.Seo;
|
||||||
|
using Nop.Core.Domain.Stores;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product
|
||||||
|
/// </summary>
|
||||||
|
public partial class Product : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountProductMapping>, ISoftDeletedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parent product identifier. It's used to identify associated products (only with "grouped" products)
|
||||||
|
/// </summary>
|
||||||
|
public int ParentGroupedProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the values indicating whether this product is visible in catalog or search results.
|
||||||
|
/// It's used when this product is associated to some "grouped" one
|
||||||
|
/// This way associated products could be accessed/added/etc only from a grouped product details page
|
||||||
|
/// </summary>
|
||||||
|
public bool VisibleIndividually { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the short description
|
||||||
|
/// </summary>
|
||||||
|
public string ShortDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the full description
|
||||||
|
/// </summary>
|
||||||
|
public string FullDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the admin comment
|
||||||
|
/// </summary>
|
||||||
|
public string AdminComment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value of used product template identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductTemplateId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a vendor identifier
|
||||||
|
/// </summary>
|
||||||
|
public int VendorId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show the product on home page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowOnHomepage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta keywords
|
||||||
|
/// </summary>
|
||||||
|
public string MetaKeywords { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta description
|
||||||
|
/// </summary>
|
||||||
|
public string MetaDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the meta title
|
||||||
|
/// </summary>
|
||||||
|
public string MetaTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product allows customer reviews
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomerReviews { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rating sum (approved reviews)
|
||||||
|
/// </summary>
|
||||||
|
public int ApprovedRatingSum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rating sum (not approved reviews)
|
||||||
|
/// </summary>
|
||||||
|
public int NotApprovedRatingSum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the total rating votes (approved reviews)
|
||||||
|
/// </summary>
|
||||||
|
public int ApprovedTotalReviews { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the total rating votes (not approved reviews)
|
||||||
|
/// </summary>
|
||||||
|
public int NotApprovedTotalReviews { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||||
|
/// </summary>
|
||||||
|
public bool SubjectToAcl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||||
|
/// </summary>
|
||||||
|
public bool LimitedToStores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the SKU
|
||||||
|
/// </summary>
|
||||||
|
public string Sku { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the manufacturer part number
|
||||||
|
/// </summary>
|
||||||
|
public string ManufacturerPartNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books).
|
||||||
|
/// </summary>
|
||||||
|
public string Gtin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is gift card
|
||||||
|
/// </summary>
|
||||||
|
public bool IsGiftCard { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the gift card type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int GiftCardTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets gift card amount that can be used after purchase. If not specified, then product price will be used.
|
||||||
|
/// </summary>
|
||||||
|
public decimal? OverriddenGiftCardAmount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product requires that other products are added to the cart (Product X requires Product Y)
|
||||||
|
/// </summary>
|
||||||
|
public bool RequireOtherProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a required product identifiers (comma separated)
|
||||||
|
/// </summary>
|
||||||
|
public string RequiredProductIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether required products are automatically added to the cart
|
||||||
|
/// </summary>
|
||||||
|
public bool AutomaticallyAddRequiredProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is download
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDownload { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the download identifier
|
||||||
|
/// </summary>
|
||||||
|
public int DownloadId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times
|
||||||
|
/// </summary>
|
||||||
|
public bool UnlimitedDownloads { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum number of downloads
|
||||||
|
/// </summary>
|
||||||
|
public int MaxNumberOfDownloads { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the number of days during customers keeps access to the file.
|
||||||
|
/// </summary>
|
||||||
|
public int? DownloadExpirationDays { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the download activation type
|
||||||
|
/// </summary>
|
||||||
|
public int DownloadActivationTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product has a sample download file
|
||||||
|
/// </summary>
|
||||||
|
public bool HasSampleDownload { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the sample download identifier
|
||||||
|
/// </summary>
|
||||||
|
public int SampleDownloadId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product has user agreement
|
||||||
|
/// </summary>
|
||||||
|
public bool HasUserAgreement { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the text of license agreement
|
||||||
|
/// </summary>
|
||||||
|
public string UserAgreementText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is recurring
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRecurring { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the cycle length
|
||||||
|
/// </summary>
|
||||||
|
public int RecurringCycleLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the cycle period
|
||||||
|
/// </summary>
|
||||||
|
public int RecurringCyclePeriodId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the total cycles
|
||||||
|
/// </summary>
|
||||||
|
public int RecurringTotalCycles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is rental
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRental { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rental length for some period (price for this period)
|
||||||
|
/// </summary>
|
||||||
|
public int RentalPriceLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rental period (price for this period)
|
||||||
|
/// </summary>
|
||||||
|
public int RentalPricePeriodId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is ship enabled
|
||||||
|
/// </summary>
|
||||||
|
public bool IsShipEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is free shipping
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFreeShipping { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value this product should be shipped separately (each item)
|
||||||
|
/// </summary>
|
||||||
|
public bool ShipSeparately { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the additional shipping charge
|
||||||
|
/// </summary>
|
||||||
|
public decimal AdditionalShippingCharge { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a delivery date identifier
|
||||||
|
/// </summary>
|
||||||
|
public int DeliveryDateId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is marked as tax exempt
|
||||||
|
/// </summary>
|
||||||
|
public bool IsTaxExempt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the tax category identifier
|
||||||
|
/// </summary>
|
||||||
|
public int TaxCategoryId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating how to manage inventory
|
||||||
|
/// </summary>
|
||||||
|
public int ManageInventoryMethodId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a product availability range identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAvailabilityRangeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether multiple warehouses are used for this product
|
||||||
|
/// </summary>
|
||||||
|
public bool UseMultipleWarehouses { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a warehouse identifier
|
||||||
|
/// </summary>
|
||||||
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display stock availability
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayStockAvailability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayStockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the minimum stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public int MinStockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the low stock activity identifier
|
||||||
|
/// </summary>
|
||||||
|
public int LowStockActivityId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the quantity when admin should be notified
|
||||||
|
/// </summary>
|
||||||
|
public int NotifyAdminForQuantityBelow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value backorder mode identifier
|
||||||
|
/// </summary>
|
||||||
|
public int BackorderModeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to back in stock subscriptions are allowed
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowBackInStockSubscriptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the order minimum quantity
|
||||||
|
/// </summary>
|
||||||
|
public int OrderMinimumQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the order maximum quantity
|
||||||
|
/// </summary>
|
||||||
|
public int OrderMaximumQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the comma separated list of allowed quantities. null or empty if any quantity is allowed
|
||||||
|
/// </summary>
|
||||||
|
public string AllowedQuantities { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether we allow adding to the cart/wishlist only attribute combinations that exist and have stock greater than zero.
|
||||||
|
/// This option is used only when we have "manage inventory" set to "track inventory by product attributes"
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowAddingOnlyExistingAttributeCombinations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to display attribute combination images only
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayAttributeCombinationImagesOnly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this product is returnable (a customer is allowed to submit return request with this product)
|
||||||
|
/// </summary>
|
||||||
|
public bool NotReturnable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to disable buy (Add to cart) button
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableBuyButton { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to disable "Add to wishlist" button
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableWishlistButton { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this item is available for Pre-Order
|
||||||
|
/// </summary>
|
||||||
|
public bool AvailableForPreOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the start date and time of the product availability (for pre-order products)
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? PreOrderAvailabilityStartDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price
|
||||||
|
/// </summary>
|
||||||
|
public bool CallForPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the price
|
||||||
|
/// </summary>
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the old price
|
||||||
|
/// </summary>
|
||||||
|
public decimal OldPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product cost
|
||||||
|
/// </summary>
|
||||||
|
public decimal ProductCost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether a customer enters price
|
||||||
|
/// </summary>
|
||||||
|
public bool CustomerEntersPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the minimum price entered by a customer
|
||||||
|
/// </summary>
|
||||||
|
public decimal MinimumCustomerEnteredPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum price entered by a customer
|
||||||
|
/// </summary>
|
||||||
|
public decimal MaximumCustomerEnteredPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether base price (PAngV) is enabled. Used by German users.
|
||||||
|
/// </summary>
|
||||||
|
public bool BasepriceEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an amount in product for PAngV
|
||||||
|
/// </summary>
|
||||||
|
public decimal BasepriceAmount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a unit of product for PAngV (MeasureWeight entity)
|
||||||
|
/// </summary>
|
||||||
|
public int BasepriceUnitId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a reference amount for PAngV
|
||||||
|
/// </summary>
|
||||||
|
public decimal BasepriceBaseAmount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a reference unit for PAngV (MeasureWeight entity)
|
||||||
|
/// </summary>
|
||||||
|
public int BasepriceBaseUnitId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this product is marked as new
|
||||||
|
/// </summary>
|
||||||
|
public bool MarkAsNew { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the start date and time of the new product (set product as "New" from date). Leave empty to ignore this property
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? MarkAsNewStartDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the end date and time of the new product (set product as "New" to date). Leave empty to ignore this property
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? MarkAsNewEndDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the weight
|
||||||
|
/// </summary>
|
||||||
|
public decimal Weight { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the length
|
||||||
|
/// </summary>
|
||||||
|
public decimal Length { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the width
|
||||||
|
/// </summary>
|
||||||
|
public decimal Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the height
|
||||||
|
/// </summary>
|
||||||
|
public decimal Height { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available start date and time
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? AvailableStartDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the available end date and time
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? AvailableEndDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a display order.
|
||||||
|
/// This value is used when sorting associated products (used with "grouped" products)
|
||||||
|
/// This value is used when sorting home page products
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is published
|
||||||
|
/// </summary>
|
||||||
|
public bool Published { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity has been deleted
|
||||||
|
/// </summary>
|
||||||
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of product creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of product update
|
||||||
|
/// </summary>
|
||||||
|
public DateTime UpdatedOnUtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product type
|
||||||
|
/// </summary>
|
||||||
|
public ProductType ProductType
|
||||||
|
{
|
||||||
|
get => (ProductType)ProductTypeId;
|
||||||
|
set => ProductTypeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the backorder mode
|
||||||
|
/// </summary>
|
||||||
|
public BackorderMode BackorderMode
|
||||||
|
{
|
||||||
|
get => (BackorderMode)BackorderModeId;
|
||||||
|
set => BackorderModeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the download activation type
|
||||||
|
/// </summary>
|
||||||
|
public DownloadActivationType DownloadActivationType
|
||||||
|
{
|
||||||
|
get => (DownloadActivationType)DownloadActivationTypeId;
|
||||||
|
set => DownloadActivationTypeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the gift card type
|
||||||
|
/// </summary>
|
||||||
|
public GiftCardType GiftCardType
|
||||||
|
{
|
||||||
|
get => (GiftCardType)GiftCardTypeId;
|
||||||
|
set => GiftCardTypeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the low stock activity
|
||||||
|
/// </summary>
|
||||||
|
public LowStockActivity LowStockActivity
|
||||||
|
{
|
||||||
|
get => (LowStockActivity)LowStockActivityId;
|
||||||
|
set => LowStockActivityId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value indicating how to manage inventory
|
||||||
|
/// </summary>
|
||||||
|
public ManageInventoryMethod ManageInventoryMethod
|
||||||
|
{
|
||||||
|
get => (ManageInventoryMethod)ManageInventoryMethodId;
|
||||||
|
set => ManageInventoryMethodId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the cycle period for recurring products
|
||||||
|
/// </summary>
|
||||||
|
public RecurringProductCyclePeriod RecurringCyclePeriod
|
||||||
|
{
|
||||||
|
get => (RecurringProductCyclePeriod)RecurringCyclePeriodId;
|
||||||
|
set => RecurringCyclePeriodId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the period for rental products
|
||||||
|
/// </summary>
|
||||||
|
public RentalPricePeriod RentalPricePeriod
|
||||||
|
{
|
||||||
|
get => (RentalPricePeriod)RentalPricePeriodId;
|
||||||
|
set => RentalPricePeriodId = (int)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttribute : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute combination
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttributeCombination : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attributes
|
||||||
|
/// </summary>
|
||||||
|
public string AttributesXml { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to allow orders when out of stock
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowOutOfStockOrders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the SKU
|
||||||
|
/// </summary>
|
||||||
|
public string Sku { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the manufacturer part number
|
||||||
|
/// </summary>
|
||||||
|
public string ManufacturerPartNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books).
|
||||||
|
/// </summary>
|
||||||
|
public string Gtin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute combination price. This way a store owner can override the default product price when this attribute combination is added to the cart. For example, you can give a discount this way.
|
||||||
|
/// </summary>
|
||||||
|
public decimal? OverriddenPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the quantity when admin should be notified
|
||||||
|
/// </summary>
|
||||||
|
public int NotifyAdminForQuantityBelow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the minimum stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public int MinStockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The field is not used since 4.70 and is left only for the update process
|
||||||
|
/// use the <see cref="ProductAttributeCombinationPicture"/> instead
|
||||||
|
/// </summary>
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
[Browsable(false)]
|
||||||
|
[Obsolete("The field is not used since 4.70 and is left only for the update process use the ProductAttributeCombinationPicture instead")]
|
||||||
|
public int? PictureId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute combination picture
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttributeCombinationPicture : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute combination id
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAttributeCombinationId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the identifier of picture associated with this combination
|
||||||
|
/// </summary>
|
||||||
|
public int PictureId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttributeMapping : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAttributeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value a text prompt
|
||||||
|
/// </summary>
|
||||||
|
public string TextPrompt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the entity is required
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRequired { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute control type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int AttributeControlTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
//validation fields
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the validation rule for minimum length (for textbox and multiline textbox)
|
||||||
|
/// </summary>
|
||||||
|
public int? ValidationMinLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the validation rule for maximum length (for textbox and multiline textbox)
|
||||||
|
/// </summary>
|
||||||
|
public int? ValidationMaxLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the validation rule for file allowed extensions (for file upload)
|
||||||
|
/// </summary>
|
||||||
|
public string ValidationFileAllowedExtensions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the validation rule for file maximum size in kilobytes (for file upload)
|
||||||
|
/// </summary>
|
||||||
|
public int? ValidationFileMaximumSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default value (for textbox and multiline textbox)
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a condition (depending on other attribute) when this attribute should be enabled (visible).
|
||||||
|
/// Leave empty (or null) to enable this attribute.
|
||||||
|
/// Conditional attributes that only appear if a previous attribute is selected, such as having an option
|
||||||
|
/// for personalizing clothing with a name and only providing the text input box if the "Personalize" radio button is checked.
|
||||||
|
/// </summary>
|
||||||
|
public string ConditionAttributeXml { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute control type
|
||||||
|
/// </summary>
|
||||||
|
public AttributeControlType AttributeControlType
|
||||||
|
{
|
||||||
|
get => (AttributeControlType)AttributeControlTypeId;
|
||||||
|
set => AttributeControlTypeId = (int)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute value
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute mapping identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAttributeMappingId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute value type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int AttributeValueTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the associated product identifier (used only with AttributeValueType.AssociatedToProduct)
|
||||||
|
/// </summary>
|
||||||
|
public int AssociatedProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color RGB value (used with "Color squares" attribute type)
|
||||||
|
/// </summary>
|
||||||
|
public string ColorSquaresRgb { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the picture ID for image square (used with "Image squares" attribute type)
|
||||||
|
/// </summary>
|
||||||
|
public int ImageSquaresPictureId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the price adjustment (used only with AttributeValueType.Simple)
|
||||||
|
/// </summary>
|
||||||
|
public decimal PriceAdjustment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage (used only with AttributeValueType.Simple)
|
||||||
|
/// </summary>
|
||||||
|
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the weight adjustment (used only with AttributeValueType.Simple)
|
||||||
|
/// </summary>
|
||||||
|
public decimal WeightAdjustment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute value cost (used only with AttributeValueType.Simple)
|
||||||
|
/// </summary>
|
||||||
|
public decimal Cost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the customer can enter the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||||
|
/// </summary>
|
||||||
|
public bool CustomerEntersQty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||||
|
/// </summary>
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the value is pre-selected
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPreSelected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute value type
|
||||||
|
/// </summary>
|
||||||
|
public AttributeValueType AttributeValueType
|
||||||
|
{
|
||||||
|
get => (AttributeValueType)AttributeValueTypeId;
|
||||||
|
set => AttributeValueTypeId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The field is not used since 4.70 and is left only for the update process
|
||||||
|
/// use the <see cref="ProductAttributeValuePicture"/> instead
|
||||||
|
/// </summary>
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
[Browsable(false)]
|
||||||
|
[Obsolete("The field is not used since 4.70 and is left only for the update process use the ProductAttributeValuePicture instead")]
|
||||||
|
public int? PictureId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product attribute value picture
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductAttributeValuePicture : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product attribute value id
|
||||||
|
/// </summary>
|
||||||
|
public int ProductAttributeValueId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the picture (identifier) associated with this value. This picture should replace a product main picture once clicked (selected).
|
||||||
|
/// </summary>
|
||||||
|
public int PictureId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product category mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductCategory : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the category identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is featured
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFeaturedProduct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
using Nop.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Product editor settings
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductEditorSettings : ISettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product type' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Visible individually' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool VisibleIndividually { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product template' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Admin comment' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AdminComment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Vendor' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Vendor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Stores' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Stores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'ACL' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ACL { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Show on home page' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowOnHomepage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Allow customer reviews' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomerReviews { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product tags' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductTags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Manufacturer part number' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ManufacturerPartNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'GTIN' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool GTIN { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product cost' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductCost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Tier prices' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool TierPrices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Discounts' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Discounts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Disable buy button' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableBuyButton { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Disable wishlist button' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableWishlistButton { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Available for pre-order' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AvailableForPreOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Call for price' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool CallForPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Old price' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool OldPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Customer enters price' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool CustomerEntersPrice { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'PAngV' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool PAngV { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Require other products added to the cart' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool RequireOtherProductsAddedToCart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Is gift card' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool IsGiftCard { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Downloadable product' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool DownloadableProduct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Recurring product' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool RecurringProduct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Is rental' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRental { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Free shipping' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool FreeShipping { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Ship separately' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ShipSeparately { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Additional shipping charge' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AdditionalShippingCharge { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Delivery date' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool DeliveryDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product availability range' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductAvailabilityRange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Use multiple warehouses' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool UseMultipleWarehouses { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Warehouse' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Warehouse { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Display stock availability' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayStockAvailability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Minimum stock quantity' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool MinimumStockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Low stock activity' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool LowStockActivity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Notify admin for quantity below' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyAdminForQuantityBelow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Backorders' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Backorders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Allow back in stock subscriptions' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowBackInStockSubscriptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Minimum cart quantity' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool MinimumCartQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Maximum cart quantity' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool MaximumCartQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Allowed quantities' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowedQuantities { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Allow only existing attribute combinations' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowAddingOnlyExistingAttributeCombinations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Not returnable' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool NotReturnable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Weight' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Weight { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Dimension' fields (height, length, width) are shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Dimensions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Available start date' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AvailableStartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Available end date' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool AvailableEndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Mark as new' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool MarkAsNew { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Published' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Published { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Related products' block is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool RelatedProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Cross-sells products' block is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool CrossSellsProducts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'SEO' tab is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Seo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Purchased with orders' tab is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool PurchasedWithOrders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Product attributes' tab is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool ProductAttributes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Specification attributes' tab is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool SpecificationAttributes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Manufacturers' field is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool Manufacturers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether 'Stock quantity history' tab is shown
|
||||||
|
/// </summary>
|
||||||
|
public bool StockQuantityHistory { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product manufacturer mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductManufacturer : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the manufacturer identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ManufacturerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the product is featured
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFeaturedProduct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product picture mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductPicture : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the picture identifier
|
||||||
|
/// </summary>
|
||||||
|
public int PictureId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product-product tag mapping class
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductProductTagMapping : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product tag identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductTagId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product review
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductReview : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the customer identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the store identifier
|
||||||
|
/// </summary>
|
||||||
|
public int StoreId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the content is approved
|
||||||
|
/// </summary>
|
||||||
|
public bool IsApproved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the title
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the review text
|
||||||
|
/// </summary>
|
||||||
|
public string ReviewText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the reply text
|
||||||
|
/// </summary>
|
||||||
|
public string ReplyText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value indicating whether the customer is already notified of the reply to review
|
||||||
|
/// </summary>
|
||||||
|
public bool CustomerNotifiedOfReply { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Review rating
|
||||||
|
/// </summary>
|
||||||
|
public int Rating { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Review helpful votes total
|
||||||
|
/// </summary>
|
||||||
|
public int HelpfulYesTotal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Review not helpful votes total
|
||||||
|
/// </summary>
|
||||||
|
public int HelpfulNoTotal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the date and time of instance creation
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product review helpfulness
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductReviewHelpfulness : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product review identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductReviewId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value indicating whether a review a helpful
|
||||||
|
/// </summary>
|
||||||
|
public bool WasHelpful { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the customer identifier
|
||||||
|
/// </summary>
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product review and review type mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductReviewReviewTypeMapping : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product review identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductReviewId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the review type identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ReviewTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rating
|
||||||
|
/// </summary>
|
||||||
|
public int Rating { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the product sorting
|
||||||
|
/// </summary>
|
||||||
|
public enum ProductSortingEnum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Position (display order)
|
||||||
|
/// </summary>
|
||||||
|
Position = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name: A to Z
|
||||||
|
/// </summary>
|
||||||
|
NameAsc = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name: Z to A
|
||||||
|
/// </summary>
|
||||||
|
NameDesc = 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Price: Low to High
|
||||||
|
/// </summary>
|
||||||
|
PriceAsc = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Price: High to Low
|
||||||
|
/// </summary>
|
||||||
|
PriceDesc = 11,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Product creation date
|
||||||
|
/// </summary>
|
||||||
|
CreatedOn = 15,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product specification attribute
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductSpecificationAttribute : BaseEntity, ILocalizedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the attribute type ID
|
||||||
|
/// </summary>
|
||||||
|
public int AttributeTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the specification attribute identifier
|
||||||
|
/// </summary>
|
||||||
|
public int SpecificationAttributeOptionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the custom value
|
||||||
|
/// </summary>
|
||||||
|
public string CustomValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the attribute can be filtered by
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowFiltering { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the attribute will be shown on the product page
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowOnProductPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute control type
|
||||||
|
/// </summary>
|
||||||
|
public SpecificationAttributeType AttributeType
|
||||||
|
{
|
||||||
|
get => (SpecificationAttributeType)AttributeTypeId;
|
||||||
|
set => AttributeTypeId = (int)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Nop.Core.Domain.Localization;
|
||||||
|
using Nop.Core.Domain.Seo;
|
||||||
|
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product tag
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductTag : BaseEntity, ILocalizedEntity, ISlugSupported
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product template
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductTemplate : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the template name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the view path
|
||||||
|
/// </summary>
|
||||||
|
public string ViewPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a comma-separated list of product type identifiers NOT supported by this template
|
||||||
|
/// </summary>
|
||||||
|
public string IgnoredProductTypes { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product type
|
||||||
|
/// </summary>
|
||||||
|
public enum ProductType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simple
|
||||||
|
/// </summary>
|
||||||
|
SimpleProduct = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grouped (product with variants)
|
||||||
|
/// </summary>
|
||||||
|
GroupedProduct = 10,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the product URL structure type enum
|
||||||
|
/// </summary>
|
||||||
|
public enum ProductUrlStructureType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Product only (e.g. '/product-seo-name')
|
||||||
|
/// </summary>
|
||||||
|
Product = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Category (the most nested), then product (e.g. '/category-seo-name/product-seo-name')
|
||||||
|
/// </summary>
|
||||||
|
CategoryProduct = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manufacturer, then product (e.g. '/manufacturer-seo-name/product-seo-name')
|
||||||
|
/// </summary>
|
||||||
|
ManufacturerProduct = 20
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a product video mapping
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductVideo : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the video identifier
|
||||||
|
/// </summary>
|
||||||
|
public int VideoId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a record to manage product inventory per warehouse
|
||||||
|
/// </summary>
|
||||||
|
public partial class ProductWarehouseInventory : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the warehouse identifier
|
||||||
|
/// </summary>
|
||||||
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the stock quantity
|
||||||
|
/// </summary>
|
||||||
|
public int StockQuantity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the reserved quantity (ordered but not shipped yet)
|
||||||
|
/// </summary>
|
||||||
|
public int ReservedQuantity { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a recurring product cycle period
|
||||||
|
/// </summary>
|
||||||
|
public enum RecurringProductCyclePeriod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Days
|
||||||
|
/// </summary>
|
||||||
|
Days = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Weeks
|
||||||
|
/// </summary>
|
||||||
|
Weeks = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Months
|
||||||
|
/// </summary>
|
||||||
|
Months = 20,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Years
|
||||||
|
/// </summary>
|
||||||
|
Years = 30,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Nop.Core.Domain.Catalog;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a related product
|
||||||
|
/// </summary>
|
||||||
|
public partial class RelatedProduct : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the first product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId1 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the second product identifier
|
||||||
|
/// </summary>
|
||||||
|
public int ProductId2 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display order
|
||||||
|
/// </summary>
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue