elazhari/sulu-admin-bar-bundle

Frontend admin bar for the Sulu CMS: edit the current page or custom entity, add new content and log out, directly from the website.

Maintainers

Package info

github.com/Melazhari1/sulu-admin-bar-bundle

Homepage

Issues

Type:symfony-bundle

pkg:composer/elazhari/sulu-admin-bar-bundle

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-07-02 20:36 UTC

This package is not auto-updated.

Last update: 2026-07-06 21:20:19 UTC


README

CI Latest Version License

A frontend admin bar for Sulu CMS. Backend users who are logged into the Sulu admin see a slim toolbar on top of the website with direct links to edit the content they are looking at. Anonymous visitors see nothing — and the page HTML stays fully HTTP cacheable.

┌──────────────────────────────────────────────────────────────────┐
│ [S] Sulu                        John Doe | Edit | Add new | Logout │
└──────────────────────────────────────────────────────────────────┘

Features

  • Edit the current page — or any custom entity (Formation, Article, …) detected automatically, without per-entity code.
  • Add new content of the same type as the current page.
  • Permission-aware: links are resolved server-side from Sulu's view registry, so users only ever see links they are allowed to use.
  • Cache-safe: no user-specific markup in the page HTML.
  • Silent for visitors: anonymous visitors trigger no extra request at all (session marker cookie).
  • Sulu 2 and Sulu 3 compatible, PHP >= 7.2.
  • One-command installer.

Installation

Quick install (recommended)

Install the package and run the installer with the PHP binary your project uses (the installer needs PHP >= 7.3):

composer require elazhari/sulu-admin-bar-bundle
php vendor/elazhari/sulu-admin-bar-bundle/install.php

Alternatively, copy the bundle into your Sulu project (e.g. to bundles/AdminBarBundle) and run:

php bundles/AdminBarBundle/install.php

It performs every manual step listed below — composer autoload entry, bundle registration, route import, default configuration, the security access_control rule, {{ sulu_admin_bar() }} in templates/base.html.twig, composer dump-autoload, assets:install and cache clearing.

The installer is idempotent (running it twice is safe), reports a [WARN] with manual instructions for anything it cannot patch automatically (e.g. a heavily customized security config), and accepts --skip-commands if you only want the file changes without running composer/console commands.

Manual installation

1. Register the code — as a composer package:

composer require elazhari/sulu-admin-bar-bundle

Or, as a local bundle copied to bundles/AdminBarBundle, add the namespace to your project's composer.json and dump the autoloader:

"autoload": {
    "psr-4": {
        "App\\": "src/",
        "Elazhari\\SuluAdminBarBundle\\": "bundles/AdminBarBundle/src/"
    }
}
composer dump-autoload

2. Enable the bundle:

// config/bundles.php
return [
    // ...
    Elazhari\SuluAdminBarBundle\AdminBarBundle::class => ['all' => true],
];

3. Import the route:

# config/routes/admin_bar.yaml
admin_bar:
    resource: '@AdminBarBundle/config/routes.yaml'

4. Allow anonymous access to the endpoint. It must stay inside the admin firewall but must not trigger the admin login — it answers 401 itself. Add this rule above the admin catch-all, using your admin base path (/admin in a stock Sulu project, /_private, /backend, … in customized ones):

# config/packages/security.yaml
# (or security_admin.yaml in projects with kernel specific security configs)
access_control:
    # ...
    - { path: ^/admin/admin-bar$, roles: PUBLIC_ACCESS }
    - { path: ^/admin, roles: ROLE_USER }

On Symfony versions without the PUBLIC_ACCESS attribute use IS_AUTHENTICATED_ANONYMOUSLY instead (the installer picks whichever the file already uses).

5. Install the assets:

php bin/console assets:install public

6. Add the bar to your base layout, right before </body>:

