Skip to content

Latest commit

 

History

History
208 lines (153 loc) · 16.6 KB

File metadata and controls

208 lines (153 loc) · 16.6 KB

✔️ Getting started with modules

Overview

Modules are designed to extend Smartstore functionality in any way you can imagine. There are no limits to what features they can add.

Here are a few examples of what modules can do:

  • alter the way the app operates
  • change workflows
  • modify / extend UI
  • overwrite services

Modules are sets of extensions that compile into a single assembly in order to be re-used in other Smartstore shops. Even though it may use Smartstore APIs, they are no necessity. The only two requirements for a module project are:

  • module.json: A manifest file describing the metadata of a module.
  • Module.cs: A class that implements IModule and contains (un-)installation routines.

Some special, mostly commerce related features are encapsulated as providers in the Smartstore ecosystem. A module can expose as many of these providers as needed.

Represented by their interfaces, provider types are:

  • IPaymentMethod: Payment providers (PayPal, Stripe, AmazonPay, offline payment etc.)
  • ITaxProvider: Tax calculation
  • IShippingRateComputationMethod: Shipping fee calculation
  • IExportProvider: Data export (Shops, products, orders etc.)
  • IMediaStorageProvider: Storage for media file blobs
  • IOutputCacheProvider: Storage for output cache items
  • IWidget: Content rendering in UI
  • IExternalAuthenticationMethod: External authenticators (Google, Facebook etc.)
  • IExchangeRateProvider: Live currency rates

Module structure

A module is a regular Class Library project in the solution. It should be placed in the /src/Smartstore.Modules/ directory in the root of your solution.

{% hint style="warning" %} Do not confuse this with the /src/Smartstore.Web/Modules/ directory, which is the build target for module. From here the application loads module assemblies into the app-domain dynamically. {% endhint %}

If your project directory is located elsewhere, you should create a symlink that links to the actual location.

{% hint style="info" %} It is good practice to use the -sym suffix in symlink sources, because they are git-ignored. {% endhint %}

For module projects we recommend the naming convention {Organization}.{ModuleName}, but you can choose any name you wish. It should also be the root namespace and the module system name.

If your module is called MyOrg.MyGreatModule, the .csproj project file should look like this:

<Project Sdk="Microsoft.NET.Sdk.Razor">
    <PropertyGroup>
	<Product>A great module for Smartstore</Product>
	<OutputPath>..\..\Smartstore.Web\Modules\MyOrg.MyGreatModule</OutputPath>
	<OutDir>$(OutputPath)</OutDir>
    </PropertyGroup>
</Project>

Each time the solution is built, your module will be compiled and copied to the OutputPath directory specified here.

Smartstore.Module.props

The file Smartstore.Build/Smartstore.Module.props defines shared properties for modules. It is auto-included into every project located in Smartstore.Modules/.

Among other things it specifies files and directories...:

  • to be copied to the OutputPath, if they exist.
    • module.json
    • wwwroot/
    • Localization/
    • Views/
  • not to be copied to the OutputPath.

{% hint style="warning" %} This is important, because the build process would copy the whole dependency graph to the output, which produces too much noise and file redundancy. {% endhint %}

Project & Package references

All projects located in the Smartstore.Modules directory reference Smartstore, Smartstore.Core and Smartstore.Web.Common projects by default.

{% hint style="info" %} You can also reference Smartstore.Web to access model types declared there. But add the following lines to the project file to prevent your project copying dependent files to your OutputPath:

<ItemGroup>
    <ProjectReference Include="..\..\Smartstore.Web\Smartstore.Web.csproj">
        <Private>False</Private>
        <CopyLocal>False</CopyLocal>
        <CopyLocalSatelliteAssemblies>False</CopyLocalSatelliteAssemblies>
    </ProjectReference>
</ItemGroup>

{% endhint %}

You can reference any NuGet package you wish, but special consideration is required for packages that are not referenced by the app core (private). These need to be listed under PrivateReferences in module.json (see below), otherwise a runtime error is thrown.

Manifest: module.json

This file describes your module to the system and is used by the Plugin Manager in it's administration screen.

Here is an example of a working module.json file.

{% code title="module.json" %}

{
    "$schema": "../module.schema.json",
    "Author": "MyOrg",
    "Group": "Payment",
    // Required. Module system name.
    "SystemName": "MyOrg.MyGreatModule",
    // Required. English friendly name.
    "FriendlyName": "A great module for Smartstore",
    "Description": "Lorem ipsum",
    // Required. The current version of module.
    "Version": "5.0",
    "MinAppVersion": "5.0",
    "Order": 1,
    "ResourceRootKey": "Plugins.Payments.MyGreatModule",
    "ProjectUrl": "https://myorg.com",
    "PrivateReferences": [
        "MiniProfiler.Shared",
        "MiniProfiler.AspNetCore",
        "MiniProfiler.AspNetCore.Mvc",
        "MiniProfiler.EntityFrameworkCore"
    ]
}

{% endcode %}

{% hint style="info" %} The properties SystemName, FriendlyName and Version are required. {% endhint %}

The following table explains the schema.

