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.
Package info
github.com/Melazhari1/sulu-admin-bar-bundle
Type:symfony-bundle
pkg:composer/elazhari/sulu-admin-bar-bundle
Requires
- php: ^7.2 || ^8.0
- sulu/sulu: ^2.0 || ^3.0
- symfony/asset: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/config: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/dependency-injection: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/http-foundation: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/http-kernel: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/routing: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/security-core: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/twig-bridge: ^4.4 || ^5.4 || ^6.4 || ^7.0
- symfony/yaml: ^4.4 || ^5.4 || ^6.4 || ^7.0
- twig/twig: ^2.13 || ^3.0
Requires (Dev)
- phpunit/phpunit: ^8.5 || ^9.6
This package is not auto-updated.
Last update: 2026-07-06 21:20:19 UTC
README
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:
{{ 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.- 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. - 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. - 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:
admin_bar.admin_base_path, when configured explicitly;- otherwise auto-detected from the Sulu admin firewall pattern in your
securityconfiguration (^/admin(\/|$),^/_private,^/backend, …); - 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_routeand reads the numericidroute 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 theviewpermission); with it theedit/addpermission 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-barendpoint, not by cacheable page HTML. - The
sulu_admin_barmarker cookie is a pure presence flag (value1, 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,uuidandidquery parameters against known webspaces/localizations and a strict UUID pattern; entity ids must be numeric and theresourceKeymust 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
fetchandURLSearchParams— 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
The bar on a regular Sulu page, with Edit / Add new / Logout.
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.