Skip to content

fix(hc-custom): scope group events calendar to the group and link into it - #133

Merged
MartinPaulEve merged 8 commits into
devfrom
bug/group-calendar-scope-131
Oct 4, 2026
Merged

MartinPaulEve merged 8 commits into
devfrom
bug/group-calendar-scope-131

Conversation

@MartinPaulEve

@MartinPaulEve MartinPaulEve commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #131
Fixes #132
Fixes #134

The group Events calendar was showing every event on the site, and its entries linked to the canonical event page rather than the group's own event page. Both come from the same thing: Event Organiser loads calendar events through an admin-ajax request, and BuddyPress doesn't parse the group URL for AJAX requests, so bp-event-organiser's group-aware filters never ran for the calendar data.

This passes the group ID along with the calendar's AJAX request and uses it on the server side to restrict the query to the group's events and to point each entry at <group>/events/<slug>/. All in hc-custom; no theme change.

Following review, the group scope is only honoured for groups the caller can see (public groups, members, community moderators); anything else gets an empty calendar rather than the unscoped one. While checking that, it turned out hc-custom was granting read_private_events to everyone, which let anonymous visitors list private-group events through the calendar endpoint. That is fixed here too (#134). Visibility of private-group events now comes from group membership: members see them on their group's calendar and on the sitewide calendar; anonymous visitors and non-members see public events only.

Unit tests cover the group resolution, access checks, the query filters, the link rewriting and the capability mapping. Checked end to end in the local test stack with public and private groups as an anonymous visitor, a non-member and a member.

The member calendar has the same underlying AJAX-context issue and is not touched here.

Event Organiser fetches calendar events via admin-ajax, where BuddyPress
skips URI parsing so bp_is_group() is false and bp-event-organiser's
bp_group calendar filter never applies. Forward the calendar's bp_group
setting on the AJAX request and resolve the group from it (or from page
context) when building the calendar query.

Fixes #131
bp-event-organiser rewrites calendar links to <group>/events/<slug>/ only
when bp_is_group() is true, which never holds inside the admin-ajax
request that builds the calendar payload. Resolve the group the calendar
is for and rewrite links for events connected to it.

Fixes #132

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Client-supplied group IDs enable private-event queries without validating the caller’s group access.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Scopes group calendars and their event links through hc-custom, addressing missing BuddyPress group context in AJAX requests.

Changes:

  • Passes calendar group IDs to server-side query and link filters.
  • Adds unit tests for group resolution, scoping, and links.
  • Leaves member calendars unchanged.
File Description
tests/​unit/​group-permissions-loader.php Adds per-group permalink mocks.
tests/​unit/​BpeoGroupCalendarScopeTest.php Tests group resolution and query scoping.
tests/​unit/​BpeoGroupCalendarLinksTest.php Tests group-specific event links.
plugins/​hc-custom/​includes/​js/​bpeo-group-calendar.js Sends group IDs with calendar requests.
plugins/​hc-custom/​includes/​bp-event-organiser.php Adds calendar query, link, and script hooks.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread plugins/hc-custom/includes/bp-event-organiser.php Outdated
The calendar AJAX endpoint has none of the group page's access control,
so a caller could name any group ID and receive that group's events,
including the private events of private and hidden groups. Only honour
the bp_group scope for public groups, group members and community
moderators; otherwise return an empty scope (no events) rather than the
unscoped calendar. Where access is granted, drop EO's capability-based
'readable' restriction so members still see their group's private
events.

Refs #131
The event capability mapping treated read_private_events as a cap with
no event context and fell through to the branch that returns 'exist',
so every visitor, including anonymous ones, passed WP_Query's readable
check for private events. Events in private and hidden groups are
stored as private posts and were therefore listed for anyone. Leave the
primitive cap on WordPress's role-based mapping; group members see
their group's private events through the group-scoped calendar query,
which checks group membership itself.

Fixes #134
With read_private_events no longer granted to everyone, the unscoped
calendar hid private-group events from the groups' own members. For a
logged-in user, look up the private events connected to their groups
and admit exactly those (plus their own private events) alongside the
public ones via a posts_where restriction. Anonymous visitors and
non-members continue to see public events only; group-scoped and
member calendars are left to their own visibility handling.

Refs #134

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The sitewide filter hides otherwise-authorized private events from callers with role-based access.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread plugins/hc-custom/includes/bp-event-organiser.php Outdated
The sitewide membership restriction was also applied to callers whose
role grants read_private_events, hiding private events outside their
groups that EO's 'readable' query already authorises them to see. Leave
the query unchanged for those callers.

Refs #134

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Public-group calendar requests can still expose private events connected to both public and private groups.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Unbounded private-event lookup bloats calendar queries

plugins/​hc-custom/​includes/​bp-event-organiser.php:745

For members without the role-based read_private_events capability, each sitewide calendar request looks up every private event in their groups, including the entire history, and embeds the IDs in the calendar query. The lookup has no calendar date bounds, so its result size and the resulting IN clause grow with group history even when viewing one month. Pass the calendar's date constraints into this lookup, or use a membership subquery instead of materializing all historical IDs.

Comment thread plugins/hc-custom/includes/bp-event-organiser.php Outdated
…oups

Passing the group access check for a public group was enough to drop
EO's 'readable' restriction, so an anonymous visitor could see a
private event connected to both that public group and a private group.
Only members of the requested group and community moderators bypass
the capability check; other visitors keep the role/author rules.

Refs #131

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Privacy-sensitive query integration needs human validation, and the unbounded historical-event lookup remains unresolved.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Unbounded private-event history loaded for calendar requests

plugins/​hc-custom/​includes/​bp-event-organiser.php:751

For members without read_private_events, each sitewide calendar request loads all private events from their groups, including the entire history, and embeds the IDs in the main query. Fetching one month therefore scales with the groups' full event history rather than the displayed range. BPEO also disables calendar transient caching (plugins/bp-event-organiser/includes/functions.php:625-626), so navigation repeats this work. Pass the calendar's date bounds into the lookup, preserving overlap and recurring-event behavior, or apply the membership restriction directly in SQL instead of materializing every ID.

The sitewide calendar looked up every private event across a member's
groups on each request and embedded the IDs in the main query, so one
month's fetch scaled with the groups' whole history (and bpeo disables
EO's calendar cache, so navigation repeated it). Express the membership
rule as a subquery on the bpeo_event_group term relationships inside
the main query, which is then bounded by the calendar's date range.

Refs #134

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Authorization-sensitive changes require human verification of Event Organiser’s runtime AJAX integration.

Review effort: Balanced
Findings: None

@MartinPaulEve
MartinPaulEve merged commit 828adc5 into dev Oct 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants