sunylcumar
May 18, 2025
visibility 1231
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
Surviving a Dirty Feed. Part 3: Error Handling and Event Batching

Committing a 250,000-row Optimizely catalog import row by row, returning a per-row failure report, and coalescing catalog events into one per batch.

Stanisław Szołkowski | Oct 16, 2026 |

Building UI Extensions for Optimizely SaaS CMS

Introduction Developers have been extending the functionality of Optimizely PaaS CMS for years using addons built in C# and deployed as NuGet...

Mark Stott | Oct 15, 2026

Cheapskate AI Translator: Because Translation Should Not Require a Second Mortgage

There is a particular kind of software project that begins with a detailed architecture document, a product roadmap, and a carefully assembled...

Linh Doan Cuu | Oct 15, 2026

Computed Fields in CMS 13 Content Contracts with Optimizely Graph

Content Contracts in Optimizely CMS 13 allow different content types to share a common structure when querying Optimizely Graph. But what if the...

Mardy | Oct 14, 2026