NameTypeDescription
AssemblyNamestringThe assembly name. Default: '{SystemName}.dll'
AuthorstringThe author's name.
FriendlyName *stringA readable, easy to understand, english name.
Groupenum

A conceptual group name. Used to visually categorize modules in listings.

Possible values: Admin, Marketing, Payment, Shipping, Tax, Analytics, CMS, Media, SEO, Data, Globalization, Api, Mobile, Social, Security, Developer, Sales, Design, Performance, B2B, Storefront, Law.

DependsOnarrayArray of module system names the module depends on.
DescriptionstringA short english description of the module.
MinAppVersionstring

The minimum compatible application version, e.g. '5.0.2'.

The module will be unavailable, if the current app version is lower than this value.

OrderintegerThe display order in the module manager group.
PrivateReferencesarray

Optional array of private dependency package names that a module references.

By default referenced packages are not copied to the OutputPath. It is assumed, that the application core already references them. Any private module package should be listed here, including the complete dependency chain.

ProjectUrlstringLink to the project's or author's homepage.
ResourceRootKeystringRoot key for language resources (see Localizing modules).
SystemName *stringModule system name. Usually the assembly name without the extension.
TagsstringComma-separated tags
Version *stringThe current version of the module e.g. '5.1.0'

Module entry class

Every module needs an entry class containing the bare minimum of the un- and install methods. To be recognized as such the class must implement the IModule interface.

{% hint style="info" %} It is also recommended to derive from the abstract ModuleBase instead of implementing IModule, because it contains some common implementations. {% endhint %}

The installation method IModule.InstallAsync() is called every time the module is installed. Respectively the same goes for the uninstall method IModule.UninstallAsync().

{% hint style="info" %} It is good practice not to delete any custom module data from the database while uninstalling, in case the user wants to re-install the module later. {% endhint %}

By convention the file named Module.cs is internal and is placed in the project's root directory.

{% hint style="info" %} If your module contains exactly one feature provider, it is recommended to let the entry class implement the provider interface directly. {% endhint %}

The IConfigurable interface is used to expose the route to a module configuration page linked to the Plugin Manager UI.

The following code shows an example of a working Module.cs file.

{% code title="Module.cs" %}

internal class Module : ModuleBase, IConfigurable
{
    public RouteInfo GetConfigurationRoute()
        => new("Configure", "MyGreatAdmin", new { area = "Admin" });

    public override async Task InstallAsync(ModuleInstallationContext context)
    {
        // Saves the default state of a settings class to the database 
        // without overwriting existing values.
        await TrySaveSettingsAsync<MyGreatModuleSettings>();
        
        // Imports all language resources for the current module from 
        // xml files in "Localization" directory (if any found).
        await ImportLanguageResourcesAsync();
        
        // VERY IMPORTANT! Don't forget to call.
        await base.InstallAsync(context);
    }

    public override async Task UninstallAsync()
    {
        // Deletes all "MyGreatModuleSettings" properties settings from the database.
        await DeleteSettingsAsync<MyGreatModuleSettings>();
        
        // Deletes all language resource for the current module 
        // if "ResourceRootKey" is module.json is not empty.
        await DeleteLanguageResourcesAsync();
        
        // VERY IMPORTANT! Don't forget to call.
        await base.UninstallAsync();
    }
}

{% endcode %}

Files & Folders Best Practices

There are some conventions on how to organize the files and directories within a project. Though there is no obligation to comply, it makes things predictable and easier to maintain.

The following is an exhaustive list of files & folders.

EntryDescription
📁 App_DataApp specific (cargo) data like templates, sample files etc. that needs to be published.
📁 BlocksPage Builder Block implementations (see Page Builder and Blocks).
📁 BootstrappingBootstrapping code like Autofac modules, DI extensions etc.
📁 ClientRESTful API clients
📁 ComponentsMVC View Components (see Controllers and ViewComponents)
📁 ConfigurationSettings class implementations (see Configuration)
📁 ControllersMVC Controllers (see Controllers and ViewComponents)
📁 DomainDomain entities (see Domain)
📁 ExtensionsStatic extension method classes
📁 FiltersMVC Filters (see Filters)
📁 HooksHook implementations (see Hooks)
📁 LocalizationLocalization files (see Localizing modules)
📁 MediaMedia system related classes
📁 MigrationsFluent data migrations (see Database Migrations)
📁 ModelsView model classes (see Data Modelling)
📁 ProvidersProvider implementations
📁 SecuritySecurity related classes
📁 ServicesService classes
📁 UtilsUtilities
📁 TasksTask scheduler jobs (see Scheduling)
📁 TagHelpersTag Helpers
📁 ViewsRazor view/template files
📁 wwwrootStatic files (including Sass)
🗄️ AdminMenu.csAdmin menu hook (see Menus)
🗄️ CacheableRoutes.csRoute registrar for output cache (see Output Cache)
🗄️ Events.csEvent handler methods (see Events)
🗄️ Module.cs *Required. Module entry class (implements IModule).
🗄️ module.json *Required. Module metadata manifest.
🗄️ Permissions.csModule permissions (see Security)
🗄️ Startup.csModule bootstrapper (see Bootstrapping)