WPMediaVerse Surface Ownership Map
Free & ProWhich plugin owns the UI for each surface BuddyNext and WPMediaVerse both touch, and which one owns the data behind it.
This page exists because the surfaces with a written owner behave, and the ones without keep producing the same bug in a new place. It is the answer to "why do we keep fixing this again and again": a shared surface with no named owner grows a second implementation, and two implementations over one API always drift.
The standing rule
From Integration Bridges: BuddyNext consumes WPMediaVerse at the REST/API level only and owns 100% of its own UX. WPMediaVerse JS/CSS is never enqueued on BuddyNext pages.
That rule is correct and is being honoured - verified 2026-08-27 by fetching
/members/{user}/media/, /activity/ and a space home and grepping the markup:
zero mvs*.js / mvs*.css on any of them.
So the question for each surface below is never "may BuddyNext render this?" - it is "has BuddyNext actually finished rendering it, and is WPMediaVerse's own version of the same screen still reachable next to it?"
The map
| Surface | UI owner | Data owner | State |
|---|---|---|---|
| Direct messages | BuddyNext (templates/messages/native.php) |
WPMediaVerse (mvs/v1) |
Settled. MV suppresses its chat panel + messages page via mvs_buddynext_active |
| Document drive (space + profile) | BuddyNext (RendersDriveFiles) |
WPMediaVerse (mvs-pro/v1) |
Settled by directive 2026-08-24. MV answers drive filters, never queries bn_* |
| Media viewer (lightbox) | BuddyNext (templates/partials/media-lightbox.php) |
WPMediaVerse (mvs/v1) |
Incomplete - see below |
| Media grid / profile Media tab | BuddyNext | WPMediaVerse | Settled |
| Composer media attach | BuddyNext (assets/js/feed/composer.js) |
WPMediaVerse | Settled |
| Activity cards for uploads | BuddyNext | WPMediaVerse | Settled by directive 2026-08-27: media only (image/video/audio), never documents |
| Member avatar | Shared, by precedence | each plugin's own | Settled - see below |
Media viewer - the two open items
1. BuddyNext's viewer is a subset of WPMediaVerse's
Measured 2026-08-27 on the same image, same comment, six viewer states:
| BuddyNext viewer | WPMediaVerse viewer | |
|---|---|---|
| View, prev/next, download, share, favorite, reactions, report | yes | yes |
| Block author | yes | - |
| Fullscreen | - | yes |
| Edit media (title, description, privacy, slug, allow-download) | - | yes |
| Save to collection | - | yes |
| Edit / delete own comment | - | yes |
| Comments visible to a logged-out visitor | - | - |
Two of those gaps are defects rather than scope decisions:
- Nobody can edit or delete their own comment in BuddyNext's viewer - not even
its author.
commentEl()builds an author<strong>and a text<span>and stops; there is nocanEdit/canDeleteanywhere inassets/js/media/lightbox.js. The API supportsPUT/PATCH/DELETEon that comment. - A logged-out visitor sees no comments at all, because
$bn_lb_can_interact = is_user_logged_in()gates the comments panel as well as the comment form - reading is gated on being able to write. The API returns 200 with the comments to an anonymous caller, so the data is public and only the rendering is missing.
The remaining gaps (fullscreen, edit media, collections) are product scope, not bugs. They are listed so the decision is deliberate.
2. WPMediaVerse's own media pages are still reachable on a BuddyNext stack
mvs_buddynext_active already tells WPMediaVerse to stand down its chat panel,
its standalone messages page and its duplicate notifications. It does not
cover its media pages: /explore-media/, /my-media/, /upload-media/ and
/explore-document/ remain published, and they legitimately load WPMediaVerse's
own viewer - those are WPMediaVerse pages, so the "no MV assets on BN pages" rule
does not apply to them.
That is where "the same image opens two different lightboxes" comes from. It is not a breach of the rule; it is a door the rule was never asked to close.
BuddyNext already closes the equivalent door for single media:
mvs_single_media_redirect sends /media/{slug}/ to the activity the media was
posted in. The same treatment is what the remaining pages need.
Avatars - shared, by precedence
Avatars are the one surface no single plugin owns, because any of them may hold
the member's picture. Three register pre_get_avatar_data:
[10] Jetonomy\Avatar::filter_avatar_data
[10] WPMediaVerse\Services\ProfileService::filter_avatar_data
[50] BuddyNext\Profile\AvatarService::filter_avatar_data (BuddyNext upload only, else defers)
[99] BuddyNext\Profile\AvatarService::filter_avatar_fallback (generated initials)
The rule: a real picture always beats a generated one. The two sources at priority 10 each hold somebody's actual uploaded image, and whichever answers first wins - a fair race. BuddyNext runs after them, at 50: it sets its own uploaded avatar when the member has one (an explicit choice made in this community, so it wins even over a sibling's) and otherwise returns the args unchanged, leaving a real photo an earlier filter set. BuddyNext's generated initials are not in that race at all; they run at 99, only when nobody produced a URL.
That split is the fix for a real defect. All three used to sit at priority 10 and
BuddyNext registered last, so its placeholder beat WPMediaVerse's photograph:
a member with a valid _mvs_custom_avatar and no BuddyNext avatar saw generated
initials on every surface, and their upload appeared nowhere. Verified before the
fix by unhooking BuddyNext's filter - the real avatar then appeared everywhere.
Measured precedence after the fix, one member, all four states:
| Member has | Renders |
|---|---|
| neither | BuddyNext initials |
| WPMediaVerse avatar only | the WPMediaVerse avatar |
| BuddyNext avatar only | the BuddyNext avatar |
| both | the BuddyNext avatar |
BuddyNext's own upload winning when a member has both is deliberate: it is the avatar they set in this community. What is not acceptable is a placeholder outranking anyone's real picture.
Adding another avatar source? Register at 10 if it holds real uploads. Never register a generated or default image at 10 - that is the mistake this documents.
Building a plugin that needs to read a member's avatar (rather than provide
one)? Pull it through buddynext_user_avatar_url() - see
Member Identity Seams. It returns this same
resolved avatar and is never empty.
Adding a surface
When either plugin adds a screen the other could also render:
- Add a row here first, with an owner, before writing the UI.
- The non-owner exposes data (REST or a filter) and renders nothing.
- If the non-owner already has its own version of that screen, say here whether it stays reachable, and close the door if not.
- Add a check to
bin/check-mediaverse-surfaces.phpthat fails if the rule is broken. A rule nothing enforces is a rule that rots - the "no MV assets on BN pages" rule held for two years because it was easy to honour, not because anything tested it.
What is enforced
bin/check-mediaverse-surfaces.php, wired into bin/check.sh, fails the build on:
| Rule | Detected by |
|---|---|
| No WPMediaVerse JS/CSS enqueued from BuddyNext | wp_enqueue_script/style( 'mvs*' ) anywhere in includes/ |
| Generated avatars never race real ones | filter_avatar_fallback missing, registered at priority <= 10, or initials generated inside the priority-10 filter |
| Comment controls follow the engine | lightbox.js no longer reading can_edit / can_delete off a comment |
Mutation-tested - each rule was broken in turn and the check failed for each, then passed again once restored. A guard that has never been seen to fail is not a guard. The comment-flag check originally matched anywhere in the file and was satisfied by the docblock explaining the flags while the code had stopped reading them; it now strips comments first and requires a real property read.