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 implementsIModuleand 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 calculationIShippingRateComputationMethod: Shipping fee calculationIExportProvider: Data export (Shops, products, orders etc.)IMediaStorageProvider: Storage for media file blobsIOutputCacheProvider: Storage for output cache itemsIWidget: Content rendering in UIIExternalAuthenticationMethod: External authenticators (Google, Facebook etc.)IExchangeRateProvider: Live currency rates
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.
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 %}
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.
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.
| 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 |
| 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' |
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 %}
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) |