Azure Nopcommerce File Upload Fails After Processing

This guide covers various aspects, best practices and approaches for nopCommerce plugin development.

Plugin is a software component enabling nopCommerce basic features extension. There is an official nopCommerce plugin marketplace distributing both costless and paid plugins.

Plugins enable developers to add new features and/or customize a UI theme without modifying nopCommerce source code. This is of import for stable running of a spider web application and for upgrading nopCommerce platform to newer versions.

This manual introduces best practices in code organization and plugin development based on eCommerce expertise of ISDK.

Official nopCommerce plugin evolution documentation tin can be plant here.

nopCommerce source code containing several free plugins included in the distribution package can be found hither.

Requirements

Plugin evolution for nopCommerce would require prior feel with Entity Framework Core two and ASP.Net Core MVC 2, understanding of DI, IoC and AOP, as well as familiarity with features and design of the nopCommerce platform.

Creating a project

A Grade Library project template is used for plugin development. It is recommended to shop a new project in \Plugins solution folder.

Naming conventions for plugin projects are the post-obit: Nop.Plugin.{Grouping}.{Name}. east.chiliad. Nop.Plugin. Payments.PayPalStandard. It is also common to use the post-obit naming template: {Solution namespace}.{Name}Plugin. east.chiliad. Company.Solution.PaymentMethodNamePlugin

After creating a project, you should edit *.csproj file by adding PropertyGroup chemical element to betoken the project'due south output path and Target.

Instance of *.csproj file: value if PLUGIN_OUTPUT_DIRECTORY should match plugin'southward name, e.g. Payments.PayPalStandard.

LISTING 1. PLUGIN *.CSPROJ FILE

<Project Sdk="Microsoft.Internet.Sdk">   <PropertyGroup>     <TargetFramework>netcoreapp2.one</TargetFramework>   </PropertyGroup>      <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">     <OutputPath>..\..\Presentation\Nop.Spider web\Plugins\PLUGIN_OUTPUT_DIRECTORY</OutputPath>     <OutDir>$(OutputPath)</OutDir>   </PropertyGroup>   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">     <OutputPath>..\..\Presentation\Nop.Web\Plugins\PLUGIN_OUTPUT_DIRECTORY</OutputPath>     <OutDir>$(OutputPath)</OutDir>   </PropertyGroup>   <!-- This target execute after "Build" target -->   <Target Proper noun="NopTarget" AfterTargets="Build">     <!-- Delete unnecessary libraries from plugins path -->     <MSBuild Projects="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" Backdrop="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />   </Target>    </Project>        

Next, create a plugin.json file in the root of your projection to describe the plugin.

Annotation: I this manual we consider nopCommerce four.10. nopCommerce three.90 and beneath take description.txt file with a unlike format.

Listing 2. PLUGIN.JSON EXAMPLE

{   "Group": "Payment methods",   "FriendlyName": "PayPal Standard",   "SystemName": "Payments.PayPalStandard",   "Version": "1.49",   "SupportedVersions": [ "4.ten" ],   "Writer": "nopCommerce team",   "DisplayOrder": 1,   "FileName": "Nop.Plugin.Payments.PayPalStandard.dll",   "Description": "This plugin allows paying with PayPal Standard"  }        

In the file properties specify value Copy if newer for holding Copy to Output Directory since information technology's necessary to copy this file to /Plugins folder of the web-awarding (Nop.Web) afterward it is built.

SystemName should exist unique. FileName should friction match a class library name.

Group property can accept any value; however, it is recommended to utilize ane of the following values reflecting the purpose of the plugin:

  • DiscountRules – rules for applying discounts.
  • ExchangeRates – currency commutation charge per unit providers.
  • ExternalAuth – external authentication providers.
  • Payments – payment gateway integrations.
  • Pickup – pickup point providers.
  • Shipping – shipping toll adding and tracking providers.
  • Tax – tax calculation providers.
  • Widgets – widgets.
  • Misc – whatsoever other plugins that can't be associated with one of the above categories.

Plugins can exist viewed, installed, configured or uninstalled in the corresponding admin section:

nopCommerce plugin development

FIG i. Listing OF PLUGINS

Standard settings of an installed plugin tin can exist edited by clicking Edit button.

nopCommerce plugin development

FIG 2. STANDARD PLUGIN SETTINGS