{# templates/base.html.twig #}
        {{ sulu_admin_bar() }}
    </body>
</html>

Finally clear the caches:

php bin/console cache:clear
php bin/websiteconsole cache:clear

Log into the admin once, then open the website: the bar appears.

How it works

In a standard Sulu setup only the admin (/admin by default — the bundle adapts to any custom prefix, see below) is behind a firewall and website responses are cached by the HTTP cache. Rendering a user-specific bar directly into the page HTML would therefore either never see the admin session or leak the bar into cached responses. This bundle avoids both:

  1. {{ sulu_admin_bar() }} renders a tiny, visitor-independent loader <script> carrying the current page context (webspace, locale, page uuid or entity id) as data attributes — safe to cache.
  2. While using the admin, a response listener sets a JS-readable session marker cookie (sulu_admin_bar) and removes it again on logout. The cookie carries no data; it only tells the loader that an admin session exists, so anonymous visitors never call the endpoint at all.
  3. When the marker is present, the script calls GET <admin path>/admin-bar (e.g. /admin/admin-bar), which runs through the admin firewall, so the Sulu admin session is available there.
  4. If the user is authenticated, the endpoint returns their name and the permission-checked admin URLs; the script injects the stylesheet and the bar. Otherwise it returns 401, the stale marker is dropped and nothing is rendered.

What the bar shows

Element Behaviour
Sulu logo Links to the Sulu admin.
User name Full name of the logged-in Sulu user (falls back to the username).
Edit Opens the current page or entity in its Sulu admin edit form.
Add new Opens the creation form for the current content type; outside of any content context it opens the page list of the webspace.
Logout Calls the Sulu admin logout route.

Permissions are checked server-side with Sulu's SecurityChecker — against the sulu.webspaces.<webspace> security context for pages and against the entity's admin views (plus the optional configured security_context) for custom entities — so links the user is not allowed to use are never rendered.

Configuration

Everything works without configuration. The full reference:

# config/packages/admin_bar.yaml
admin_bar:
    enabled: true   # set to false to remove the loader snippet entirely

    # Base path of the Sulu admin — see "Custom admin URL" below.
    # Auto-detected when omitted; falls back to /admin.
    #admin_base_path: /admin

    # Texts of the toolbar links — override them to localize the bar.
    labels:
        edit: Edit
        add: Add new
        logout: Logout

    # Optional per-entity extras — see "Custom entities" below.
    entities:
        formation:                                        # request attribute
            resource_key: formations                      # Sulu resource key
            security_context: sulu.formations.formation   # optional extra gate
            routes: [formation]                           # optional route names

Custom admin URL

Nothing about the admin URL is hardcoded. The endpoint route is registered as <admin base path>/admin-bar, and the base path is resolved per kernel:

  1. admin_bar.admin_base_path, when configured explicitly;
  2. otherwise auto-detected from the Sulu admin firewall pattern in your security configuration (^/admin(\/|$), ^/_private, ^/backend, …);
  3. otherwise Sulu's default /admin.

So /admin, /_private, /backend, /cms or any other prefix works out of the box as long as the security configuration is visible to both Sulu kernels (the standard skeleton's single security.yaml).

Set admin_base_path explicitly in one case: your project uses kernel specific security configs (security_admin.yaml / security_website.yaml). The website kernel then has no admin firewall to inspect, and the loader would point to the default /admin. Since config/packages/admin_bar.yaml is shared by both kernels, one line fixes it:

admin_bar:
    admin_base_path: /_private

(The installer detects your admin path and writes this line for you.)

Custom entities

Sulu projects often route Doctrine entities through the RouteBundle with a RouteDefaultsProviderInterface implementation. When such a provider exposes the entity as a request attribute in its route defaults:

public function getByEntity($entityClass, $id, $locale, $object = null)
{
    return [
        '_controller' => '...',
        'id' => $formation->getId(),
        'formation' => $formation,   // <- request attribute
    ];
}

the admin bar links to the entity's admin form automatically. Detection is convention based: any request attribute holding an object with a RESOURCE_KEY class constant (the standard Sulu entity convention) and a scalar getId() is linkable.

The admin URLs are just as dynamic: the endpoint looks the edit and add form views up in Sulu's view registry (sulu_admin.view_registry) by resource key and uses the actual registered view paths. Because Admin classes register views per user, a user without access to the entity simply gets no link.

Configuration under admin_bar.entities is only needed for two cases:

  • routes — entity detail pages that are not served through the Sulu route system but by plain Symfony routes (e.g. /formation/{slug}/{id}) carry no entity object in the request attributes. List the route names here: the bar then matches the current _route and reads the numeric id route parameter instead.
  • security_context — an additional permission gate. Without it a link is shown whenever the current user has a matching admin view (which usually means the view permission); with it the edit/add permission of that context is required on top.

Notes:

  • Explicitly configured attributes win over auto-detection, in the configured order. With auto-detection alone, the request attribute order decides (the route defaults order), which normally puts the primary entity first.
  • The entity must have a scalar, non-empty getId(); the endpoint only accepts numeric ids.

Security notes

  • The bar never renders for anonymous visitors; the decision is made by the authenticated <admin path>/admin-bar endpoint, not by cacheable page HTML.
  • The sulu_admin_bar marker cookie is a pure presence flag (value 1, session lifetime, SameSite=Lax). It is never trusted server-side: it only prevents pointless endpoint calls from visitors without an admin session. Deleting or forging it changes nothing security-wise.
  • The endpoint validates the webspace, locale, uuid and id query parameters against known webspaces/localizations and a strict UUID pattern; entity ids must be numeric and the resourceKey must match a view registered for the current user in the Sulu view registry.
  • The JSON response is sent with Cache-Control: private, no-store.
  • The toolbar DOM is built with textContent (no HTML injection via user names or page data).

Compatibility

  • PHP >= 7.2 (the optional installer script needs PHP >= 7.3)
  • Sulu ^2.0 || ^3.0
  • Symfony ^4.4 || ^5.4 || ^6.4 || ^7.0
  • Browsers: the loader uses fetch and URLSearchParams — every evergreen browser works; Internet Explorer does not (visitors are unaffected either way, the script only acts for logged-in admin users).

The bundle intentionally uses the classic Bundle + DI extension pattern and PHP 7.2 compatible syntax so the same code runs on legacy Sulu 2 projects and current Sulu 3 projects. The current page is detected via the object request attribute on Sulu 3 and via the structure attribute (PageBridge) on Sulu 2; admin URL patterns and the sulu.webspaces.<webspace> security context are identical in both major versions.

Bundle structure

AdminBarBundle/
├── composer.json
├── install.php                  # one-shot project installer
├── LICENSE
├── README.md
├── config/
│   ├── routes.yaml              # <admin path>/admin-bar JSON endpoint
│   └── services.yaml            # service definitions
├── public/
│   ├── admin-bar.css            # toolbar styles (loaded only when logged in)
│   └── admin-bar.js             # loader + toolbar rendering
├── src/
│   ├── AdminBarBundle.php       # bundle class
│   ├── DependencyInjection/
│   │   ├── AdminBarExtension.php  # loads services, "admin_bar" config
│   │   └── Configuration.php
│   ├── Controller/
│   │   └── AdminBarController.php # authenticated JSON endpoint
│   ├── EventListener/
│   │   └── AdminSessionCookieListener.php # session marker cookie
│   └── Twig/
│       └── AdminBarExtension.php  # {{ sulu_admin_bar() }}
├── templates/
│   └── admin_bar.html.twig      # cache-safe loader snippet
└── tests/                       # PHPUnit test suite (vendor/bin/phpunit)

Screenshots

Admin bar on a Sulu page The bar on a regular Sulu page, with Edit / Add new / Logout.

Admin bar on a custom entity The bar on a custom entity detail page — the Edit link opens the entity's admin form.

Troubleshooting

The bar does not appear at all. Log into the Sulu admin first — the bar only renders for authenticated backend users, and only after the sulu_admin_bar marker cookie has been set by an admin page load. Also check that {{ sulu_admin_bar() }} is in your base layout and admin_bar.enabled is not false.

The browser console shows a 404 for …/admin-bar. The resolved admin base path is wrong. This happens with kernel specific security configs (security_admin.yaml): set admin_bar.admin_base_path explicitly — see "Custom admin URL" above. bin/websiteconsole debug:router admin_bar.info shows the path the website kernel resolved.

The endpoint redirects to the admin login instead of returning 401. The access_control rule is missing or below the admin catch-all. It must be above - { path: ^<admin path>, roles: ROLE_USER }.

The bar appears but has no styles / the JS file 404s. Run php bin/console assets:install public so bundles/adminbar/ exists.

Edit/Add links are missing for an entity. The current user needs a matching admin view (view permission); with a configured security_context, the EDIT/ADD permission on that context is required on top. The entity must expose a numeric getId() and a RESOURCE_KEY constant, or be listed under admin_bar.entities.

Development

git clone https://github.com/elazhari/sulu-admin-bar-bundle.git
cd sulu-admin-bar-bundle
composer install

vendor/bin/phpunit                              # tests
vendor/bin/php-cs-fixer fix --dry-run --diff    # coding standards
vendor/bin/phpstan analyse                      # static analysis

See CONTRIBUTING.md for the contribution guidelines and CHANGELOG.md for the release history.

License

Released under the MIT license.