[buddypress-trac] [BuddyPress Trac] #9327: Feature Request: Add `privacy` column to `bp_activity` for per-item visibility control
buddypress-trac
noreply at wordpress.org
Thu Feb 5 23:59:14 UTC 2026
#9327: Feature Request: Add `privacy` column to `bp_activity` for per-item
visibility control
-------------------------+-----------------------------
Reporter: indigetal | Owner: (none)
Type: enhancement | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Core | Version: 14.4.0
Severity: normal | Keywords:
-------------------------+-----------------------------
#### The Need
BuddyPress activities have a single visibility control: `hide_sitewide` (0
or 1). This provides binary visibility — shown everywhere, or hidden from
sitewide feeds. There's no way to express:
- **"Only me"** — private activity visible only to the author
- **"Friends only"** — visible only to the author's friends
- **"Logged-in users"** — hidden from anonymous visitors
- **"Group members only"** — visible only within the group context where
it was posted
Per-item privacy is a foundational social platform feature. Facebook has
had it since 2009. LinkedIn, Twitter/X (with protected tweets), Instagram,
and every major social network support some form of per-post audience
control. It's as fundamental to a social platform as `hide_sitewide` is —
just multi-valued instead of binary.
This is not a request for any specific addon's needs. It's a request for
the same kind of infrastructure that `hide_sitewide` provides — a first-
class column that any addon can use to implement privacy-aware features.
#### Current State
BuddyPress 14.4.0's activity query system is well-designed for
extensibility:
- `bp_activity_get_where_conditions` lets addons inject WHERE clauses
- `bp_activity_get_join_sql` lets addons add JOINs
- Data hydration uses `SELECT *` (in `populate()` and the cache-miss path
of `get()`), so any column added to the table is automatically returned to
calling code
- `bp_activity_before_save` / `bp_activity_after_save` provide save-time
hooks
An addon **can** add a `privacy` column via `dbDelta` and filter queries
via `bp_activity_get_where_conditions`. However, the `save()` method has a
hardcoded column list in its INSERT/UPDATE SQL (in
`BP_Activity_Activity::save()`), meaning an addon must do a separate
`$wpdb->update()` after every save — a double-write on every activity
creation. This works but is suboptimal, and the column's general-purpose
nature (used by media addons, document addons, moderation addons, group
addons — not just one feature) argues for core inclusion.
#### Proposed Change
**Schema:** Add one column + index to `bp_activity` in
`bp_core_install_activity_streams()` (in `bp-core-admin-schema.php`).
`dbDelta()` handles adding the column on upgrade automatically:
```sql
-- Added after the existing `is_spam` column in the CREATE TABLE
definition:
privacy varchar(75) NOT NULL DEFAULT 'public',
-- Added to the KEY list:
KEY privacy (privacy)
```
**Model class (`BP_Activity_Activity`):**
1. Add `public $privacy = 'public';` property
2. In `save()` — add `privacy` to the INSERT/UPDATE column list alongside
the existing columns (user_id, component, type, action, content, etc.)
3. In `get()` — add `'privacy'` to the `$r` defaults (default `false`,
meaning no filtering). When set, add `AND a.privacy IN (...)` to the WHERE
conditions.
**REST API (`BP_REST_Activity_Endpoint`):**
4. In `prepare_item_for_response()` — expose `privacy` in the response
object (paralleling how `hide_sitewide` is currently exposed as `hidden`)
5. In `prepare_item_for_database()` — accept `privacy` as a writable field
on create/update
**Migration:** `dbDelta()` runs on every upgrade via `bp_core_install()`,
so modifying the CREATE TABLE definition is sufficient for the schema
change. A DB version bump in `bp-core-update.php` tracks the upgrade.
#### Scope
- ~40 lines of changes across 4 files (`bp-core-admin-schema.php`, `class-
bp-activity-activity.php`, `class-bp-rest-activity-endpoint.php`, `bp-
core-update.php`)
- Default value `'public'` — 100% backward compatible
- Existing queries that don't pass `privacy` return all activities as
before
- `hide_sitewide` continues to work as-is (orthogonal — `hide_sitewide`
controls directory listing, `privacy` controls per-item access)
#### What Core Provides vs. What Addons Handle
To be explicit about scope: this proposal adds **storage and query
filtering only**. Core would:
- Store the `privacy` value on each activity row
- Allow `get()` callers to filter by privacy value(s)
- Expose/accept the field through the REST API
Core would **not** need to:
- Define a fixed set of allowed values (addons register the values they
need — e.g., `'onlyme'`, `'friends'`, `'loggedin'`)
- Enforce access control based on privacy values (addons hook into
`bp_activity_get_where_conditions` to inject viewer-aware WHERE clauses,
exactly as they would today for any custom filtering)
- Provide UI for selecting privacy (addon/theme territory)
This mirrors how `hide_sitewide` works today — core stores the flag and
filters on it; the decision of *when* to set it is made by callers (groups
component, plugins, etc.).
#### Prior Art
BuddyBoss Platform (a BuddyPress-derived platform) implemented this exact
column. Their `bp_activity` schema includes `privacy varchar(75) NOT NULL
DEFAULT 'public'`, and their media, document, video, and moderation
subsystems all depend on it for privacy filtering. The column is
referenced in their activity save/query paths, REST API, and template
layer — making it one of the most cross-cutting additions they made to the
activity table. The fact that an independent platform needed this to build
standard social features demonstrates both the demand and the general-
purpose nature of the column.
I'm happy to submit a PR for this change.
--
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/9327>
BuddyPress Trac <http://buddypress.org/>
BuddyPress Trac
More information about the buddypress-trac
mailing list