nopCommerce plugin development

FIG 3. USER-DEFINED PLUGIN SETTINGS

If a plugin has user-defined settings unique to this plugin (encounter department User-defined plugin settings), the Configure button is available for editing these settings.

Logging

While developing controllers, services, and tasks, it is recommended to employ an inbuilt logging service Nop.Services.Logging.ILogger to log information and error letters.

LISTING iii. LOGGING Data Message

//log data near the successful renew of the access token  _logger.Information(_localizationService.GetResource("Plugins.Payments.Foursquare.RenewAccessToken.Success"));        

During exception treatment, exception object tin can also be passed to the logging service:

LISTING 4. LOGGING EXCEPTION

catch (Exception exception)  {     //log fault on renewing of the access token     _logger.Mistake(_localizationService.GetResource("Plugins.Payments.Square.RenewAccessToken .Error"), exception);  }        

Log is stored in the database and is displayed in a respective admin section:

nopCommerce plugin development

FIG 4. LOG ADMIN SECTION

Dependency injection

nopCommerce uses Autofac as an inversion of a command container. When classes are being adult, all required service interfaces are defined as constructor parameters and their instances are automatically resolved and initialized during the respective grade example initializing.

To register classes by implementing respective service interfaces y'all should create an implementation of Nop.Core.Infrastructure.DependencyManagement.IDependencyRegistrar. nopCommerce scans through all assemblies to detect classes implementing a respective interface and registers them.

Plugin interface and base of operations form

Plugin should incorporate an implementation of Nop.Cadre.Plugins.IPlugin. Abstract class Nop.Cadre.Plugins.BasePlugin which implements this interface can be used equally a base course.

For some specific type of a plugin a respective derived interface that corresponds to the plugin'south purpose should exist implemented (run across FIG 5. Plugin interfaces below).

Install() and Uninstall() methods of a base interface are required to exist implemented to mark a plugin as installed and uninstalled respectively. (see Listing five below).

Listing 5. MARKING PLUGIN AS INSTALLED OR UNINSTALLED.

public virtual void Install()   {     PluginManager.MarkPluginAsInstalled(PluginDescriptor.SystemName);  }  public virtual void Uninstall()   {     PluginManager.MarkPluginAsUninstalled(PluginDescriptor.SystemName);  }        

PluginDescriptor is initialized automatically from data in plugin.json.

nopCommerce plugin development

FIG 5. PLUGIN INTERFACES

Besides this, these methods may contain one of the following:

  • User-defined plugin settings
  • Localization
  • Creating custom tables
  • Configuring scheduled tasks

It is recommended to have a await at the examples of plugins of unlike types in nopCommerce source code.

User-defined plugin settings

If a plugin has user-defined settings, it is required to develop model, view and controller for implementing plugin'due south configuration page. For this purpose, the following project folders are created: Controllers, Models and Views. Class holding properties for settings should implement Nop.Core.Configuration.ISettings. Naming convention is {PluginName}Settings.

User-divers settings can be either global or per store (nopCommerce allows to run multiple eCommerce stores in one nopCommerce setup).

Recommended proper noun for the model is ConfigurationModel.Configuration and {PluginName}Controller is for view and controller respectively.

A base course for all nopCommerce models is Nop.Web.Framework.Models.BaseNopModel from Nop.Web.Framework projection. NopResourceDisplayName attribute is used for localizing properties. It contains a respective resource key as cord.

If settings are applied per store, int ActiveStoreScopeConfiguration property is added to the model. New special backdrop are added to the model for each setting holding with the following naming template – {PropertyName}_OverrideForStore of bool blazon.

LISTING half-dozen. USER-Defined SETTINGS MODEL

public class ConfigurationModel : BaseNopModel  {     public int ActiveStoreScopeConfiguration { become; set up; }        [NopResourceDisplayName("Plugins.Payments.PayPalStandard.Fields.UseSandbox")]     public bool UseSandbox { get; set; }     public bool UseSandbox_OverrideForStore { get; fix; }        

Configuration view uses special layout _ConfigurePlugin. If settings are practical per store, StoreScopeConfiguration method is chosen asynchronously.

LISTING seven. USER-Defined SETTINGS VIEW

@model Nop.Plugin.Payments.PayPalStandard.Models.ConfigurationModel  @inject Nop.Core.IWebHelper webHelper  @{     Layout = "_ConfigurePlugin";  }     @expect Component.InvokeAsync("StoreScopeConfiguration")        

HTML form is used to shop settings server-side. Fields for each setting belongings are implemented using Bootstrap markup and helpers (meet sp Nop.Spider web.Framework.TagHelpers).

List 8. Class MARKUP

<form asp-controller="PaymentPayPalStandard" asp-action="Configure" method="post">     <div class="console-group">         <div class="console console-default">             <div form="console-body">                 @Html.Raw(T("Plugins.Payments.PayPalStandard.Instructions", $"{webHelper.GetStoreLocation()}Plugins/PaymentPayPalStandard/PDTHandler"))                 <div form="course-group">                     <div form="col-doctor-3">                         <nop-override-shop-checkbox asp-for="UseSandbox_OverrideForStore" asp-input="UseSandbox" asp-store-scope="@Model.ActiveStoreScopeConfiguration" />                         <nop-label asp-for="UseSandbox" />                     </div>                     <div class="col-md-ix">                         <nop-editor asp-for="UseSandbox" />                         <span asp-validation-for="UseSandbox"></span>                     </div>                 </div>  ...                 <div class="course-group">                     <div form="col-md-nine col-md-offset-three">                         <input type="submit" proper name="salvage" form="btn bg-primary" value="@T("Admin.Common.Save")" />                     </div>                 </div>             </div>         </div>     </div>  </form>        

Controller can be inherited from Nop.Web.Framework.Controllers.BasePluginController or from Nop.Spider web.Framework.Controllers.BasePaymentController in case of a payment method. Both of them are inherited from Nop.Web.Framework.Controllers.BaseController.

nopCommerce plugin development

FIG 6. PLUGIN CONTROLLERS

Controller should implement ii Configure actions:

  1. Reading user-divers settings – loading settings using nopCommerce service, passing data to model, returning view with model.
  2. Saving user-defined settings – passing information from model to settings and saving to database using nopCommerce service.

For both methods, Surface area attribute should be specified as "Admin" (see Listing 9 below).
Nop.Services.Configuration.ISettingService is used for working with settings. If you lot need to shop settings per shop, Nop.Core. IStoreContext is used. Nop.Services.Security.IPermissionService is used to validate permissions to modify settings.

List 9. LOADING USER-Defined SETTINGS

[AuthorizeAdmin]  [Area(AreaNames.Admin)]  public IActionResult Configure()  {     if (!_permissionService.Qualify(StandardPermissionProvider.ManagePaymentMethods))         return AccessDeniedView();        //load settings for a called store scope     var storeScope = _storeContext.ActiveStoreScopeConfiguration;     var payPalStandardPaymentSettings = _settingService.LoadSetting<PayPalStandardPaymentSettings>(storeScope);        var model = new ConfigurationModel     {         UseSandbox = payPalStandardPaymentSettings.UseSandbox,         BusinessEmail = payPalStandardPaymentSettings.BusinessEmail,         PdtToken = payPalStandardPaymentSettings.PdtToken,         PassProductNamesAndTotals = payPalStandardPaymentSettings.PassProductNamesAndTotals,         AdditionalFee = payPalStandardPaymentSettings.AdditionalFee,         AdditionalFeePercentage = payPalStandardPaymentSettings.AdditionalFeePercentage,         ActiveStoreScopeConfiguration = storeScope     };     if (storeScope > 0)     {         model.UseSandbox_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => x.UseSandbox, storeScope);         model.BusinessEmail_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => x.BusinessEmail, storeScope);         model.PdtToken_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => ten.PdtToken, storeScope);         model.PassProductNamesAndTotals_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => x.PassProductNamesAndTotals, storeScope);         model.AdditionalFee_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => x.AdditionalFee, storeScope);         model.AdditionalFeePercentage_OverrideForStore = _settingService.SettingExists(payPalStandardPaymentSettings, x => x.AdditionalFeePercentage, storeScope);     }        render View("~/Plugins/Payments.PayPalStandard/Views/Configure.cshtml", model);  }        

It's important to clear cache earlier saving settings so they take upshot (see Listing 10 below).

List 10. SAVING USER-DEFINED SETTINGS

[HttpPost]  [AuthorizeAdmin]  [AdminAntiForgery]  [Area(AreaNames.Admin)]  public IActionResult Configure(ConfigurationModel model)  {     if (!_permissionService.Authorize(StandardPermissionProvider.ManagePaymentMethods))         return AccessDeniedView();        if (!ModelState.IsValid)         return Configure();        //load settings for a called store scope     var storeScope = _storeContext.ActiveStoreScopeConfiguration;     var payPalStandardPaymentSettings = _settingService.LoadSetting<PayPalStandardPaymentSettings>(storeScope);        //save settings     payPalStandardPaymentSettings.UseSandbox = model.UseSandbox;     payPalStandardPaymentSettings.BusinessEmail = model.BusinessEmail;     payPalStandardPaymentSettings.PdtToken = model.PdtToken;     payPalStandardPaymentSettings.PassProductNamesAndTotals = model.PassProductNamesAndTotals;     payPalStandardPaymentSettings.AdditionalFee = model.AdditionalFee;     payPalStandardPaymentSettings.AdditionalFeePercentage = model.AdditionalFeePercentage;        /* Nosotros practice not clear enshroud after each setting update.      * This behavior can increase performance considering cached settings will not exist cleared       * and loaded from database after each update */     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, x => x.UseSandbox, model.UseSandbox_OverrideForStore, storeScope, imitation);     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, ten => x.BusinessEmail, model.BusinessEmail_OverrideForStore, storeScope, false);     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, x => x.PdtToken, model.PdtToken_OverrideForStore, storeScope, false);     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, ten => 10.PassProductNamesAndTotals, model.PassProductNamesAndTotals_OverrideForStore, storeScope, false);     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, x => x.AdditionalFee, model.AdditionalFee_OverrideForStore, storeScope, false);     _settingService.SaveSettingOverridablePerStore(payPalStandardPaymentSettings, x => x.AdditionalFeePercentage, model.AdditionalFeePercentage_OverrideForStore, storeScope, faux);        //at present clear settings cache     _settingService.ClearCache();        SuccessNotification(_localizationService.GetResource("Admin.Plugins.Saved"));        return Configure();  }        

Controller tin can implement not just configuration actions but other actions implementing plugin UI besides.

Localization

If a plugin uses letters displayed to user, tooltips and other UI texts, then respective texts should be implemented every bit localization resources stored for each used linguistic communication using Nop.Services.Localization.ILocalizationService.

User with admin permissions can then edit these resources in the admin area.

As described above (see department "Plugin interface and base of operations class"), plugin form has Install() and Uninstall() methods, which is used to implement localization using Nop.Services.Localization.ILocalizationService.AddOrUpdatePluginLocaleResource and Nop.Services.Localization.ILocalizationService.DeletePluginLocaleResource methods respectively.

LISTING 11. INSTALLING USER-Divers SETTINGS AND LOCALIZATION

public override void Install()  {     //settings     _settingService.SaveSetting(new PayPalStandardPaymentSettings     {         UseSandbox = true     });        //locales     _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Payments.PayPalStandard. Fields.AdditionalFee", "Boosted fee");  ...     base.Install();  }        

Naming convention for a localization resource key is the following: {PluginName}.{ResourceType}.{ResourceName}.

AddOrUpdatePluginLocaleResource method has a third optional parameter (cord languageCulture = nothing) that indicates linguistic communication and civilisation.

List 12. DELETING USER-DEFINED SETTINGS AND LOCALIZATION Resource DURING UNINSTALLING PLUGIN

public override void Uninstall()  {     //settings     _settingService.DeleteSetting<PayPalStandardPaymentSettings>();        //locales     _localizationService.DeletePluginLocaleResource("Plugins.Payments.PayPalStandard.Fields. AdditionalFee");  ...     base of operations.Uninstall();  }        

Resource can exist loaded using GetResource method (see example in Listing eight above).

Localization resource are rendered in markup using tags and helpers (due east.chiliad. T, nop-characterization) from Nop.Spider web.Framework.TagHelpers (see example in List 6 in a higher place)

Custom entities and database tables

A plugin can extend and/or change nopCommerce database structure without modifying nopCommerce code base. (run across the source code of Nop.Plugin.Pickup.PickupInStore).

Primal steps to perform:

  • Define new entities in a plugin projection
  • Develop the database context for these entities
  • Implement database mapping for these entities
  • Implement services for these entities

Defining new entities

Entity classes are defined in Domain projection folder and are inherited from Nop.Core. BaseEntity, which has Id property of int type.

Implementing database context

Database context and database mapping for entities are defined in Data project binder. Database context is inherited from Microsoft.EntityFrameworkCore.DbContext and implements Nop.Information.IDbContext. You should also implement Install/Uninstall methods for creating/deleting custom database tables which are called in Install() and Uninstall() plugin methods respectively.

LISTING thirteen. DATABASE CONTEXT METHODS FOR CREATING AND DELETING CUSTOM TABLES

public void Install()  {     this.ExecuteSqlScript(this.GenerateCreateScript());  }     public void Uninstall()  {     this.DropPluginTable(nameof(StorePickupPoint));  }        

Mapping entities to database tables

To create database tables the following interface should exist implemented: Nop.Cadre.Infrastructure.INopStartup. Recommended naming convention for a new class – PluginDbStartup. Recommended projection folder for a new class is Infrastructure.

Database mappings are created for each entity. Mappings are inherited from Nop.Information.Mapping.NopEntityTypeConfiguration.

Listing xiv. MAPPING ENTITY TO ITS TABLE

public override void Configure(EntityTypeBuilder<StorePickupPoint> builder)  {     architect.ToTable(nameof(StorePickupPoint));     builder.HasKey(point => bespeak.Id);        builder.Holding(indicate => betoken.PickupFee).HasColumnType("decimal(18, 4)");  }        

Services for new entities

To implement business concern logic for new entities, interfaces and classes of corresponding services should exist created in Services projection binder.

To register database context and services classes, Nop.Cadre.Infrastructure.DependencyManagement.IDependencyRegistrar should exist implemented. Recommended proper name for implementation is DependencyRegistrar. Recommended binder for implementation is Infrastructure project binder. Each entity likewise has an implementation of Nop.Cadre.Data.IRepository inherited from Nop.Data.EfRepository.

Listing 15. REGISTERING DATABASE CONTEXT, SERVICE AND REPOSITORY

public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)  {     builder.RegisterType<StorePickupPointService>().As<IStorePickupPointService>() .InstancePerLifetimeScope();        //data context     builder.RegisterPluginDataContext<StorePickupPointObjectContext>( "nop_object_context_pickup_in_store-pickup");        //override required repository with our custom context     builder.RegisterType<EfRepository<StorePickupPoint>>().As<IRepository<StorePickupPoint>>()         .WithParameter(ResolvedParameter.ForNamed<IDbContext>( "nop_object_context_pickup_in_store-pickup"))         .InstancePerLifetimeScope();  }        

Scheduled tasks

Scheduled tasks are usually used for a regular information exchange with third-political party systems. E.g. exporting itemize information to external marketplace or importing orders and customers from CRM. nopCommerce also has inbuilt scheduled tasks such as Clear cache, Transport emails or Delete guests.

Fundamental steps to perform:

  • Implement IScheduleTask
  • Insert/Remove task during Install/Uninstall of plugin
  • Manage task in the admin area

Implement IScheduleTask

Classes implementing IScheduleTask are recommended to keep in Tasks projection folder. It tin can also be Services project folder. Nop.Services.Tasks.IScheduleTask defines sole method Execute. Instance of implementation can be found in Nop.Plugin.Payments.Foursquare source code (class Nop.Plugin.Payments.Square.Services.RenewAccessTokenTask).

All tasks' business logic is encapsulated in the Execute method.

Scheduled Task is registered in plugin's Install method using Nop.Services.Tasks.IScheduleTaskService.InsertTask method. Appropriately, job should exist deleted in plugin's Uninstall method using Nop.Services.Tasks.IScheduleTaskService.DeleteTask method.

If a plugin is updated and the task appears in a new version of the plugin, the older version of the plugin should be deleted and the plugin should be reinstalled again. Otherwise, task would not be registered.

When registering a task, its description should exist formed using Nop.Core.Domain.Tasks.ScheduleTask class. Information technology is recommended to name a task after the organization name of its blazon. Earlier registering the task, information technology'south recommended to cheque whether it already exists by calling GetTaskByType method using the system name of its type.

Listing 16. DEFINING A Proper noun FOR A Chore

public static string GetTaskName<T>()  {     return $"{typeof(T).FullName}, {typeof(T).Associates.GetName().Name}";  }        

During registering the task its display name, launch interval, error beliefs are defined.

LISTING 17. REGISTERING TASK

string taskName = GetTaskName<ExportCatalogTask>();  ScheduleTask task = _scheduleTaskService.GetTaskByType(taskName);  if (job == null)  {     chore = new ScheduleTask()     {         Enabled = true,         Proper name = "retailCRM: Consign itemize",         Seconds = 60 * 60 * four,         StopOnError = false,         Blazon = taskName,     };  _scheduleTaskService.InsertTask(task);  }        

When the plugin is existence uninstalled, the chore is as well being deleted by its arrangement name in the Uninstall method. If multiple tasks are deployed with the plugin, yous can utilize loop to deleted them in social club to keep the code cleaner.

LISTING xviii. DELETING TASKS DURING UNINSTALLING THE PLUGIN

foreach (string typeName in new string[] {     GetTaskName<DownloadHistoryTask>(),     GetTaskName<ExportCatalogTask>(),     GetTaskName<UploadCustomerTask>(),     GetTaskName<UploadOrderTask>() })  {     ScheduleTask job = _scheduleTaskService.GetTaskByType(typeName);     if (task != zippo)         _scheduleTaskService.DeleteTask(task);  }        

Managing tasks in the admin area

A user with admin rights can view and edit tasks in the respective admin section. The task can also exist executed at any time using Run Now push (meet FIG seven. List of scheduled tasks). Final start date, last finish date and last success date are likewise displayed.

If an error occurred during the task execution, information technology's being logged and can be viewed in the arrangement log. If an error occurred during manual launch using Run at present command, it's besides displayed in UI. As a best practice, it'south recommended to use additional logging (see Listing four. Logging exception).

nopCommerce plugin development

FIG 7. Listing OF SCHEDULED TASKS

Events subscription

nopCommerce implements outcome-driven architecture and allows to subscribe to various system events. Information technology is useful for developing triggers that run specific business organization logic after specific events occur. Due east.1000. sending data to external systems such as sending new orders to CRM correct subsequently an order was placed. Unlike scheduled tasks, such triggers are beingness executed in real-time without delays. Below is the listing of key events.

Data modification:

  • Nop.Core.Events.EntityInsertedEvent – generic event of inserting new information
  • Nop.Cadre.Events.EntityUpdatedEvent – generic event of updating data
  • Nop.Core.Events.EntityDeletedEvent – generic event of deleting data

Note: if data inserting/updating/deleting occurs multiple times, each time an effect would be generated. E.g., Nop.Core.Events.EntityUpdatedEvent for an order occurs multiple times during the society placement. So, if you only need to handle gild placement, in that location is a number of other high-level events.

Client events:

  • Nop.Core.Domain.Customers.CustomerRegisteredEvent – event of registering a client
  • Nop.Core.Domain.Customers.CustomerLoggedinEvent – event of signing in
  • Nop.Core.Domain.Customers.CustomerLoggedOutEvent – issue of signing out
  • Nop.Core.Domain.Customers.CustomerPasswordChangedEvent – event of changing a countersign
  • Nop.Core.Domain.Customers.CustomerPermanentlyDeleted – event of deleting a client

Social club events:

  • Nop.Core.Domain.Orders.OrderPlacedEvent – event or order placement
  • Nop.Core.Domain.Orders.OrderVoidedEvent – event of placing an empty order
  • Nop.Core.Domain.Orders.OrderPaidEvent – issue of an order payment
  • Nop.Core.Domain.Orders.OrderCancelledEvent – event of canceling an order
  • Nop.Core.Domain.Orders.OrderRefundedEvent – consequence of an social club refund

Other events:

  • Nop.Core.Domain.Blogs.BlogCommentApprovedEvent – effect of blog comment approval
  • Nop.Cadre.Domain.Catalog. ProductReviewApprovedEvent – outcome of production review approving
  • Nop.Core.Domain.Letters.AdditionTokensAddedEvent – event of adding new tokens to non-campaign bulletin templates
  • Nop.Core.Domain.Letters.CampaignAdditionTokensAddedEvent – event of adding new tokens to message templates for campaigns
  • Nop.Core.Domain.Messages.MessageTokensAddedEvent – event of adding new tokens to all message templates
  • Nop.Core.Domain.Messages.EntityTokensAddedEvent – generic result of adding new tokens to an entity of a specific type
  • Nop.Core.Domain.Letters.EmailSubscribedEvent – event of an email subscription
  • Nop.Cadre.Domain.Messages.EmailUnsubscribedEvent – issue of canceling an electronic mail subscription
  • Nop.Core.Domain.News.NewsCommentApprovedEvent – event of news comment approval

Implementation of Nop.Services.Events.IConsumer is used for subscribing to events. HandleEvent method should be implemented to encapsulate result handling logic.

List 19. Treatment EVENT OF DELETING DISCOUNT Dominion

public partial form DiscountRequirementEventConsumer :      IConsumer<EntityDeletedEvent<DiscountRequirement>>  {     private readonly ISettingService _settingService;     public DiscountRequirementEventConsumer(ISettingService settingService)     {         this._settingService = settingService;     }     public void HandleEvent(EntityDeletedEvent<DiscountRequirement> eventMessage)     {         var discountRequirement = eventMessage?.Entity;         if (discountRequirement == zip)             return;            //delete saved restricted customer part identifier if exists         var setting = _settingService.GetSetting(             string.Format(DiscountRequirementDefaults.SettingsKey, discountRequirement.Id));         if (setting != null)             _settingService.DeleteSetting(setting);     }  }        

nopCommerce checks all assemblies and registers all classes implementing IConsumer to send events to them. (see source code in Nop.Web.Framework.Infrastructure. DependencyRegistrar).

Widgets

Nop.Services.Cms.IWidgetPlugin is used for developing widgets that can be embedded every bit part of a view on different pages. Plugin implementation's GetWidgetZones method returns a list of so called widget zones – placeholders – where a widget can be embedded..
Nop.Web.Framework.Infrastructure.PublicWidgetZones class contains all zones for nopCommerce UI where widgets can exist embedded. Nop.Web.Framework.Infrastructure.AdminWidgetZones class contains widget zones in the admin area.

Plugins Nop.Plugin.Widgets.GoogleAnalytics and Nop.Plugin.Widgets.NivoSlider are examples of the widget plugins.

MVC pattern is used for developing widgets, therefore model, partial view and controller should be developed. View component based on Nop.Web.Framework.Components. NopViewComponent should too exist developed. To reference additional namespaces _ViewImports.cshtml file should be added to Views projection folder.

Listing twenty. EXAMPLE OF _VIEWIMPORTS.CSHTML

@inherits Nop.Web.Framework.Mvc.Razor.NopRazorPage<TModel>  @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers  @addTagHelper *, Nop.Web.Framework     @using Microsoft.AspNetCore.Mvc.ViewFeatures  @using Nop.Web.Framework.UI  @using Nop.Web.Framework.Extensions  @using Arrangement.Text.Encodings.Web        

The recommended name for a partial view is PublicInfo.cshtml. Best practices for development are the same as for ASP.Net Core MVC two.

User with admin permission can enable and disable widgets in admin section besides as specify their brandish order. Configure and Edit buttons allow editing user-divers and standard settings of a plugin respectively (see FIG ii. Standard plugin settings and FIG 3. User-defined plugin settings).

nopCommerce plugin development

FIG eight. LIST OF WIDGETS

Generic attributes

nopCommerce has a flexible blueprint blueprint for extending standard entities without modifying database tables. It is a GenericAttribute table that has non-strange key relations with nopCommerce entities and stores their custom attributes.

Beneath is the list and descriptions of GenericAttribute columns:

Column Type Description
ID int Chief fundamental
EntityId int non-FK reference to the entity to which an aspect belongs to
KeyGroup nvarchar(400) Entity table where entity attribute that belongs to it is stored. E.g. "Client" for a Customer entity or "Order" for an Social club entity
Key nvarchar(400) Attribute cardinal
Value nvarchar(MAX) Attribute value
StoreId int Store Id if an attribute is defined per store or 0 if the attribute is defined globally

Example:
Id: i
EntityId: 2
KeyGroup: Customer
Key: FirstName
Value: John
StoreId: 0
A client whose Id equals 2 has a get-go name "John" defined as an aspect globally.

Generic attributes enable to extend entities with custom attributes belongings additional information. For case, a standard Client entity implements most of the customer profile fields as generic attributes. Basic nopCommerce feature of extending registration fields also uses GenericAttribute under the hood. By default, generic attributes cannot be edited in the admin expanse and common practice is to implement additional editable fields on entities in the admin surface area every bit part of the plugin.

nopCommerce plugin development

FIG nine. CUSTOM CUSTOMER ATTRIBUTES

Note: you lot should carefully design your plugin earlier choosing between Generic attributes arroyo and extending entity via custom entities and database tables approach (see "Custom entities and database tables department in a higher place"). Generic attributes don't concord foreign keys and all attribute values are strings. So, overuse of GenericAttributes may lead to issues with functioning and information integrity when the format of some attribute has been changed (older records may contain inconsistent values) or entity which is referenced in GenericAttribute tabular array may no longer exist. For this reason, in some cases, you may use GenericAttribute while in other cases you should stick to more complex notwithstanding more correct and scalable approach of modifying the database via a plugin.

Writing and reading generic attributes is washed by using IGenericAttribute service and extension methods on entities (come across LISTING 21 below)

Listing 21. READING AND WRITING GENERIC ATTRIBUTES

public virtual ActionResult Create(CustomerModel model, bool continueEditing, FormCollection form)  {     //writing generic attributes     if (_customerSettings.GenderEnabled)         _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Gender, model.Gender);     _genericAttributeService.SaveAttribute(client, SystemCustomerAttributeNames.FirstName, model.FirstName);     _genericAttributeService.SaveAttribute(client, SystemCustomerAttributeNames.LastName, model.LastName);     //reading generic attributes     string gender = customer.GetAttribute<cord>(SystemCustomerAttributeNames.Gender)  }        

Method overriding

One of the core approaches of extending basic nopCommerce business organisation logic in plugin is method overriding. This is one of the well-nigh powerful however simple approaches. Eastward.thou. if we demand to change the behaviour of adding products to cart, nosotros can implement child grade inherited from ShoppingCartService in our plugin and override AddToCart method.

Listing 22. OVERRIDING BASE Course METHOD

public class ExtendedShoppingCartService: ShoppingCartService, IShoppingCartService  {     public override IList AddToCart(Customer customer, Product production, ShoppingCartType shoppingCartType, int storeId, cord attributesXml = cypher, decimal customerEnteredPrice = 0, DateTime? rentalStartDate = goose egg, DateTime? rentalEndDate = goose egg, int quantity = 1, bool automaticallyAddRequiredProductsIfEnabled = truthful)     {         //overridden business logic goes here  …     }  }        

To avoid copy-pasting code from the base grade' method that should be reused, telephone call base of operations course via base.. It's also a adept practice to pattern your customization in a way that child class contains minimum of code and calls base class' method to reuse its lawmaking.

In club to tell nopCommerce to employ our child class instead of its parent when resolving dependencies, we need to register our class using plugin'south implementation of Nop.Cadre.Infrastructure.DependencyManagement.IDependencyRegistrar.

List 23. REGISTERING CHILD CLASS

public void Annals(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)  {     …    builder.RegisterType<ExtendedShoppingCartService>().As<Orders.IShoppingCartService>().InstancePerLifetimeScope();     …  }        

Later registering child course all IShoppingCartServices instances will be resolved as ExtendedShoppingCartService instead of a standard nopCommerce's ShoppingCartService. In guild to verify that plugin's kid class is registered after its parent, you should specify Nop.Core.Infrastructure.DependencyManagement.IDependencyRegistrar.Society belongings to be higher than in nopCommerce's IDependencyRegistrar implementation. For example, by setting information technology to int.MaxValue. And then child class will be registered after its parent and will accept college priority.

Note: fifty-fifty though method overriding is a powerful customization approach it involves risks that should exist addressed. One of the common risks is the situation of conflicting plugins when several plugins override the aforementioned method which leads to an unexpected behavior. You should minimize using tertiary-political party plugins with airtight source code (because they have unknown method overrides) and should advisedly design your own plugins by fugitive overriding the same method by different plugins or utilise Nop.Cadre.Infrastructure.DependencyManagement.IDependencyRegistrar.Gild property in your plugins to manage priority order when several plugins extending the same methods are deployed.

mosleyscretwert.blogspot.com

Source: https://isdk.pro/nopcommerce-plugin-development-manual/

0 Response to "Azure Nopcommerce File Upload Fails After Processing"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel