A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

sunylcumar
May 18, 2025
  679
(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

Please login to comment.
Latest blogs
Building simple Opal tools for product search and content creation

Optimizely Opal tools make it easy for AI agents to call your APIs – in this post we’ll build a small ASP.NET host that exposes two of them: one fo...

Pär Wissmark | Dec 13, 2025 |

CMS Audiences - check all usage

Sometimes you want to check if an Audience from your CMS (former Visitor Group) has been used by which page(and which version of that page) Then yo...

Tuan Anh Hoang | Dec 12, 2025

Data Imports in Optimizely: Part 2 - Query data efficiently

One of the more time consuming parts of an import is looking up data to update. Naively, it is possible to use the PageCriteriaQueryService to quer...

Matt FitzGerald-Chamberlain | Dec 11, 2025 |

Beginner's Guide for Optimizely Backend Developers

Developing with Optimizely (formerly Episerver) requires more than just technical know‑how. It’s about respecting the editor’s perspective, ensurin...

MilosR | Dec 10, 2025