sunylcumar
May 18, 2025
visibility 1164
star star star star star star
(4 votes)

Add a new menu item to the Admin Menu in Optimizely CMS

Create a new Controller called CustomMenuController and decorate with [Authorize(Roles ="CMSAdmins")] so that it will be accessed by admins only

namespace AdminMenu.Controllers
{
    [Route("episerver/CustomMenu")]
    [Authorize(Roles ="CMSAdmins")]
    public class CustomMenuController : Controller
    {
        [HttpGet("")]
        public IActionResult Index()
        {
            return View("~/Views/CustomMenu /Index.cshtml");
        }     
    }
}

Create a view under Views/CustomMenu/Index.cshtml for the controller

@{
Layout = null;
}
<html lang="en">
<head>    
    <title>Custom Menu</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="shortcut icon" href="/Util/images/favicon.ico" type="image/x-icon" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

 <body>
    @Html.AntiForgeryToken()
    @Html.Raw(Html.CreatePlatformNavigationMenu())
    <div class="margin-100">
        <div class="container mt-5">
          <h1>This is a custom menu example</h1>
          <p>Custom menu implementation</p>
       </div> 
     </div>  
    <required-client-resources area="Footer" />
</body>
</html>

Create a new class called MenuItem that inherits IMenuProvider

namespace FEO.CMS.Global.Business.MenuProviders
{
    [MenuProvider]
    public class CustomMenuProvider : IMenuProvider
    {
        public IEnumerable<MenuItem> GetMenuItems()
        {
            var menuItems = new List<MenuItem>();

            menuItems.Add(new UrlMenuItem("Custom Admin Menu",
              "/global/cms/CustomAdminMenu",
             "/EPiServer/CustomMenu")
            {
                SortIndex = SortIndex.First + 25,
                AuthorizationPolicy = CmsPolicyNames.CmsAdmin
            });
            return menuItems;
        }
       
    }
}

Thats all, run the application and you can find a new menu item under the admin menu

May 18, 2025

Comments

El Magnifico
El Magnifico May 18, 2025 02:13 PM

Good working example

El Magnifico
El Magnifico May 18, 2025 02:20 PM

Can you extend this example for any real world usecase like triggering sitemap generation or robots.txt implementation

sunylcumar
sunylcumar May 18, 2025 02:22 PM

Sure I will create a new article with real world implementation for robots.txt

Mark Stott
Mark Stott May 20, 2025 09:44 AM

There is a really good robots solution already out there called Stott Robots Handler. See: https://github.com/GeekInTheNorth/Stott.Optimizely.RobotsHandler 

This features:

  • Manage your Robots.Txt content by Site or Host URL
    • By allowing this to be managed by host, users can have domain specific production robots.txt and fallback robots.txt content which blocks indexing when cloning from Prod to a lower environment
  • Environment based Robots settings for X-Robots-Tag and Robots Meta Tag that allow you to clone Prod down to a lower environment and have the immediate benefit of all your content having a No Index / No Follow response for robots.

What's really good is that it's maintained by an Optimizely MVP.

FYI: I may be incredibly biased :)

sunylcumar
sunylcumar May 22, 2025 03:41 PM

Thanks Mark,

 

I really appreciate your effort in taking time and providing good articles.

 

Sunil

error Please login to comment.
Latest blogs
Composition Over Inheritance: Why Your Content Contract Is Not a Base Class

Inside the structural conformance pattern in SaaS CMS — from server-side inheritance to pipeline governance. A contract in Optimizely SaaS CMS is...

Vipin Banka | Sep 19, 2026

Optimizely Opti ID Integration: Why the API Key Was Missing from the DXP Management Portal

While working on an Optimizely CMS 12 Opti ID integration recently, we ran into an issue that initially appeared to be related to SSO authenticatio...

Madhu | Sep 18, 2026 |

Extending a Contract: What You Can Override

Contracts share fields across content types — but not every field setting can be overridden. Push the same contract onto several components, and CM...

Chirag Khanna | Sep 18, 2026 |

Building a Blazor Server UI Inside the Optimizely CMS 13 Admin Shell

What I learned shipping a Blazor Server UI inside Optimizely CMS 13 — Razor components in a NuGet package, the Blazor hub next to MapContent(),...

Stanisław Szołkowski | Sep 17, 2026 |