⌘K

Admin Integration

Rendering a page inside the admin panel

Plugins never implement their own admin layout. admin/index.php?action=plugin_page renders your content inside the standard chrome — same sidebar, top bar, and footer as every built-in admin screen. You define one function:

function my_plugin_render_admin_page(string $view): array
{
    // $view comes from ?view=... in the URL — your plugin defines what
    // views exist ('overview', 'settings') and what to render for each.

    return [
        'title'      => 'My Plugin',
        'html'       => '<p>Hello, view: ' . htmlspecialchars($view) . '</p>',
        'extra_head' => '', // optional <link>/<style>/<script> for <head>
    ];
}

Hyphens in your slug become underscores in the function name — a plugin with slug my-plugin defines my_plugin_render_admin_page. The core router handles session/auth, loading your entry file if it isn't already loaded, calling your render function, and wrapping the result in includes/layout.php.

Building admin URLs

Your plugin's admin page lives at:

{cms_base_url}/admin/index.php?action=plugin_page&slug=my-plugin&view={view}

Build this from your own filesystem position rather than hardcoding a path — the CMS may be installed at the domain root or a sub-directory, and the admin folder name is configurable at install time (admin_dir in config.json). Never hardcode admin/:

function my_plugin_admin_url(string $view, array $extraParams = []): string
{
    $params = array_merge(['action' => 'plugin_page', 'slug' => 'my-plugin', 'view' => $view], $extraParams);

    $configFile = dirname(__DIR__, 2) . '/config.json'; // adjust depth to your file's location
    $adminDir   = 'admin';
    if (file_exists($configFile)) {
        $decoded = json_decode(file_get_contents($configFile), true);
        if (is_array($decoded) && !empty($decoded['admin_dir'])) {
            $adminDir = $decoded['admin_dir'];
        }
    }

    return site_base_url() . $adminDir . '/index.php?' . http_build_query($params);
}

Always build absolute URLs (full https://host/path/...) — your own view templates, and any return_url field in a form that posts to your plugin's own endpoint, need to resolve correctly regardless of which physical file the browser is currently on. A relative path breaks the moment your action handler lives in a different folder than /admin/.

Matching the admin's visual style

If your admin page uses any custom CSS beyond what the standard layout already provides, use the admin panel's own CSS custom properties (--surface, --border, --primary, --danger, --radius-sm, etc. — see Admin UI Styling) rather than hardcoded colors, so your plugin's page follows the same light/dark theming as the rest of the panel automatically.