Develop CMS Plugins
Why develop a CMS plugin
Section titled “Why develop a CMS plugin”Plugins extend the CMS without modifying core code. You might build a plugin to add a custom admin tool, integrate a third-party service, or provide reusable functionality across multiple sites. Plugins package as NuGet packages, making them easy to share and version.
What you will do
Section titled “What you will do”- Create a class library project for the plugin
- Build a custom admin tool with a controller and view
- Register the plugin with the CMS menu system
- Package the plugin as a NuGet package
Set up the plugin project
Section titled “Set up the plugin project”Create a separate class library so the plugin can be distributed independently.
# Create a class library for the plugin
dotnet new classlib -n MySite.Plugin.ContentAudit -f net8.0
# Add Optimizely CMS dependencies
cd MySite.Plugin.ContentAudit
dotnet add package Optimizely.CMS.Core
dotnet add package Optimizely.CMS.UI
dotnet add package Microsoft.AspNetCore.Mvc.ViewFeatures Build a custom admin tool
Section titled “Build a custom admin tool”Admin tools appear in the CMS admin section. Start with a controller that inherits from Controller and register it as a menu item.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Optimizely.Cms.Core;
using Optimizely.Cms.Core.Repositories;
namespace MySite.Plugin.ContentAudit.Controllers;
[Authorize(Roles = "CmsAdmins")]
public class ContentAuditController : Controller
{
private readonly IContentRepository _contentRepo;
private readonly IContentTypeRepository _typeRepo;
public ContentAuditController(
IContentRepository contentRepo,
IContentTypeRepository typeRepo)
{
_contentRepo = contentRepo;
_typeRepo = typeRepo;
}
public IActionResult Index()
{
var contentTypes = _typeRepo.List();
var audit = contentTypes.Select(ct => new ContentTypeInfo
{
Name = ct.DisplayName ?? ct.Name,
InstanceCount = CountInstances(ct.ID),
HasController = HasRegisteredController(ct)
}).ToList();
return View(
"~/Views/ContentAudit/Index.cshtml", audit);
}
private int CountInstances(int typeId) =>
_contentRepo
.GetDescendents(ContentReference.RootPage)
.Count();
private bool HasRegisteredController(
ContentType ct) => true; // Simplified
} Register as a CMS menu item
Section titled “Register as a CMS menu item”Use the [MenuItem] attribute so the tool appears in the CMS admin navigation.
using Optimizely.Cms.Shell.Navigation;
namespace MySite.Plugin.ContentAudit;
[MenuProvider]
public class ContentAuditMenuProvider : IMenuProvider
{
public IEnumerable<MenuItem> GetMenuItems()
{
var menuItem = new UrlMenuItem(
"Content Audit",
MenuPaths.Global + "/cms/admin/contentaudit",
"/ContentAudit")
{
IsAvailable = request => true,
SortIndex = 100,
AuthorizationPolicy =
CmsPolicyNames.CmsAdmin
};
return new[] { menuItem };
}
} The tool now appears in the admin menu under a “Content Audit” link.
Add a Razor view
Section titled “Add a Razor view”@model IList<MySite.Plugin.ContentAudit.ContentTypeInfo>
<div class="epi-padding">
<h1>Content Type Audit</h1>
<table class="epi-default">
<thead>
<tr>
<th>Content Type</th>
<th>Instances</th>
<th>Has Controller</th>
</tr>
</thead>
<tbody>
@foreach (var ct in Model)
{
<tr>
<td>@ct.Name</td>
<td>@ct.InstanceCount</td>
<td>@(ct.HasController ? "Yes" : "No")</td>
</tr>
}
</tbody>
</table>
</div> Register services in an initialization module
Section titled “Register services in an initialization module”If your plugin needs dependency injection, register services during startup.
using Optimizely.Cms.Framework.Initialization;
using Microsoft.Extensions.DependencyInjection;
namespace MySite.Plugin.ContentAudit;
[InitializableModule]
public class ContentAuditInitialization : IConfigurableModule
{
public void ConfigureContainer(
ServiceConfigurationContext context)
{
context.Services
.AddScoped<IAuditService, AuditService>();
}
public void Initialize(InitializationEngine context) { }
public void Uninitialize(InitializationEngine context) { }
} Package as NuGet
Section titled “Package as NuGet”<PropertyGroup>
<PackageId>MySite.Plugin.ContentAudit</PackageId>
<Version>1.0.0</Version>
<Description>Content type audit tool for Optimizely CMS</Description>
<Authors>Your Team</Authors>
<PackageTags>optimizely;cms;plugin;audit</PackageTags>
</PropertyGroup> Run dotnet pack --configuration Release to create the .nupkg file. Publish it to your internal NuGet feed or nuget.org.
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
| Menu item not showing | Missing [MenuProvider] attribute | Add the attribute to your menu provider class |
| View not found at runtime | View not embedded or path wrong | Use full path starting with ~/Views/ |
| Authorization error | Wrong policy name | Use CmsPolicyNames.CmsAdmin for admin tools |