GeniXCMS

Admin Menu Registration

categoryHow To edit_calendar31 Mar 2026

Extending the Admin Navigation Menu


GeniXCMS v2.0.0 introduces a programmable admin menu system via the AdminMenu class. This allows modules and themes to add, remove, or reorder navigation items in the admin panel sidebar and top navbar without modifying any core file.


🏗️ Architecture Overview

The admin navigation is driven by a central registry. When the system boots, it:

  1. Loads all active Modules (new Mod())
  2. Loads the active Theme (new Theme())
  3. Fires the init hook
  4. Initializes AdminMenu (new AdminMenu()) — which registers core items and processes any items modules have already queued

This means you can safely call AdminMenu::add() from anywhere in your module or theme's function.php or index.php.


🔌 Registration in a Module

The recommended location to register your admin menu items is your module's function.php file (loaded automatically on every page load when the module is active).

Minimal Example

// inc/mod/my_shop/function.php

AdminMenu::add([
    'id'       => 'my_shop',
    'label'    => _('My Shop'),
    'icon'     => 'bi bi-cart3',
    'url'      => 'index.php?page=mods&mod=my_shop',
    'access'   => 3,
    'position' => 'external',
    'order'    => 10,
]);

Full Example with Sub-Menu

// inc/mod/inventory/function.php

AdminMenu::add([
    'id'       => 'inventory',
    'label'    => _('Inventory'),
    'icon'     => 'bi bi-box-seam-fill',
    'url'      => 'index.php?page=mods&mod=inventory',
    'access'   => 2,           // Editor and above
    'position' => 'external',
    'order'    => 20,
    'children' => [
        [
            'label'  => _('Products'),
            'icon'   => 'bi bi-boxes',
            'url'    => 'index.php?page=mods&mod=inventory&tab=products',
            'access' => 2,
        ],
        [
            'label'  => _('Orders'),
            'icon'   => 'bi bi-receipt',
            'url'    => 'index.php?page=mods&mod=inventory&tab=orders',
            'access' => 2,
        ],
        [
            'label'  => _('Stock Report'),
            'icon'   => 'bi bi-bar-chart-line',
            'url'    => 'index.php?page=mods&mod=inventory&tab=report',
            'access' => 1,    // Supervisor only for reports
        ],
    ],
]);

🎨 Registration in a Theme

If your theme provides its own admin utilities (e.g., a Theme Options shortcut), register the item in your theme's function.php.

// inc/themes/my_theme/function.php

AdminMenu::add([
    'id'       => 'my_theme_options',
    'label'    => _('Theme Options'),
    'icon'     => 'bi bi-brush-fill',
    'url'      => 'index.php?page=themes&act=options',
    'access'   => 0,          // Administrator only
    'position' => 'external',
    'order'    => 5,
]);

📐 Menu Positions

Choose the right position to place your item in the correct region:

Position Sidebar Section Top Navbar Behavior
main Under "Main Navigation" Shown as top-level nav links
management Under "Management" Shown as top-level or dropdown
settings Under Settings group Shown inside "Settings" dropdown
external Under "External" Shown inside "External" dropdown
lightbulb
TipBest Practice: Use external for module-specific items. Reserve main and management for items that have broad, primary importance across the whole site.

🌲 Adding Sub-Menus to Existing Items

If you want to inject a sub-menu into an existing parent item (e.g., adding a "Draft Store" link under the "Posts" menu or a "Custom Settings" under "Settings"), use AdminMenu::addChild().

Adding a Single Sub-Menu

// Add a custom link under the core "Posts" menu
AdminMenu::addChild('posts', [
    'label'  => _('My Post Type'),
    'url'    => 'index.php?page=posts&type=custom',
    'icon'   => 'bi bi-star',
    'access' => 4,   // Author level and above
]);

Adding Multiple Sub-Menus at Once

Use AdminMenu::addChildren() to inject a batch of items into a parent.

AdminMenu::addChildren('settings', [
    [
        'label' => _('Store Settings'),
        'url'   => 'index.php?page=settings&tab=store',
    ],
    [
        'label' => _('API Configuration'),
        'url'   => 'index.php?page=settings&tab=api',
    ]
]);
lightbulb
TipParent IDs: Common core parent IDs include posts, pages, users, settings, themes, modules, permissions, comments, and media.

🔄 Removing Core Menu Items

You can remove any core menu item using its id. This is useful for building specialized or restricted admin panels.

// Remove core items your custom installation does not need
AdminMenu::remove('comments');    // Remove Comments from sidebar
AdminMenu::remove('permissions'); // Remove ACL Manager link
warning
CautionRemoving core items may confuse users who expect standard navigation. Only do this if your module or client installation explicitly requires a simplified interface.

🔢 Controlling Order

All core items use order values that are multiples of 10. Use values between them to inject items precisely:

order: 10  → Posts
order: 15  → 📌 My Custom Item  ← injected between Posts and Pages
order: 20  → Pages
order: 30  → Comments
AdminMenu::add([
    'id'    => 'newsletter',
    'label' => _('Newsletter'),
    'icon'  => 'bi bi-envelope-paper',
    'url'   => 'index.php?page=mods&mod=newsletter',
    'position' => 'main',
    'order' => 15,   // Appears between Posts (10) and Pages (20)
]);

🧩 Using Access Control

The access key controls which user levels can see the menu item. It uses the same numeric role system as User::access():

Value Role
0 Administrator
1 Supervisor
2 Editor
3 Author
4 Contributor
5 VIP Member
6 General Member
// Only administrators see this item
AdminMenu::add([
    ...
    'access' => 0,
]);

// Authors and above
AdminMenu::add([
    ...
    'access' => 3,
]);

Child items can also have their own access level, which overrides the parent:

'children' => [
    ['label' => 'Public Stats',  'url' => '...', 'access' => 4], // Authors+
    ['label' => 'Private Stats', 'url' => '...', 'access' => 0], // Admin only
],

🔌 Using the init Hook (Advanced)

For items that depend on runtime conditions (e.g., feature flags or options), you can defer registration to the init hook:

// inc/mod/my_module/function.php

Hooks::attach('init', function() {
    if (Options::v('my_module_premium') === 'on') {
        AdminMenu::add([
            'id'       => 'my_module_premium',
            'label'    => _('Premium Features'),
            'icon'     => 'bi bi-star-fill',
            'url'      => 'index.php?page=mods&mod=my_module&tab=premium',
            'position' => 'external',
            'order'    => 30,
        ]);
    }
});
priority_high
ImportantThe init hook fires before new AdminMenu(), so items registered via init are available when the core menu is being built.

📋 Complete Reference

Method Description
AdminMenu::add(array $item) Register a new menu item.
AdminMenu::remove(string $id) Unregister a menu item by ID.
AdminMenu::getItems(?string $pos) Get sorted array of items, optionally filtered by position.
AdminMenu::renderSidebar(?string $pos) Output sidebar <li> HTML for given position.
AdminMenu::renderTopNav(?string $pos) Output top navbar <li> HTML for given position.

See Also