[buddypress-trac] [BuddyPress Trac] #9327: Enhancement Request: Add `privacy` column to `bp_activity` for per-item visibility control (was: Feature Request: Add `privacy` column to `bp_activity` for per-item visibility control)

buddypress-trac noreply at wordpress.org
Fri Feb 6 02:24:54 UTC 2026


#9327: Enhancement 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:  Activity     |     Version:
 Severity:  normal       |  Resolution:
 Keywords:               |
-------------------------+------------------------------
Changes (by emaralive):

 * version:  14.4.0 =>
 * component:  Core => Activity


Old description:

> #### 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.

New description:

 == 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.

--

Comment:

 [[span(style=color: #FF0000, Edited the description for readability.
 Slightly modified the Summary. )]]

 For reference, this started as a support forum topic, see
 [https://buddypress.org/support/topic/add-privacy-column-to-bp_activity-
 for-per-item-visibility-control/ Add `privacy` column to `bp_activity` for
 per-item visibility control]

-- 
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/9327#comment:1>
BuddyPress Trac <http://buddypress.org/>
BuddyPress Trac


More information about the buddypress-trac mailing list