Releases: submoro/admin-menu-organizer
Release list
v1.1.0 — sidebar filter box
Reviewed in #1, where CodeRabbit found four issues — one of them a real functional bug. All four are fixed in this release.
New: filter box at the top of the sidebar
A grouped sidebar trades scanning for clicking — an item you cannot see is behind a collapsed header, and on a thirty-plugin site you may not know which one. Start typing and the menu narrows to matching items across every group, open or closed.
Escapeclears,Enterfollows the first match- While a query is active every header hides and the list goes flat, because disclosure is meaningless when the visible set is decided by a query rather than by your clicks
- A polite live region announces the match count, so the menu does not silently rearrange under a screen reader
- Hidden below eight menu items, and while the sidebar is folded
With JavaScript off there is no box and the sidebar is a plain grouped menu.
Opening a group brings it to the top
Expanding a group near the bottom of a long sidebar used to reveal its members below the fold, so the click looked like it had done nothing. The group's header is now scrolled to the top of the scrollable area. Only on expand — doing it on collapse would yank the page for no reason — and it honours prefers-reduced-motion.
Long group names wrap instead of being cut off
This is the third attempt at the clipped-name problem and the first that fixes the right thing.
Shortening the defaults did not work, because a name is stored per group: a site that saved a layout beforehand keeps the long one, and you can type a long name at any time. Truncation was never a defaults problem. Names now wrap onto a second line, which holds for names you choose as well as the ones that ship.
The indent can no longer be cached away
The member indent is now also written inline on every page load, not left to the stylesheet alone.
A live site reported the Plugins screen showing the new version while the sidebar still rendered flat. The cause was a stale OPcache: old compiled PHP meant the version constant evaluated to the previous release, so the stylesheet was fetched at its old URL and the browser served a copy predating the indent rules. Meanwhile WordPress reads the version header straight off disk, so the plugin looked updated and behaved as though it were not.
No plugin can flush someone's OPcache. It can stop depending on a cacheable file for the part that matters.
From the review
- Filtering now outranks the collapsed-row rules. The desktop selector carried one more class than the filter-hidden rule, so it won on specificity and re-revealed every non-matching row inside a collapsed group — the filter appeared to do nothing for exactly the groups it helps most. The mirror image applied below 782px, hiding rows a query had just matched. Both scoped correctly now.
- Dropped
word-break: break-word, deprecated in CSS Text 3 and redundant besideoverflow-wrap: anywhere. - Lowercased
currentcolorkeyword values. - Added the Arabic translation for the filter's match-count status, which was reaching Arabic users in English.
Verification
12 checks green on the PR and 11 on main: Plugin Check across all five categories, the integration suite on PHP 7.4 / 8.1 / 8.3 against both WordPress 6.4 and current stable, unit suite 323 tests / 3135 assertions, a real PHP 7.4 syntax pass, WordPress coding standards, an outbound-request scan, and CodeRabbit.
Not verified: the filter box and the reveal-on-expand have not been used in a browser. Both degrade to the previous behaviour if the script fails, but the interaction itself wants a human.
v1.0.3
Fixes an update appearing to apply while the old stylesheet was still being served.
The problem
On a live site the Plugins screen read 1.0.2 while the sidebar still behaved like 1.0.0 — group names clipped, members not indented. Both are things 1.0.2 fixed.
The 1.0.2 code was correct. Fed the exact data that site had, it healed every name. It simply was not running.
If a server's OPcache is still serving previously compiled PHP after the plugin files were replaced in place, the version constant evaluates to the old version. The stylesheet is then requested at the old URL and the browser serves its cached copy, which is why the indent was missing too. Yet the Plugins screen reports the new version, because WordPress reads that header straight off disk rather than through OPcache.
The plugin says it updated and behaves as though it did not, which is a miserable state to debug.
The fix
Asset URLs now include the file's modification time as well as the plugin version, so the URL changes whenever the file on disk changes — whatever the constant evaluates to. Applies to the sidebar CSS and JS, and to the settings screen's.
This cannot fix a stale OPcache, and no plugin can. It does mean a stale OPcache can no longer disguise itself as a broken stylesheet.
If your sidebar still looks wrong
Group names should read DASHBOARD, CONTENT, COMMERCE, DESIGN, MARKETING, SECURITY, PERFORMANCE, USERS, INTEGRATIONS, TOOLS — no ellipses — and members should sit indented under their header with a faint rule running down the group.
If they do not, after installing this build:
- Clear WP Rocket's cache, and any object cache.
- Restart PHP-FPM if you can. That is the only reliable way to flush OPcache.
- Hard-reload the admin page.
CI 11/11 green. Unit suite 323 tests / 3135 assertions.
v1.0.2
Two fixes from running 1.0.1 on a live site.
Group members are now indented
Members sit in from their group header, with a hairline rule running down the group that closes off after the last item, so the sidebar reads as a two-level tree instead of a flat list with headings in it.
Previously the indent variable was declared and never applied — so there was no hierarchy at all. The rule is drawn in currentColor at low opacity, which means it adapts to every admin colour scheme without naming a single hex value, and the current item gets a stronger one because that answers "where am I" faster than the row highlight alone. Dropped entirely when the sidebar is folded, and widened on mobile where there is room.
Group names that stayed clipped after updating are fixed
The 1.0.1 schema migration was correct, and its test passed — but it can only fire on a layout that still claims the old schema number. Saving from the Groups tab right after an update writes the new schema number together with the old name, because the form field was filled before the default changed. Such a layout never re-enters the migration chain, so the name stayed clipped indefinitely.
Names are now normalised on every read rather than once per schema bump. A name is only replaced when it exactly matches one this plugin itself previously shipped, for the group that shipped it — so a name you chose yourself is never touched, and a stale default belonging to a different group is not applied across.
Verification
Unit suite 323 tests / 3135 assertions. CI 11/11 green: Plugin Check across all five categories, the integration suite on PHP 7.4 / 8.1 / 8.3 against both WordPress 6.4 and current stable, a real PHP 7.4 syntax pass, coding standards, and an outbound-request scan.
v1.0.1
Fixes three defects found by running v1.0.0 on a live 30-plugin WooCommerce marketplace.
Fixed
Four plugins were landing in the Other group and had to be moved by hand: MultiVendorX, WoodMart, WPML and CookieYes. Their real slugs turned out to be multivendorx, xts_dashboard, a WPML menu path, and cookie-law-info. WPML was the interesting one — it registers a path rather than a conventional slug, and that path differs between the core plugin and Translation Management, so the entry already in the table never matched. All four are now mapped exactly, plus the xts_, mvx, wpml and cky- vendor prefixes, so other screens from those families are recognised too rather than just these four items.
The Comments item displayed as "Comments 0 Comments in moderation". WordPress nests a screen-reader sentence inside the update-count bubble, and the title cleaner stopped at the first closing tag. Peeling by class name did not work either, because the inner spans are called whatever the plugin chose. Replaced with a balanced-tag scan that tracks nesting depth.
Group names were clipped in the sidebar — DESIGN & LA..., SECURITY & B..., USERS & ACC.... The originals came straight from the specification and simply did not fit a 160px sidebar. Shortened to single nouns, with a schema migration so existing installs get the fix. A group you renamed yourself is left exactly as you set it, even if the name is long, and membership and order are untouched, so nothing moves.
Also fixes a duplicate array key that PHP would have silently collapsed.
Verification
11 CI jobs green, unchanged from v1.0.0: Plugin Check across all five categories, the integration suite on PHP 7.4 / 8.1 / 8.3 against both WordPress 6.4 and current stable, a real PHP 7.4 syntax pass, WordPress coding standards, and an outbound-request scan.
The unit suite is now 319 tests / 3126 assertions, up from 295. The new ones lock in every slug observed on that live site — including an assertion that a real 30-plugin marketplace produces an empty Other group — and fail the build on any default group label that would clip.
Still not verified
Anything that needs a rendered page: the nine admin colour schemes, RTL with Arabic, the responsive breakpoints, keyboard navigation, and a screen-reader pass.
v1.0.0
First release. Not yet on the WordPress.org directory, so install from the zip below.
Install
Plugins -> Add New -> Upload Plugin, choose admin-menu-organizer.1.0.0.zip, Install Now, Activate. Your sidebar is grouped immediately; nothing to configure. To rearrange anything, go to Settings -> Menu Organizer.
Or with WP-CLI:
wp plugin install admin-menu-organizer.1.0.0.zip --activate
Requires WordPress 6.4+ and PHP 7.4+.
What it does
Groups top-level admin menu items into named, collapsible categories inside the native sidebar. It reorders and decorates the existing menu rather than rebuilding it, so every capability check, update bubble and third-party integration keeps working.
- Recognises plugins on sight and files them correctly, including plugins it has never heard of, via an eight-layer detection cascade. Measured 40/40 on real plugins deliberately excluded from its own lookup table.
- Full drag-and-drop override, with a keyboard equivalent for every drag.
- Per-user open/closed memory. The group containing the current page always opens.
- Collapsed groups show an aggregated update-count badge.
- Site-wide default, optional per-role layouts, optional per-user personalisation.
It never hides an item. Anything unrecognised goes to an always-visible Other group. It never modifies a capability, so it cannot affect what you are allowed to reach.
Zero outbound HTTP requests. No telemetry, no analytics, no calling home. Enforced by the test suite, not merely promised.
If something goes wrong
?amorg=offon any admin URL disables it for one page loaddefine( 'AMORG_DISABLE', true );in wp-config.php disables it entirely
Neither discards your saved arrangement.
Verification
11 CI jobs green: Plugin Check across all five categories, the integration suite on PHP 7.4/8.1/8.3 against both WordPress 6.4 and current stable, 295 unit tests, a real PHP 7.4 syntax pass, WordPress coding standards, and an outbound-request scan.
Not yet verified: anything needing a rendered page. The nine admin colour schemes, RTL, the responsive breakpoints, keyboard navigation and a screen-reader pass have not been looked at in a browser.