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.

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

It is good practice to use the -sym suffix in symlink sources, because they are git-ignored.

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.

Project & Package references

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

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>

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.

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"
    ]
}

The properties SystemName, FriendlyName and Version are required.

The following table explains the schema.

Name
Type
Description

AssemblyName

string

The assembly name. Default: '{SystemName}.dll'

Author

string

The author's name.

FriendlyName *

string

A readable, easy to understand, english name.

Group

enum

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.

DependsOn

array

Array of module system names the module depends on.

Description

string

A short english description of the module.

MinAppVersion

string

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.

Order

integer

The display order in the module manager group.

PrivateReferences

array

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.

ProjectUrl

string

Link to the project's or author's homepage.

ResourceRootKey

string

Root key for language resources (see Localizing modules).

SystemName *

string

Module system name. Usually the assembly name without the extension.

Tags

string

Comma-separated tags

Version *

string

The 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.

It is also recommended to derive from the abstract ModuleBase instead of implementing IModule, because it contains some common implementations.

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

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.

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

If your module contains exactly one feature provider, it is recommended to let the entry class implement the provider interface directly.

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.

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();
    }
}

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.

Entry
Description

πŸ“ App_Data

App specific (cargo) data like templates, sample files etc. that needs to be published.

πŸ“ Blocks

Page Builder Block implementations (see Page Builder and Blocks).

πŸ“ Bootstrapping

Bootstrapping code like Autofac modules, DI extensions etc.

πŸ“ Client

RESTful API clients

πŸ“ Components

MVC View Components (see Controllers and ViewComponents)

πŸ“ Configuration

Settings class implementations (see Configuration)

πŸ“ Controllers

MVC Controllers (see Controllers and ViewComponents)

πŸ“ Domain

Domain entities (see Domain)

πŸ“ Extensions

Static extension method classes

πŸ“ Filters

MVC Filters (see Filters)

πŸ“ Hooks

Hook implementations (see Hooks)

πŸ“ Localization

Localization files (see Localizing modules)

πŸ“ Media

Media system related classes

πŸ“ Migrations

Fluent data migrations (see Database Migrations)

πŸ“ Models

View model classes (see Data Modelling)

πŸ“ Providers

Provider implementations

πŸ“ Security

Security related classes

πŸ“ Services

Service classes

πŸ“ Utils

Utilities

πŸ“ Tasks

Task scheduler jobs (see Scheduling)

πŸ“ TagHelpers

Tag Helpers

πŸ“ Views

Razor view/template files

πŸ“ wwwroot

Static files (including Sass)

πŸ—„οΈ AdminMenu.cs

Admin menu hook (see Menus)

πŸ—„οΈ CacheableRoutes.cs

Route registrar for output cache (see Output Cache)

πŸ—„οΈ Events.cs

Event handler methods (see Events)

πŸ—„οΈ Module.cs *

Required. Module entry class (implements IModule).

πŸ—„οΈ module.json *

Required. Module metadata manifest.

πŸ—„οΈ Permissions.cs

Module permissions (see Security)

πŸ—„οΈ Startup.cs

Module bootstrapper (see Bootstrapping)

Last updated

Was this helpful?