Repository navigation
Release 0.36.1 - #2446
Release 0.36.1#2446
Conversation
…mplates (#2444) Venue\Setup::maybe_apply_venue_template() ran the venue template pattern's content through apply_block_hooks_to_content(), which core marks private. WP_Block_Patterns_Registry::get_registered() already applies hooked blocks to a pattern's content before returning it (WordPress 7.0 included), and leaves ignoredHookedBlocks metadata that made the second pass a no-op, so the content can be used as returned. Adds a test that a block hooked into the venue template still lands in the seeded content exactly once. Fixes #2443. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 67e3b23)
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
* fix: keep RSVP focus on screen after an RSVP change After an RSVP change, the success callback of updateRsvp() opened the next state's modal and closed the current one after a fixed 10 ms. On a full event, Attend returns waiting_list, but the callback always opened the attending modal, which stays hidden, so focus fell to the page body. On other events, the renderRsvpBlock watch sometimes showed the new state after the 10 ms, so focus again fell to the page body, now and then with the attending modal open. Use the status in the server response to pick the next modal, so Attend on a full event opens the waiting list modal. Add whenStatusShown(), which waits one animation frame at a time, for up to about a second, until the new state's container is shown, and only then opens the next modal or closes the current one. A missing or never-shown container still ends in a full close (#1719). Add Jest tests for the waiting list, Attend and Not Attending when the new state shows late, and a state that never shows. Add a changelog entry. Fixes #2428 Co-Authored-By: Claude <noreply@anthropic.com> * fix: wait for the no_status fallback before closing the RSVP modal When the RSVP block has no container for the status the server returns (partial block markup, #1719), whenStatusShown() got a null container and called back at once. closeModal() then ran before renderRsvpBlock hid the old state and showed its no_status fallback, so from the not attending state focus went to a trigger that was hidden a frame later. Wait for the same no_status container that renderRsvpBlock shows, and open a modal only for a real container of the new status. Say in the whenStatusShown() docblock that the wait is 60 animation frames, about a second only at 60 Hz, and that animation frames pause in a background tab. Add a Jest test for not attending to waiting list in a block that has no waiting_list container. Make the simulated watch fall back to no_status the same way renderRsvpBlock does, and make the not attending button request attending. Co-Authored-By: Claude <noreply@anthropic.com> * test: cover the RSVP modal close when no status container exists Add a Jest test for a block that has neither the container of the returned status nor a no_status container. whenStatusShown() gets no container and gives up at once, so updateRsvp() must fully close the modal without opening another one. Co-Authored-By: Claude <noreply@anthropic.com> * test: assert where focus lands with the real modal actions The #2428 tests swap openModal and closeModal for spies, so they could pass while the real transition focused the wrong control. Run updateRsvp against the real modal-manager actions, with a modal manager in each status container, and assert document.activeElement after Attend on a full event (waiting list modal), Attend with room (number of guests), and Not Attending (the not attending state's Edit RSVP trigger). jsdom has no layout, so these pin the focus target rather than reproduce focus dropping to the page body. Against develop's view.js the waiting list and Not Attending cases fail; the attending case passes there because jsdom focuses the hidden input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Mike Auteri <mauteri@gmail.com> (cherry picked from commit 8d3094e)
* fix: name RSVP and Login dialogs after their visible title
Screen readers announced the RSVP block's dialogs as "RSVP Modal" and "Login Modal". These are the editor names from the block templates. The visible title ("RSVP to this event", "You're Attending", "Login Required") and the instructions below it were not read when the dialog opened.
For modals with an RSVP template class (gatherpress-modal--type-rsvp, gatherpress-modal--login, gatherpress-modal--type-login), Modal::apply_modal_attributes() now points aria-labelledby at the title, the first paragraph or heading, and aria-describedby at the next paragraph. A title that a site owner changed to a Heading block still names the dialog, and the instructions do not become its name. An element keeps an id it already has; otherwise it gets a unique one. The editor name stays as the aria-label fallback. Other modals keep the #2366 behavior.
The RSVP block renders its saved templates with do_blocks() on each request, so existing events change without an edit, and the templates do not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: add changelog entry for #2462
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e77cd57)
…2450) * fix: tell apart RSVP forms with the same layout when finding a schema id Rsvp_Form::blocks_match() identified the rendered form in the post content by comparing innerHTML. A block's innerHTML holds only its wrapper and one blank line per inner block, so two forms with the same wrapper and number of fields matched each other, and the second rendered the first one's schema id. Its submissions were then validated and saved against the first form's fields: rejected for a required field it does not have, or with its own answers dropped. blocks_match() now also compares the inner blocks' names, attributes and markup, recursively. The form's own attributes stay out of it, because they can be filtered at render. Two truly identical forms still share the first id, but their schemas are identical. Fixes #2448. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: cover RSVP forms that differ by inner block name, markup or depth Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: match RSVP forms by their fields so hooked blocks don't break it A block added inside a form by Block Hooks is in the rendered form but not in the saved one, so comparing every inner block failed to find the form and every form fell back to form_0, whose schema rejected each submission. Compare the form's own markup and the fields its inner blocks hold instead. Forms with the same fields store the same schema, so either one's id fits both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b34462a)
* fix: start the address search with no suggestion active The venue address search made the first suggestion active as soon as the list appeared. The first ArrowDown then moved to the second suggestion, so ArrowDown and Enter saved the second address, not the first. This differs from the ARIA combobox pattern, where no option is active until an arrow key is pressed. Start activeIndex at -1 and reset it to -1 when the suggestions change. ArrowDown from -1 goes to the first suggestion and ArrowUp to the last. Enter with no active suggestion closes the list and keeps the typed address. fetchAddressSuggestions() now leaves out a row whose label repeats an earlier one, so each address shows once. Add Jest tests for no active option before an arrow key, Enter keeping the typed text, ArrowUp from no active option, the reset on new suggestions, and duplicate labels. Update three tests that expected the first suggestion to be active from the start. Add a changelog entry. Fixes #2449 Co-Authored-By: Claude <noreply@anthropic.com> * fix: fill five address suggestions and keep a closed list closed The server asked Photon for 5 results when no country filter was set and kept up to 5, so a repeated label used up a place, and the client then showed fewer than 5 suggestions. Ask Photon for 10 candidates in every search (SEARCH_CANDIDATE_LIMIT), and skip a label that an earlier suggestion already has before the 5-row cap. Keep the client-side check for results cached before this change. closeSuggestions() aborted only a request already in flight. A search still waiting for its 400 ms debounce ran after Enter or Escape and opened the list again. Cancel the debounced search too. Add PHPUnit coverage for repeated labels and change the search URL test to expect limit=10. Add Jest tests that use the real debounce for Enter and Escape before the search runs, and give the pass-through debounce mock in the field tests a cancel method. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> (cherry picked from commit 38b681b)
Screen readers named the "Date & time start" and "Date & time end" buttons in the event settings only by their heading label. The heading's <label for> replaced the button text, so the date on the button was not read. NVDA said "DATE and TIME START, button, focused, collapsed". The label and the date text in the button now each have an id, and the button's aria-labelledby points at both. The name is the label and the date, for example "Date & time start October 21, 2026 6:00 pm", or "Date start October 21, 2026" for an all-day event. aria-labelledby points at the date span, not at the button, because a referenced button still takes its name from its <label> first. htmlFor stays, so a click on the heading still opens the picker, and the button ids stay. Add Jest tests for both buttons: the name with and without All day, and the label click. Add the changelog entry. Fixes #2466 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit bd802fc)
* fix: remove the Login modal for logged-in users The RSVP block template gives the Login modal gatherpress-modal--login, but filter_login_modal() only looked for gatherpress-modal--type-login, which the class was renamed to everywhere else in c7c4ca3. The modal stayed in the page for logged-in users, hidden and unused. is_login_modal() matches both classes, since saved events keep the template's, and filter_login_modal() and is_rsvp_block_modal() share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * doc_update: say which code uses each Login modal class Only class-modal.php uses gatherpress-modal--type-login. The template, the e2e helper and saved events use gatherpress-modal--login, so the docblock no longer reads as if --login were the old name. The changelog says logged-in viewers, since member is a site role. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 76c50e6)
(cherry picked from commit b347ef5)
* Skip the shared calendar feed cache for logged-in viewers * fix: call is_feed() inside the ICS renderer The renderer runs in the same request, right away for logged-in viewers or inside remember(), so copying is_feed() into a variable first added nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: cache calendar feeds per viewer scope instead of skipping the cache Skipping the cache for every logged-in request meant any account, Subscribers included, could make the site render every upcoming event on each feed request. The events query only differs by which private events a viewer may read, so the cache key now carries that scope instead: public for viewers who see no private events, private for viewers who read all of them, and a per-user scope only for a viewer whose own private events set their feed apart. Every request is cached again, and only the public scope is sent with Cache-Control: public. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: move the viewer scope into Cache to keep Setup under PHPMD's limit The scope method took Calendar\Setup to a class complexity of 152, over PHPMD's threshold of 150. It is about cache scope, so it lives on Cache as get_viewer_scope() now, with its tests in Test_Cache. No behavior change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: return early from get_viewer_scope() Bail out for logged-out viewers and for viewers who read every private event, instead of wrapping the rest of the method in the logged-in check. Three returns, same behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: assert the Cache-Control visibility in a unit test - Move the public/private choice into Cache::get_cacheability(). - Call it from Setup::send_ics_headers(); the header is unchanged. - Add test_get_cacheability() for anonymous, Subscriber, Administrator and Author viewers. Addresses PR feedback. Refs #2473 * fix: give an unpublished queried event a per-viewer calendar scope * fix: send a password-protected event's calendar as private A visitor who entered an event's password can be in the public scope, so the event's .ics went out with Cache-Control: public. A shared cache could then hand it to people without the password. get_cacheability() returns private for any queried post with a password. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: decide an unpublished event's calendar privacy in get_cacheability() An unpublished or password-protected queried post only changes who outside WordPress may store the response, not what the body holds: every viewer who can reach it gets the same body, because WordPress answers anyone else with a 404 before the cache is read. Both cases now live in get_cacheability(), and get_viewer_scope() is back to describing which events a viewer's feed lists, so those viewers share one cache entry instead of one each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mike Auteri <mauteri@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5698156)
* fix: make strings shown in the editor and admin translatable * chore: add changelog entry for #2477 * fix: join the Credits list with core's wp_sprintf_l() Core already ships translated list separators for this, in every locale WordPress supports, so the Credits list uses wp_sprintf_l() instead of three new GatherPress strings that start or end with spaces. The closing period becomes a translatable string too, for languages with a different full stop. The link partial no longer takes a separator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mike Auteri <mauteri@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 2c0ac51)
…he REST API (#2481) * fix: keep password-protected events out of calendar feeds and links A calendar feed is shared by every viewer, so it now leaves out password-protected events instead of listing their date, time and location. The single-event calendar links (iCal, Outlook, Google, Yahoo) only answer a viewer who has entered the event's password; the check runs before the cache is read, so the cached body is only served to viewers allowed to see it. get_events_list() takes an optional has_password argument for this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * doc_update: say calendar feeds leave out password-protected events Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: blank a password-protected event's date and place in REST responses The event's date, time and timezone meta and the venue geo fields copied onto it are registered with show_in_rest, and core only hides a protected post's content and excerpt, so the REST API handed them to anyone. prepare_event_data() now blanks them unless the viewer can edit the event or has its password, as a cookie or as the REST password argument, the way core gates the content. The rest_prepare filter now receives the request for that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: hide a protected event's date and place on every event post type The REST blanking ran on rest_prepare_gatherpress_event, so a companion post type with gatherpress-event-date support, which gets the same date and geo meta, still handed a protected post's date and place to anyone. Event\Meta now hooks hide_password_protected_meta() on rest_prepare_ for every event-date post type, next to the read-only meta filter it already adds there. Rest_Api::prepare_event_data() is back to handling the online link only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: blank a protected event's place after the geo refresh Geo_Sync::maybe_refresh_on_rest() runs on the same rest_prepare hook and writes refreshed geo_* values back into the response when an event's stored location is out of date. Both filters sat at priority 10 and the blanking filter was added first, so the first read after a venue change showed the address and coordinates. Run hide_password_protected_meta() at priority 20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit aca935e)
#2474) * Respect post passwords in the RSVP Response, Template and Count blocks * test: cover the RSVP blocks once the viewer has the event password * test: assert RSVP data in the password-protected block tests The with-password tests checked markup each block emits even with no responses. Save an attending RSVP and assert the count, the response counts and the attendee's roster entry instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mike Auteri <mauteri@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 38daf15)
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @hifarrer. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Props Bot commandsYou can trigger Props Bot actions by commenting on this PR:
|
mattcowan
left a comment
There was a problem hiding this comment.
One note to make here:
#2472 hides the All Members checkbox from users who can't use it, but the help text above it still says "All Members" includes site users only (src/modals/email-communication/index.js:148). Do you want a develop fix that shows that sentence only with the checkbox in this release, or can it wait for 0.37?
* Skip calendar endpoints when the requested slug is not theirs Every calendar endpoint shares the gatherpress_calendar query var and hooks template_redirect. Endpoint::template_redirect() looked up the type matching that slug and called activate() on the result without checking it, so an endpoint whose validation passed but which has no type for the slug crashed with "Call to a member function activate() on false". Two requests reach that state: - /event/my-event/outlook/?page=<junk>&feed=<junk>: the page value 404s the singular query, set_404() keeps is_feed but clears is_singular, and the sitewide feed endpoint (ical only) validates while the slug is still "outlook". Vulnerability scanners send this shape. - /event/my-event/outlook/?gatherpress_calendar=nope: the query string value overrides the rewrite value. Bail when no type matches, so WordPress handles the request as it would without the endpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Prefix the changelog entry with its PR number Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit bcbf39f)
…2507) After an RSVP change, updateRsvp opens the new state's modal, which focuses its first control on a 10 ms timer, and closes the starting modal on its own 10 ms timer scheduled just after. The waiting list and attending tests waited only for focus, then checked at once that the starting modal was hidden, so a check between the two timers failed (seen on CI for #2496). Check both inside waitFor(). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 02ceb80)
* fix: keep an Event Query Loop unchanged when the editor opens The naming and query cleanup effects read usePostTypeSupports(), which is false until getPostType() resolves. On the first render they treated an event loop as a plain loop: the cleanup removed gatherpress_event_query and include_unfinished and set orderBy to date desc, and the naming effect replaced the name with "" and then "Upcoming Events". Both effects now wait for the new useIsPostTypeLoaded() hook. The naming effect also keeps the other metadata keys, and it replaces only an empty name or a name it generates: for the current post type, for the post type before a switch, or for the fallback "Events" label. A name that the author gave the loop stays. Add a changelog entry, and note in AGENTS.md that an effect that acts on a missing support must wait for the post type to load. Co-Authored-By: Claude <noreply@anthropic.com> * fix: keep the event query panel's naming from dirtying the post Naming a loop that has no name now uses __unstableMarkNextChangeAsNotPersistent, as core's Query block does for its defaults, so opening a loop saved without a name no longer shows unsaved changes. The name is saved with the next save. Renames after a mode or post type switch stay normal edits. When the panel clears a name, it now removes the name key, as core's rename modal does, and removes metadata that has no keys left, so the block no longer saves "name":"". A comment also says that a name generated in another locale counts as an author's name. Co-Authored-By: Claude <noreply@anthropic.com> * fix: count a post type as loaded only once its record is in the store useIsPostTypeLoaded() also returned true when the getPostType() request finished without a record. A failed request does that, for example the 403 core sends for /wp/v2/types/<type>?context=edit to a user who can edit the page but not posts of that type. Its supports are unknown then, but the cleanup read them as missing and stripped the loop on every open: the #2495 symptom, every time instead of on a race. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Mike Auteri <mauteri@gmail.com> (cherry picked from commit 7f34301)
Feed::get_default_event_excerpt() and get_default_event_content() added "Date:" and the venue to every event in an RSS feed, including a password-protected one, whose excerpt and content core already replaces. Leave those untouched when the post password is required. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 82d8347)
…2497) * fix: leave password-protected events out of the upcoming events ability The gatherpress/get-upcoming-events ability is public (REST and MCP) and returned a protected event's start, end and timezone. Query with has_password false, as the calendar feeds do since #2481. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * doc_update: Say the upcoming events ability leaves out protected events The permission comment still said the ability adds no gate of its own, but the callback now drops password-protected events itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d003170)
#2506's docs updates ship in 0.36.1, so the credit goes on the patch branch rather than develop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: update settings names, RSVP mode values and requirements Bring the docs back in line with the code after the settings renames: - RSVP mode values are enabled, per_event_enabled and per_event_disabled (were all_on, per_event_on, per_event_off), with the labels shown on the settings screen. - "Maximum Attendance Limit" is now "Capacity". - Venue map defaults: drop "Default Width" (removed), add "Default Map Type" (Google Maps only). - Developer settings tables: enable_rsvp_cleanup defaults to 'disabled', the RSVP tab class is Settings\Rsvp, add event_archive, and list venues_url under the Venues tab where it lives. Also fix two other outdated statements: the installation page now says WordPress 7.0 (the plugin's minimum), and the FAQ no longer calls iCal "planned", since the Add to Calendar block offers iCal and Outlook. * doc_update: add the changelog entry for the settings docs update Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * doc_update: rename the RSVP Mode values and Capacity in two more docs The features list still named the per-event modes "default on" and "default off", and the event settings list still called Capacity "Maximum attendance limit", which is no longer the editor's label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mike Auteri <mauteri@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e473fa2)
|
@mattcowan Good catch on the All Members help text. It's going into this release: #2510 shows that sentence only when the checkbox is, and I'll cherry-pick it here once it merges. |
…2510) #2472 hides the All Members checkbox from users who can't send to all members, but the help text above the checkboxes still explained the option to everyone. Show that sentence only with the checkbox, and keep both versions whole strings for translators. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 74ec271)
RSVP Count, Form and Response declare `state: { posts: {} }` in their
store() calls, but RSVP Template read `state.posts[ postId ]` without
declaring it. When renderBlocks ran before another RSVP block had
registered, `state.posts` was undefined and the list failed to render
with a TypeError, seen on an events archive in iOS Safari.
Fixes #2505
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 11cc6e5)
…#2498) * fix: keep a protected event's date, calendar links and venue out of blocks The Event Date and Add to Calendar blocks, and the Venue block's lookup of an event's venue, checked only Event::is_viewable(), which ignores the post password. In a Query Loop on another page they showed a protected event's date, calendar links and venue to anyone. Add the password check the Online Event block already makes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a protected online event's Online Event block behind its password Online_Event::render_block() checked the password only for a postId override. In a Query Loop the block reads the event from the loop with no override, so a password-protected online event still said it was online while its date, calendar links and venue were hidden. Return '' there too until the viewer has the password. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit c98ea91)
* fix: keep a password-protected event's venue hidden The venue taxonomy is public, so core listed a protected event's venue term in REST, in /wp/v2/_gatherpress_venue?post=<id>, in its post classes and REST class_list, and in the venue feed link in its head. Lists filtered by venue also showed the protected event under its venue. - Event::is_hidden_by_password() holds the rule: the viewer needs the password and can't edit the event; a REST request's right `password` argument counts. Event\Meta's date and place meta now use it. - Venue\Setup blanks the venue field on event REST responses, returns no term from the venue terms endpoint for such a post, drops the venue classes, and leaves protected events out of venue-filtered queries for anyone who can't edit others' events (not in wp-admin). - Calendar\Setup leaves the venue feed link out of a protected event's head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: register the venue REST hooks in the REST test rest_api_init only fires for the first REST server a test run creates, and hooks added during an earlier test are removed after it, so a run where another test creates the server first would miss the filters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a protected event's venue out of the Post Terms block Core adds a Post Terms variation for each public taxonomy, and the block prints terms with get_the_term_list(), which none of the venue filters reach, so a Query Loop or template still named a protected event's venue and linked to it. Empty the block when its term is the event's venue taxonomy and the event's password hides it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: run the venue list filter at priority 11 It only has to run after Event\Query::prepare_event_query_before_execution(), which adds the venue clause on the default priority 10, so stay just after the default rather than at an arbitrary 20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: honor the password in the venue terms endpoint and keep an empty taxonomy list out of get_terms() The venue terms endpoint now passes the request to the password check, so the right password lists a protected event's venue, as the event endpoint already does. When dropping the venue taxonomy leaves no taxonomy, the head's term links return early, since get_terms() with an empty list matches every term on the event, the venue's included. Cover a set rest_base, a response without the venue field and a terms request without a post. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 756370c)
…ate (#2501) * fix: keep a password-protected event's venue hidden The venue taxonomy is public, so core listed a protected event's venue term in REST, in /wp/v2/_gatherpress_venue?post=<id>, in its post classes and REST class_list, and in the venue feed link in its head. Lists filtered by venue also showed the protected event under its venue. - Event::is_hidden_by_password() holds the rule: the viewer needs the password and can't edit the event; a REST request's right `password` argument counts. Event\Meta's date and place meta now use it. - Venue\Setup blanks the venue field on event REST responses, returns no term from the venue terms endpoint for such a post, drops the venue classes, and leaves protected events out of venue-filtered queries for anyone who can't edit others' events (not in wp-admin). - Calendar\Setup leaves the venue feed link out of a protected event's head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: register the venue REST hooks in the REST test rest_api_init only fires for the first REST server a test run creates, and hooks added during an earlier test are removed after it, so a run where another test creates the server first would miss the filters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a password-protected venue's name and details private Only the Venue block checked a venue's password. The venue's REST record still exposed its address, contact, structured address, geo and static map meta; Geo_Sync copied its address onto its events; calendars and RSS feeds named it with its address; its events' REST records, post classes and head feed links named it; and its taxonomy term was named "Protected: <title>". - Has_Capabilities::is_hidden_by_password() replaces Event's copy, so Event and Venue share the rule. - Venue\Meta blanks the details on rest_prepare_<venue type>. - Geo_Sync treats a password-protected post as not public. - Event::get_public_venue_information() blanks such a venue for calendars and RSS unless the viewer can edit it; emails keep get_venue_information(). - Venue\Setup::is_event_venue_hidden() hides the venue for either password, for the REST field, the terms endpoint, the post classes and the head link. - Shadow_Source names terms with the_title on post_title, without the "Protected: " or "Private: " prefix get_the_title() adds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: address review on protected venue details - Whoever can edit an event keeps its venue in REST. An Author editing their own event at another user's protected venue got an empty venue field, and the online-event toggle then saved the event without it. - Calendars and RSS blank a protected venue for everyone. The edit exception let a venue's Author fill the public calendar cache, since Calendar\Cache gives an Author the public scope. - The event REST filter blanks the copied geo_* fields whenever the event's venue is hidden, not only when the event is protected, so a past event's stored copy stays out of REST. - Saving a venue that isn't public clears the location its events copied, past events included, writing what maybe_refresh() would. - The changelog says the venue's details and its link to events stay private; its name stays public, as for any protected post. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: hide a protected venue's copied location from the event's editor hide_password_protected_meta() reused is_event_venue_hidden(), whose event-editor exception exists so the editor doesn't drop the venue term on save. That exception also let an event's Author see geo_* copied from a protected venue they can't see. The geo_* fields are read-only, so they now use is_venue_hidden_by_password(), which checks only the venue's own password. clear_event_copies() also primes the events' meta in one query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a protected event's venue out of the Post Terms block Core adds a Post Terms variation for each public taxonomy, and the block prints terms with get_the_term_list(), which none of the venue filters reach, so a Query Loop or template still named a protected event's venue and linked to it. Empty the block when its term is the event's venue taxonomy and the event's password hides it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: run the venue list filter at priority 11 It only has to run after Event\Query::prepare_event_query_before_execution(), which adds the venue clause on the default priority 10, so stay just after the default rather than at an arbitrary 20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: honor the password in the venue terms endpoint and keep an empty taxonomy list out of get_terms() The venue terms endpoint now passes the request to the password check, so the right password lists a protected event's venue, as the event endpoint already does. When dropping the venue taxonomy leaves no taxonomy, the head's term links return early, since get_terms() with an empty list matches every term on the event, the venue's included. Cover a set rest_base, a response without the venue field and a terms request without a post. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: give events back the location a venue cleared once it is public again A venue set to draft, private or a password clears the location its events copied, past events included. When it became public again, upcoming events got the location back on their next view, but past events stayed blank for good, since maybe_refresh() skips them. clear_event_copies() now keeps the locations it clears on the venue (_gatherpress_geo_cleared_copies), keeping the first copy if the venue is saved again while hidden. A public venue save writes them back with restore_event_copies(), to events still at the venue, with geo_public from each event's own status. The exact copy comes back rather than the venue's current address, so a past event keeps where it happened, as Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d97febc)
The bump resolves every credited username on WordPress.org, and hifarrer's GitHub username has no WordPress.org profile (404), so the bump would fail. They haven't shared their WordPress.org username on #2506, so they can be credited in a later release once they do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ran npm run version:bump -- --version=0.36.1 with credits/0.36.1.json copied from 0.36.0.json, then refreshed the lockfile. It folds camaleaun in from unreleased.json and resolves the fixes' @SInCE TBD tags to 0.36.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only viewing an event refreshed its geo_* meta, and maybe_refresh() skips events that already happened, so a past event that was password-protected or unpublished kept geo_public 1 with the location it copied. REST already blanks that location for a protected event, but anything reading the meta directly and following geo_public treated it as public. Geo_Sync::on_event_saved() runs on wp_after_insert_post and writes only geo_public, from the same rule maybe_refresh() uses, now shared as is_event_public(): the event, and its venue if it has one, must be published without a password. A past event keeps its location. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e672421)
Picked after the 0.36.1 bump, so its six tags were still TBD. Reran npm run version:bump -- --version=0.36.1, which changed only those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2520) The RSVP and RSVP Form blocks checked only Event::is_viewable(), which ignores the post password. In the event's content the password form hides them, but in a block template they render outside it, so a protected event showed its RSVP button and form. Add the post_password_required() check the Event Date and Add to Calendar blocks make. The RSVP form's REST route also accepted a response for a protected event without its password. Without JavaScript the form posts to wp-comments-post.php, which refuses a password-protected post, but process_rsvp() inserts the comment directly and skips that check. The route now refuses it with a 403 until the password is entered. The account RSVP route already requires it through Event::can_read_rsvps(). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e692104)
Picked after the 0.36.1 bump, so its three test tags were still TBD. Reran npm run version:bump -- --version=0.36.1, which changed only those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ec186e5 ("Cleanup switch statement", 0.34.0) moved adjust_event_sql()'s default arm from ordering by datetime_start_gmt to leaving the order alone, so custom column sorts keep their own clause. get_events_list() never set an orderby, so since then it fell to that default and listed events in WordPress's publish-date order. With a limit it kept the events published first rather than the soonest. That affects the events calendar feed, the upcoming events ability, and get_upcoming_events() and get_past_events(). get_events_list() now asks for the datetime order explicitly. This also makes Test_Abilities::test_get_upcoming_events_orders_chronologically deterministic: its two events are published in the same second, so it passed or failed on how the database broke the tie. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 42ec2f2)
Picked after the 0.36.1 bump, so its two test tags were still TBD. Reran npm run version:bump -- --version=0.36.1, which changed only those. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…est (#2524) test_handle_rsvp_form_submission_password_protected_event (#2520) checked that a refused submission saved no RSVP with get_comments(), but comment queries leave RSVPs out by default (gatherpress_rsvp_comment_query_exclusion), so the count was 0 whether or not one was saved. The test still caught the bug through its 403 check, but that assertion proved nothing. count_rsvp_comments() turns the exclusion off for its own query, and the test now also expects a count of 1 after the accepted submission, which shows the count can see an unapproved RSVP. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 51890f0)
Picked after the 0.36.1 bump, so its test helper's tag was still TBD. Reran npm run version:bump -- --version=0.36.1, which changed only that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preview changes with PlaygroundCustomize PR PlaygroundTo customize the Playground preview for this specific PR, create a file at:
The override is merged into the generated blueprint and can be used to:
Example: {
"$schema": "https://gatherpress.org/playground-preview/pr-override-schema.json",
"landingPage": "/wp-admin/edit.php?post_type=gatherpress_venue",
"siteOptions": {
"show_on_front": "page"
},
"features": {
"networking": true
},
"prependSteps": [
{
"step": "installPlugin",
"pluginData": {
"resource": "wordpress.org/plugins",
"slug": "simple-location"
},
"options": {
"activate": true
}
}
],
"appendSteps": [
{
"step": "runPHP",
"code": ""
}
]
}Go to Playground PR preview customization for more details. You can preview the recent changes for PR#2446 with the following PHP versions: PHP Version 8.4PHP Version 8.2PHP Version 8.1Download Made with 💙 from GatherPress & a little bit of WordPress Playground. Changes will not persist between sessions. |
The 0.36.1 patch release. Fixes are born on
developand cherry-picked here (git cherry-pick -x), per the patch flow indocs/contributor/release-process.md.Merge with a merge commit, never squash.
In this release
Twenty-six fixes, each cherry-picked from its
developsquash commit with its changelog entry file. All but #2472, #2481, #2500, #2501, #2520 and #2521 applied cleanly, and apart from #2497 and #2521 (below),git range-diffshows each is byte-identical to thedevelopcommit apart from the(cherry picked from …)line. #2472 conflicted becausedevelopnames the event edit capabilityEvent::edit_capability()(#2417), while 0.36 still hasEvent::EDIT_CAPABILITY. It was resolved to the fix's own lines with the 0.36 name, plus 0.36's wording on a neighboring test docblock, and the range-diff shows only those two differences. #2481 conflicted becausedevelop'sQuery::get_events_list()takes an args array, while 0.36 still takes positional parameters. On this branchhas_passwordis an optional fifth parameter, the feed passesfalsepositionally and the test calls it that way, and the REST check usesEvent::EDIT_CAPABILITYwheredevelophasCapability::EDIT_POST(both areedit_post). Those are the only differences from thedevelopcommit. #2497 applied cleanly but calleddevelop's args-arrayget_events_list(), which would throw on 0.36, so its one call was changed to the positional form #2481's feed call uses here ('upcoming', $count, array(), array(), false). The range-diff shows only that line. #2500 conflicted inEvent\Meta, where it replaces an inline password check with its newEvent::is_hidden_by_password(); that check usedEvent::EDIT_CAPABILITYhere andCapability::EDIT_POSTondevelop. It was resolved to #2500's line, and the new helper usesself::EDIT_CAPABILITYwheredevelophasself::edit_capability()(all three areedit_post). Those are the only differences from thedevelopcommit. #2501 conflicted onHas_Capabilities, a trait 0.36 doesn't have: ondevelop, #2501 putsis_hidden_by_password()in that trait, whichVenueandEventshare. HereEventkeeps its own from #2500, andVenuegets the same method (edit_post, as the trait'sedit_capability()is ondevelop), tested inVenue\Test_Venuein place of the trait's test.Venue\SetupusesEvent::EDIT_CAPABILITYwheredevelophasEvent::edit_capability(). Those are the only differences from thedevelopcommit. #2520 conflicted only on a neighboring test inTest_Rest_Apithat 0.36 still has, and both are kept. #2521 conflicted because 0.36 namesget_events_list()'s query args$argsrather than$query_args, and its test calls the positionalget_events_list( 'upcoming', 2 )wheredeveloppasses an args array; the range-diff shows only those four calls.The branch also carries five non-fix commits, apart from the version bump below:
58a9f24f5: the credits bot's Credit new contributors #2484 (camaleaunincredits/unreleased.json), cherry-picked from7e0faecc8so his credit lands in 0.36.1. See the credits step below.7cdb7c34d: Wait for the starting modal to close in the RSVP focus tests #2507, cherry-picked from02ceb80ce. A test-only fix for a race in two of fix: keep keyboard focus on screen after an RSVP change #2447's RSVP focus tests, which failed on CI for Keep an Event Query Loop unchanged when the editor opens #2496. It has no changelog entry. Its test file passes on this branch (11 tests).5449e2559: addshifarrertocredits/unreleased.json, for the docs update in docs: update settings names, RSVP mode values and requirements #2506, which is milestoned for 0.36.1. Committed here directly, so the credit would land in 0.36.1.d500c59b3takes it back out (see the version bump below).80905041d: docs: update settings names, RSVP mode values and requirements #2506, hifarrer's docs update for the renamed settings, RSVP Mode values and the WordPress 7.0 minimum, cherry-picked frome473fa224with itsdocs-settings-and-requirementschangelog entry. It conflicted on one row of the developer settings table: this branch keeps its note thatcapacitywas calledmax_attendance_limitbefore 0.36.0 and still answers until 0.37.0, whichdevelopdropped for 0.37.0. The range-diff shows only that context line.00e010805: Count RSVPs past the comment-query exclusion in the RSVP form test #2524, cherry-picked from51890f05d. A test-only fix for Keep a protected event's RSVP and RSVP Form behind its password #2520's RSVP form test: comment queries leave RSVPs out by default (gatherpress_rsvp_comment_query_exclusion), so its "no RSVP was saved" count was always 0. It now counts past the exclusion, and also expects 1 after the accepted submission. It has no changelog entry.Test_Rest_Apipasses on this branch (96 tests).developapply_block_hooks_to_content()when a new venue is seeded from the venue template.8f681a00767e3b2358327322eb18d3094e89cba6ca7f0e77cd577de9611c0a5b34462a37143c9594b38b681b99befea6e00bd802fc6959f02ec2b76c50e6a4hash_equals()instead of===. Hardening only, no behavior change.91136847db347ef567Cache-Control: private.00ae9a6cb569815632wp_sprintf_l()), and the Venues column's screen reader text.ce87a733d2c0ac5174edit_others_posts(Editors and Administrators). Anyone who can edit an event can still email its own attendees, and the email dialog hides All Members from users who can't use it.73174e798c9b1f72372b270f265aca935e41ff256014f38daf1534<time>tag when the date format starts with a number, such asd/m/Y.7ae83d764190dd4866/outlook/with junkpageandfeedparameters, or an unknowngatherpress_calendarvalue, no longer cause a fatal error.bd86299f1bcbf39f036005f9df57f34301670dccf9e8382d83471egatherpress/get-upcoming-events, over REST and MCP) leaves out password-protected events, so it no longer returns their start, end and timezone.dfbda4423d003170a9c2469158474ec27120state.postsin its own store, so its list no longer fails with a TypeError when its view script runs before the other RSVP blocks have loaded, as seen on an events archive in iOS Safari.10ac3118a11cc6e51edc0fb9e11c98ea91e320ab4c325756370c97495a045cad97febccfed9ca8babe672421d6wp-comments-post.phpalready did. Found while testing this release with the blocks in a template.fbaaf573ae69210413get_events_list()(the events calendar feed, the upcoming events ability,get_upcoming_events()andget_past_events()) are ordered by event date again, not by publish date as they had been since 0.34.0, so a limit keeps the soonest events. Found through a flaky test on #2520.83fa7ee9e42ec2f286.github/changelog/on this branch holds the twenty-six matching entries:2443-private-block-hooks-call,2428-rsvp-focus-after-status-change,2462-rsvp-dialog-names,2448-rsvp-form-schema-id,2449-address-suggestion-active-option,2466-date-button-names,login-modal-logged-in,rsvp-token-compare,calendar-feed-cache-logged-in,2477-untranslatable-strings,event-email-all-members,calendar-password-protected-events,rsvp-blocks-post-password,2476-post-date-leading-digit,2489-calendar-endpoint-unknown-slug,2495-event-query-loop-on-open,feed-password-protected-events,abilities-upcoming-events-password,email-all-members-help-text,2505-rsvp-template-state-posts,blocks-password-protected-event-details,protected-event-venueprotected-venue-details,protected-event-geo-public,rsvp-password-protected-eventsandevents-list-date-order, plus #2506'sdocs-settings-and-requirements.Verification on this branch
class-modal.phpand its tests) and Compare RSVP tokens with hash_equals() #2475 (one line inRsvp\Token::is_valid()) were added after that run. All three passed CI ondevelopand were not rerun here.SetupandCachetests pass on this branch (82 tests).developand was not rerun here.Rest_ApiandSetuptests pass on this branch (156 tests) after the conflict resolution.MetaandQuerytests and the calendarSetupandPost_Type_Singletests pass on this branch (147 tests) after the conflict resolution, and PHPCS and PHPStan are clean.develop, so they were not rerun here.Feedtests pass on this branch (36 tests).Abilitiestests pass on this branch (29 tests) after the adaptation, and PHPCS and PHPStan are clean.developline for line. On this branch, the RSVP Template view test passes (2), as do the Event Date and Add to Calendar block tests (18) and, in the multisite suite, the Online Event and Venue block tests (72).developline for line. With its tags resolved (below), the full single-site suite passes on this branch (2542), as does multisite (441) apart fromtest_on_site_create_multisite, which fails only when run from a copy of the plugin outsidewp-content/plugins.Test_Rest_Api, where 0.36 still hastest_handle_email_send_action()next to the new test; both are kept, andgit range-diffshows the pick matchesdevelopline for line. With its tags resolved (below), the full single-site suite passes on this branch (2545), as does multisite (441) apart fromtest_on_site_create_multisite. On a local Twenty Twenty-Five site with the GatherPress blocks in a per-event template, the protected event hides its RSVP button, date, venue and RSVP list until the password is entered.Querylist tests pass on this branch (12), and the new one fails without the fix. The full single-site suite passes (2546), as does multisite (441) apart fromtest_on_site_create_multisite. On the local site, the events calendar feed now lists Oct 25, Nov 14, Nov 28 and Dec 2 in that order, where it led with the Dec 2 event, published first.Version bump
d500c59b3takeshifarrerback out ofcredits/unreleased.json. Their GitHub username has no WordPress.org profile (404), the bump fails on a username it can't resolve, and they haven't shared their WordPress.org username on docs: update settings names, RSVP mode values and requirements #2506, so they can be credited in a later release if they do. The credits bot never queuedhifarrerondevelop, since props-bot's line for docs: update settings names, RSVP mode values and requirements #2506 named onlymauteri, so there's nothing to remove there.c4030f2f9isnpm run version:bump -- --version=0.36.1, withcredits/0.36.1.jsoncopied from0.36.0.json, plus the lockfile refresh.camaleaun, the author of Keep the leading digit of the event date in the Post Date block #2478 and Make strings shown in the editor and admin translatable #2479 (from Credit new contributors #2484, picked as58a9f24f5), into the 0.36.1 credits and emptiesunreleased.json. Move camaleaun's pending credit to 0.36.1 #2487 (merged) removed camaleaun's pending credit fromdevelop, so it isn't queued again for 0.37.0. Every other PR above is bymauteri,mattcowan(matthewneilcowanin the credits, Keep an Event Query Loop unchanged when the editor opens #2496 included),faisalahammadorraicem(Skip calendar endpoints when the requested slug is not theirs #2489), all already in 0.36.0's credits.@since TBDtags to0.36.1. Each is either the introducing tag of a symbol that isn't onmain(103) or a change note on a method that is (8), so none dates an older symbol to 0.36.1.gatherpress.php,package.jsonand its lockfile,readme.txt'sStable tag:, theREADME.mdbadge) and the regeneratedincludes/data/credits.php. Regenerating also picked up a display name change on WordPress.org: Mariusz Szatkowski is now "WPPoland (Mariusz Szatkowski)".SECURITY.mdwas already on 0.36.x.836ea8f75resolves Recheck an event's geo_public on save, past events included #2519's six@since TBDtags, since it was picked after the bump. Rerunningnpm run version:bump -- --version=0.36.1changed only those, both on symbols Recheck an event's geo_public on save, past events included #2519 introduced;credits.phpregenerated unchanged, and Alpha's header was already 0.36.1.78a5711e9resolves Keep a protected event's RSVP and RSVP Form behind its password #2520's three@since TBDtags the same way; the rerun changed only those.806ce00f7resolves List events by their date in get_events_list() #2521's two@since TBDtags the same way; the rerun changed only those.aee7bf1e9resolves Count RSVPs past the comment-query exclusion in the RSVP form test #2524's one@since TBDtag the same way; the rerun changed only that.version-0.36.1into Alpha'smain) holds six Alpha fixes, cherry-picked from Alpha'sdevelop: the 0.36.1 migration for password-protected venues (gatherpress-alpha#108), the change that makes it keep the event locations it clears, so Keep a password-protected venue's details and its link to events private #2501 can give them back when a venue is public again (gatherpress-alpha#110), the network RSVP settings migration (gatherpress-alpha#107), which gatherpress-alpha#111 moves from 0.37.0 to 0.36.1, marking password-protected events' locations not public (gatherpress-alpha#112, the existing-data side of Recheck an event's geo_public on save, past events included #2519), and labeling the Alpha screen's sections by exact version (gatherpress-alpha#113). The same bump wrote itsVersion: 0.36.1header (541679e), so the two match. Its changelog entries mean Alpha's release rollup has entries even without a separatesync-0-36-1one.Still to come
0.36.1onmain.🤖 Generated with Claude Code