Skip to content

Groups: store registration questions in GatherPress's form schema meta - #2234

Merged
bor0 merged 1 commit into
productionfrom
groups/rsvp-questions-gatherpress-schema
Oct 22, 2026
Merged

bor0 merged 1 commit into
productionfrom
groups/rsvp-questions-gatherpress-schema

Conversation

@bor0

@bor0 bor0 commented Oct 22, 2026

Copy link
Copy Markdown
Member

Fixes #1871.

GatherPress 0.36.0 shipped the fixes this was blocked on, so registration questions now use GatherPress's own storage:

  • Questions: a wporg_groups schema in gatherpress_rsvp_form_schemas (text fields, same shape GatherPress builds from Form Field blocks).
  • Answers: gatherpress_custom_{question id} comment meta on the RSVP.
  • A gatherpress_rsvp_form_schemas filter keeps our schema when GatherPress rebuilds it from blocks on save. Block-derived schemas are left alone.

Our RSVP route and the guard on /gatherpress/v1/event/rsvp stay: GatherPress only validates custom fields on its open-RSVP route, which groups switch off. The function API in rsvp-questions.php is unchanged, so REST, render and JS don't change.

Migration

Existing questions and answers have to be moved after deploy. Run this from the sandbox (dry run first). It walks every site, is idempotent, and never overwrites data already under the new keys. I'll post the output here.

wp eval-file migrate-rsvp-questions.php dry-run
wp eval-file migrate-rsvp-questions.php
migrate-rsvp-questions.php
<?php
/**
 * One-off: move Groups registration questions and answers from the legacy
 * `wporg_groups_rsvp_questions` / `wporg_groups_rsvp_answers` meta onto
 * GatherPress's `gatherpress_rsvp_form_schemas` / `gatherpress_custom_*`.
 *
 * Usage: wp eval-file migrate-rsvp-questions.php [dry-run]
 *
 * Walks every site on the network. Safe to re-run: legacy meta is deleted once
 * moved, and anything already stored under the new keys is never overwritten.
 */

$dry_run = in_array( 'dry-run', $args ?? array(), true );
$totals  = array( 'events' => 0, 'answers' => 0 );

foreach ( get_sites( array( 'number' => 0, 'fields' => 'ids' ) ) as $site_id ) {
	switch_to_blog( $site_id );
	global $wpdb;

	$events = $wpdb->get_col( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'wporg_groups_rsvp_questions'" );
	$rsvps  = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key = 'wporg_groups_rsvp_answers'" );

	if ( ! $events && ! $rsvps ) {
		restore_current_blog();
		continue;
	}

	foreach ( $events as $event_id ) {
		$event_id  = (int) $event_id;
		$questions = get_post_meta( $event_id, 'wporg_groups_rsvp_questions', true );
		$schemas   = get_post_meta( $event_id, 'gatherpress_rsvp_form_schemas', true );
		$schemas   = is_array( $schemas ) ? $schemas : array();

		if ( ! $dry_run ) {
			if ( is_array( $questions ) && $questions && ! isset( $schemas['wporg_groups'] ) ) {
				$fields = array();

				foreach ( $questions as $question ) {
					$fields[ $question['id'] ] = array(
						'name'        => $question['id'],
						'type'        => 'text',
						'required'    => (bool) $question['required'],
						'label'       => $question['label'],
						'placeholder' => '',
					);
				}

				$schemas['wporg_groups'] = array(
					'fields' => $fields,
					'hash'   => wp_hash( (string) wp_json_encode( $fields ) ),
				);

				update_post_meta( $event_id, 'gatherpress_rsvp_form_schemas', $schemas );
			}

			delete_post_meta( $event_id, 'wporg_groups_rsvp_questions' );
		}

		++$totals['events'];
	}

	foreach ( $rsvps as $comment_id ) {
		$comment_id = (int) $comment_id;
		$answers    = get_comment_meta( $comment_id, 'wporg_groups_rsvp_answers', true );

		if ( ! $dry_run ) {
			foreach ( is_array( $answers ) ? $answers : array() as $id => $answer ) {
				$key = 'gatherpress_custom_' . $id;

				if ( '' === get_comment_meta( $comment_id, $key, true ) ) {
					update_comment_meta( $comment_id, $key, $answer );
				}
			}

			delete_comment_meta( $comment_id, 'wporg_groups_rsvp_answers' );
		}

		++$totals['answers'];
	}

	WP_CLI::log( sprintf( '%s: %d events, %d RSVPs with answers', home_url( '/' ), count( $events ), count( $rsvps ) ) );
	restore_current_blog();
}

WP_CLI::success( sprintf( '%s%d events, %d RSVPs with answers.', $dry_run ? '[dry run] ' : '', $totals['events'], $totals['answers'] ) );

Testing

  1. phpunit --filter Test_Groups_RSVP_Questions. The new tests cover the schema shape, a block-editor save keeping the schema (alongside a block's form_0), and per-field answer meta.
  2. Locally: seed legacy meta on an event, run the migration (dry, real, again: second run is a no-op), then:
    • RSVP without the required answer: 400 from both our route and GatherPress's.
    • RSVP with answers: stored as gatherpress_custom_*, "0" kept, organizer sees them, attendee sees them prefilled.
    • Save the event via wp/v2: schema survives.
    • Cancel: answers removed.

🤖 Generated with Claude Code

Questions now live in `gatherpress_rsvp_form_schemas` under a `wporg_groups`
schema, and answers in `gatherpress_custom_{id}` comment meta. A
`gatherpress_rsvp_form_schemas` filter keeps the schema when GatherPress
rebuilds it from blocks on save.

Fixes #1871.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bor0
bor0 marked this pull request as ready for review October 22, 2026 11:14
@bor0
bor0 merged commit beeedbb into production Oct 22, 2026
6 checks passed
@bor0
bor0 deleted the groups/rsvp-questions-gatherpress-schema branch October 22, 2026 11:33
@bor0

bor0 commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

Migration ran on production after deploy.

$ wp eval-file migrate-rsvp-questions.php dry-run
https://events.wordpress.org/group/internal-testing-group/: 7 events, 33 RSVPs with answers
https://events.wordpress.org/group/gatherpress/: 1 events, 16 RSVPs with answers
Success: [dry run] 8 events, 49 RSVPs with answers.

$ wp eval-file migrate-rsvp-questions.php
https://events.wordpress.org/group/internal-testing-group/: 7 events, 33 RSVPs with answers
https://events.wordpress.org/group/gatherpress/: 1 events, 16 RSVPs with answers
Success: 8 events, 49 RSVPs with answers.

Read back afterwards: internal-testing-group has 7 schemas and 33 answered RSVPs, gatherpress has 1 and 16. Questions show in the RSVP dialog on production.

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.

Migrate custom registration questions onto gatherpress_rsvp_form_schemas

1 participant