November Happy Hour will be moved to Thursday December 5th.
November Happy Hour will be moved to Thursday December 5th.
An add-on extends the functionality of the Episerver website with initializable modules, gadgets, visitor group criteria, virtual path providers, page and search providers, and so on. This topic provides guidelines and recommendations for developing and packaging custom add-ons.
Episerver and third-parties can develop add-on modules. An add-on is packaged as a NuGet package and follows a set of guidelines. The module system (or Shell modules) defines the folder structure and configuration system that an add-on uses, and is a built-in module system to extend the user interface. You can use it to build any module.
Episerver loads and processes add-on assemblies during site start-up, so an add-on can contain components like InitializableModule and plug-ins that require assembly scanning. Use the following guidelines to develop an add-on.
User control example:
<%@ Control Language="C#" AutoEventWireup="false" CodeBehind="Map.ascx.cs" Inherits="EPiServer.Research.DynamicContent.Map, GoogleMapsDynamicContent" %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoredModel>" %>
<%@ Assembly Name="EPiServer.Samples.Module" %>
<%@ Import Namespace="EPiServer.Samples.Module.Models" %>
Resolving a path to an add-on resource by type
// MapDynamicContent is type from add-on assembly:
string pathToControl = Paths.ToResource(typeof(MapDynamicContent), "Map.ascx");
// load control using resolved path:
page.LoadControl(pathToControl);
string pathToAddonScript = Paths.ToClientResource(typeof(MapDynamicContent), "ClientResources/MapContent.js");// register client script using resolved path:
Page.ClientScript.RegisterClientScriptInclude("MapContent.js", pathToAddonScript);
You should define paths to plug-in resources as follows:
[GuiPlugIn(UrlFromModuleFolder="Control.ascx")]
[DynamicContentPlugIn(ViewUrlFromModuleFolder ="View.ascx")]
[RenderDescriptor(Path = "Blocks/SampleBlockControl.ascx")]
The resolved virtual path to the block template in a public add-on is ~/modules/<package ID>/Blocks/SampleBlockControl.ascx.
The directory structure where template files reside, must follow the namespace convention if the Path property of RenderDescriptorAttribute of a page or a block template is not set.
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>Google Maps dynamic content</title>
<authors>$author$</authors>
<owners />
<iconUrl>http://world.episerver.com/PageFiles/3/Icons/Nuget.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Allows to add Google Maps as dynamic content on pages.</description>
<tags>EPiServerPublicModulePackage Google Maps Dynamic</tags>
<dependencies>
<dependency id="EPiServer.Samples.SampleFoundation" version="1.2" />
<dependency id="EPiServer.CMS.UI" version="7.5" />
</dependencies>
</metadata>
</package>
Public and protected add-ons differ in user access rights required to access the add-on files and routes.
Public and protected add-ons have the following file locations and virtual paths:
Note: Mark protected add-ons with the EPiServerModulePackage tag. Mark public add-ons with the EPiServerPublicModulePackage tag.
If your add-on requires you to install other add-ons on the site, add these to the list of dependencies in the nuspec file to ensure that the other add-ons get installed when a user chooses to install your add-on. If the other add-ons cannot install to the environment, the installation aborts.
Add-on packages must follow the Semantic Versioning (SemVer) scheme for versioning for the package itself to have a common understanding of which versions are compatible and which versions introduce breaking changes.
SemVer means having version numbers in the format Major.Minor.Patch, where the different segments correspond to:
For example, if you require the feature set from version 1.3 of a dependency, set [1.3,2) as the version range, which accepts versions that are known to be compatible. When version 2.0 is released, it may or may not turn out to be compatible. If it is compatible, the version range can be changed to [1.3,3) in the next update. Otherwise the version range, after your code base is changed to run with version 2.0 of this dependency, is changed to something like [2.0,3). See NuGet Docs for version ranges in NuGet.
A prerequisite is a dependency that your add-on has to a product that is not yet installed, but must be installed for the add-on package to function. Assemblies installed in the application and system packages (for example, EPiServer.CMS.Core and EPiServer.CMS.UI) are represented in the NuGet environment as virtual packages to which you can add dependencies. The names and version numbers of these packages are based on the assembly names and assembly versions. Dependencies must be actual NuGet packages for Visual Studio-enabled add-ons.
The package directory structure should follow the conventions in NuGet Docs. If you are going to create the package from a Visual Studio project, run the following command:
nuget pack AddOnProject.csproj
Alternatively, if you have prepared a NuGet manifest (nuspec) file and a convention-based directory structure for the actual package content, you can create the package with the following command:
nuget.exe pack addondirectory\addon.nuspec
Use the NuGet Package Explorer GUI tool to view metadata and create new packages. The following example shows the Google Maps add-on package opened in NuGet Package Explorer.
You should attach dependencies to ordinary NuGet packages rather than as references to assemblies. In most scenarios, the primary assembly of the NuGet package matches the ID of the package.
Content files must have a path in the nuspec as they appear on site, which implies that paths for content files should include \modules\<packageid> for public add-ons. For example:
<file src="Settings.aspx" target="Content\modules\<packageId>\Settings.aspx" />
...and modules\_protected\<packageid> for protected add-ons. For example:
<file src="Views\Setting\Index.aspx" target="Content\modules\_protected\<packageId>\Views\Setting\Index.aspx" />
The NuGet package should contain a module.config file in the package root (for example, <file src="module.config" target="Content\modules\<packageId>\module.config" />). The module.config must contain a tags attribute that contains either EPiServerModulePackage or EPiServerPublicModulePackage, which is is required for the Episerver add-on user interface to distinguish the add-on from other shell modules. The module.config also can contain a description attribute that describes the module in the add-on UI. It also should list assemblies the package contains. The following code sample shows a minimal module.config.
<?xml version="1.0" encoding="utf-8"?>
<module loadFromBin="false" description="Allows to run various support tools on the site." tags=" EPiServerModulePackage ">
<assemblies>
<add assembly="DeveloperTools" />
</assemblies>
</module>
The EPiServer.Packaging.Converter converts old add-ons to a format that lets the add-on be installed from Visual Studio. A readme file describes how to use the converter.
Deliver content in your add-on as a compressed ZIP archive by compressing the whole content structure below the package folder to a zip file. Give it the same name as the package and place within the package directory, for example:
Content\modules\<packageId>\<packageId>.zip
Episerver scans module directories during startup and adds a virtual path provider for any archive that is found as long as it follows this convention.
Using this feature is NOT recommended if your module contains large files because the content is kept in a memory cache. However, it is useful if you are developing a Visual Studio add-on that contains many small files because it prevents the module from adding the files to the Visual Studio project of the developers installing your add-on. If you want to debug files in an add-on that uses this functionality, extract the archive into its current directory and delete or rename the archive.
Execute custom code at the following extension points, when certain actions are performed:
If your add-on requires executing custom code only on web application start-up and does not need to be notified about installations, updates or deletions, consider using IInitializableModule.
To execute custom code when the status of the add-on package changes, the custom code should include a class inherited from the abstract class EPiServer.Packaging.PackageInitializer in the EPiServer.Packaging assembly:
public abstract class PackageInitializer : IInitializableModule, IPackageNotification
{
#region Implementation of IInitializableModule
public virtual void ConfigureContainer(ServiceConfigurationContext context);
public virtual void Initialize(InitializationEngine context);
public virtual void Uninitialize(InitializationEngine context);
public virtual void Preload(string[] parameters);
#endregion
#region Implementation of IPackageNotification
public abstract void AfterInstall();
public abstract void AfterUpdate();
public abstract void BeforeUninstall();
#endregion
}
The PackageInitializer class combines the IInitializableModule and IPackageNotification interfaces. Inheritors of this class are instantiated and executed by the Episerver Framework initialization system in the same manner as for the regular IInitializableModule.
The Initialize method in PackageInitializer checks if the add-on (package ID) containing the assembly with the inheriting class is newly installed, and calls the AfterInstall method if necessary, or if the add-on is newly updated calls AfterUpdate if necessary.
When overriding the Initialize method, you should call the base implementation before proceeding with the initialization to ensure that the AfterInstall and AfterUpdate methods are executed before the initialization.
The BeforeUninstall method is called before the package contents are removed, when the user clicks the Uninstall button in the add-on system.
The AfterInstall method is called after the add-on installation is complete and only the first time the application starts, (as opposed to the Initialize method which is called each time the application starts).
Do the following to get custom code executed after an add-on installation:
The add-on installation and the point where the AfterInstall method is called:
The AfterUpdate method executes after the add-on update is complete and is called only the first time the application starts after updating an add-on.
Do the following to execute custom code after an add-on update:
The add-on installation and the point where the AfterUpdate method is called:
The BeforeUninstall method executes immediately after the user clicks Uninstall in the add-on management user interface, but before the actual uninstallation takes place. If an exception occurs in the BeforeUninstall method, the uninstallation is aborted. It does not trigger when uninstalling an add-on using Visual Studio.
Do the following to execute custom code before add-on unistallation:
The process of add-on uninstallation:
If an add-on is dependent on other systems or add-ons, you need to indicate these dependencies in the ModuleDependencies element so that initialization methods are called after those of the listed dependencies. To load the add-on automatically when a dependency is loaded, set the type attribute to RunAfter. This is needed for CMS UI add-ons, otherwise they are not loaded.
Last updated: Jan 21, 2016