Campus Connect: CC status dropdown + MCP vetting server (issue #1714 points 1 & 3)#1720
Closed
dorsvenabili wants to merge 6 commits into
Closed
Campus Connect: CC status dropdown + MCP vetting server (issue #1714 points 1 & 3)#1720dorsvenabili wants to merge 6 commits into
dorsvenabili wants to merge 6 commits into
Conversation
…point 3) Introduces two authenticated REST endpoints under `wordcamp/v1/vetting/`: - `GET /queue` — returns Campus Connect posts in `wcpt-needs-vetting` status, with organizer meta fields the vetting agent needs to evaluate each application. - `POST /process` — accepts a post ID and vetting note; writes the note as a private `_note` meta entry and advances the post to `wcpt-needs-action`. Both endpoints require the `wordcamp_wrangle_wordcamps` capability (Application Passwords is the intended auth mechanism for the automated agent). Also adds `wcpt_add_private_note()` to `wcpt-functions.php` as a reusable helper for programmatically writing private notes outside the admin UI (used here and available for future use in Points 4/5). Status-change log entries are written explicitly by the controller since `WordCamp_Admin` (which normally hooks `transition_post_status`) is not loaded outside the admin context. CC-specific labels from `WordCamp_Loader::get_campus_connect_statuses()` are used so log entries read "Needs Vetting -> Needs Action" rather than raw slugs. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an authenticated REST API surface for an automated Campus Connect “vetting agent” to read the vetting queue, write a private note, and advance an application’s status, plus a small helper for writing private notes programmatically.
Changes:
- Registers new REST routes under
wordcamp/v1/vetting(queue + process) and loads the controller. - Implements
WordCamp_REST_Vetting_ControllerwithGET /queueandPOST /process. - Adds
wcpt_add_private_note()helper to write_noteprivate log entries outside the admin UI.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-loader.php |
Loads the new REST controller and registers routes on rest_api_init. |
public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wp-rest-vetting-controller.php |
Implements the vetting queue/process endpoints and status-change logging. |
public_html/wp-content/plugins/wcpt/wcpt-functions.php |
Adds a helper to create _note private log entries programmatically. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+73
to
+78
| public function check_permission() { | ||
| if ( ! is_user_logged_in() ) { | ||
| return new WP_Error( | ||
| 'rest_not_logged_in', | ||
| __( 'You must be logged in to use the vetting API.', 'wordcamporg' ), | ||
| array( 'status' => 401 ) |
Comment on lines
+235
to
+240
| $result = wp_update_post( | ||
| array( | ||
| 'ID' => $post_id, | ||
| 'post_status' => 'wcpt-needs-action', | ||
| ), | ||
| true |
Comment on lines
+272
to
+276
| $locale_switched = switch_to_locale( 'en_US' ); | ||
|
|
||
| $cc_statuses = WordCamp_Loader::get_campus_connect_statuses(); | ||
| $old_label = $cc_statuses[ $old_status ] ?? $old_status; | ||
| $new_label = $cc_statuses[ $new_status ] ?? $new_status; |
Comment on lines
+104
to
+110
| 'post_type' => WCPT_POST_TYPE_ID, | ||
| 'post_status' => 'wcpt-needs-vetting', | ||
| 'meta_key' => 'event_subtype', | ||
| 'meta_value' => 'campusconnect', | ||
| 'posts_per_page' => 100, | ||
| 'orderby' => 'date', | ||
| 'order' => 'ASC', |
Comment on lines
+229
to
+235
| // 1. Attach the vetting note. | ||
| wcpt_add_private_note( $post_id, $note, get_current_user_id() ); | ||
|
|
||
| // 2. Advance the status. | ||
| $old_status = $post->post_status; | ||
|
|
||
| $result = wp_update_post( |
Comment on lines
+135
to
+139
| public function prepare_item_for_response( $post, $request ) { | ||
| $meta_fields = array( | ||
| 'Organizer Name', | ||
| 'WordPress.org Username', | ||
| 'Email Address', |
- Convert indented two-line inline comment to block comment in process_application() (Squiz.Commenting.InlineComment.SpacingBefore) - Remove trailing spaces before array closer in register_vetting_rest_routes hook registration (WordPress.Arrays.ArrayDeclaration) Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add WP_REST_Request $request parameter to check_permission() to match the WP_REST_Controller interface signature - Add pagination to GET /vetting/queue: page/per_page args (default 1/20, max 100), X-WP-Total and X-WP-TotalPages response headers via WP_Query - Validate that the vetting note is non-empty before any writes (400) - Write the note before advancing the status, and check the return value of wcpt_add_private_note(); abort with 500 if it fails - Re-fetch the post after writing the note to detect concurrent status changes (optimistic concurrency); return 409 if status shifted - Rename prepare_item_for_response() to prepare_queue_item() to avoid accidentally overriding the WP_REST_Controller base-class method Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Comment on lines
+337
to
+341
| $locale_switched = switch_to_locale( 'en_US' ); | ||
|
|
||
| $cc_statuses = WordCamp_Loader::get_campus_connect_statuses(); | ||
| $old_label = $cc_statuses[ $old_status ] ?? $old_status; | ||
| $new_label = $cc_statuses[ $new_status ] ?? $new_status; |
Comment on lines
+273
to
+293
| // 1. Attach the vetting note first so it is always paired with the transition. | ||
| $note_id = wcpt_add_private_note( $post_id, $note, get_current_user_id() ); | ||
|
|
||
| if ( ! $note_id ) { | ||
| return new WP_Error( | ||
| 'wcpt_vetting_note_failed', | ||
| __( 'Could not save the vetting note. Status was not changed.', 'wordcamporg' ), | ||
| array( 'status' => 500 ) | ||
| ); | ||
| } | ||
|
|
||
| // 2. Reload the post to guard against a concurrent status change (optimistic concurrency check). | ||
| $post = get_post( $post_id ); | ||
|
|
||
| if ( ! $post || 'wcpt-needs-vetting' !== $post->post_status ) { | ||
| return new WP_Error( | ||
| 'wcpt_vetting_concurrent_update', | ||
| __( 'The application status changed while this request was being processed. Please reload and try again.', 'wordcamporg' ), | ||
| array( 'status' => 409 ) | ||
| ); | ||
| } |
… (issue #1714) - Delete class-wp-rest-vetting-controller.php integration; keep file for PR #1720 reference - Add class-wcpt-vetting-abilities.php: registers wcpt/get-vetting-queue and wcpt/process-application as WordPress Abilities (WP 6.9+ Abilities API) - Expose both abilities via a dedicated MCP server at /wp-json/mcp/wcpt-vetting using the wordpress/mcp-adapter Composer package (v0.5.0) - Add wcpt/vendor/ with plugin-scoped Composer install of mcp-adapter - Update wordcamp-loader.php: bootstrap McpAdapter directly from vendor autoload, register the wcpt ability category, call WCPT_Vetting_Abilities::init() - Add WordCamp_Loader::get_campus_connect_statuses() for log label lookups Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…p (issue #1714 point 1) - Expand get_campus_connect_statuses() from 7 to 10 statuses: add Needs Orientation (wcpt-needs-orientati), WordCamp Scheduled, and WordCamp Closed - Register wcpt-needs-action and wcpt-needs-more-info in get_post_statuses() so Event_Loader::register_post_statuses() calls register_post_status() for them - Add WordCamp_Loader::get_campus_connect_status_transitions() with the definitive transition map; Declined, Cancelled and On Hold can transition to any CC status - WordCamp_Admin::get_post_statuses() returns CC statuses for CC posts, strips CC-exclusive slugs for all other subtypes - WordCamp_Admin::get_valid_status_transitions() delegates to the CC map for CC posts - Add WordCamp_Admin::is_campus_connect_post() (reads event_subtype meta) - Add WordCamp_Admin::get_status_label() override for CC-specific log labels - Add Event_Admin::get_status_label() base method; log_status_changes() calls it - metabox-status.php: use labels from get_post_statuses() not the global WP status object; inject current status as disabled option when absent from the list - enforce_post_status() blocks CC-exclusive statuses on non-CC posts - Scheduled validation fallback reverts CC posts to wcpt-approved-pre-pl Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This was referenced May 21, 2026
- class-event-admin.php: add missing docblock for log_status_changes() - class-wcpt-vetting-abilities.php: - Inline comments now end with full-stops (resources, prompts) - @throws tag corrected to \RuntimeException (fully-qualified) - All __() calls inside throw statements replaced with esc_html__() - Dynamic values in exception messages wrapped with esc_html() Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Collaborator
Author
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements points 1 and 3 of #1714.
Point 1 — CC status dropdown (10 statuses, scoped transitions, CC-specific labels)
Restricts the WordCamp Status dropdown, transition map, and status-change log to Campus Connect–specific values for entries where
event_subtypeequalscampusconnect. Non-CC event subtypes (WordCamp, DoAction, Student Club, Other Event) are unaffected.wcpt-wordcamp/wordcamp-loader.phpwcpt-needs-actionandwcpt-needs-more-infotoget_post_statuses(). Both are registered globally so WordPress is aware of them, but excluded from non-CC dropdowns via theWordCamp_Adminoverride.get_campus_connect_statuses(): returns the ten CC-specific statuses with CC-specific labels (wcpt-approved-pre-pl→ "Approved For Pre-Planning";wcpt-more-info-reque→ "On Hold").get_campus_connect_status_transitions(): dedicated transition map for CC entries (see table below).wcpt-wordcamp/wordcamp-admin.phpget_post_statuses()override: CC posts → 10 CC statuses; non-CC posts → original list withwcpt-needs-actionandwcpt-needs-more-infostripped.get_valid_status_transitions()override: CC posts → CC transition map; non-CC posts → original map, unchanged.is_campus_connect_post(): readsget_post_meta( $id, 'event_subtype' )to determine whether the post being edited is a CC entry.get_status_label()override: returns CC-specific labels for log entries on CC posts; falls back to parent for everything else.require_complete_meta_to_publish_wordcamp(): when scheduling validation fails on a CC post, reverts towcpt-approved-pre-plinstead of the non-CCwcpt-needs-schedule.enforce_post_status(): blocks CC-exclusive statuses (wcpt-needs-action,wcpt-needs-more-info) from being saved on non-CC posts.wcpt-event/class-event-admin.phplog_status_changes(): replaced directget_post_status_object()->labelcalls with a new overridableget_status_label( $status, $post )method.get_status_label()base implementation wrapsget_post_status_object(); subclasses can override for event-subtype–specific labels. No behaviour change for non-CC event types.views/wordcamp/metabox-status.php$post_status_label(from theget_post_statuses()foreach) instead of$status->label(globally registered), so subtype-specific labels are respected at render time.get_post_statuses()(e.g. a mid-transition state not in the CC set), so saving never silently mutates the status.Campus Connect transition map
Point 3 — MCP vetting server (
wcpt/get-vetting-queue+wcpt/process-application)Adds the server-side integration surface for the automated vetting agent using the WordPress Abilities API (MCP HTTP transport, WP 6.9+).
wcpt-wordcamp/class-wcpt-vetting-abilities.php(new file)Registers two WordPress Abilities exposed at
/wp-json/mcp/wcpt-vetting:wcpt/get-vetting-queueReturns all Campus Connect posts (
event_subtype = campusconnect) inwcpt-needs-vettingstatus, ordered oldest-first. Supportspageandper_pageparameters for pagination. Each item includesid,title,status,date,content,edit_url, and ametaobject with the organizer fields the agent needs to evaluate (Organizer Name,WordPress.org Username,Email Address,Location,Number of Anticipated Attendees).wcpt/process-applicationAccepts
post_id(int, required) andnote(string, required). Validates the post is a Campus Connect entry inwcpt-needs-vetting, then:_noteprivate meta entry (viawcpt_add_private_note()).wcpt-needs-actionviawp_update_post()._status_changelog entry with CC-specific labels ("Needs Vetting → Needs Action").Both abilities require the
wordcamp_wrangle_wordcampscapability. Auth via WordPress Application Passwords on a dedicated service account.wcpt-functions.phpNew
wcpt_add_private_note( $post_id, $message, $user_id = 0 )helper: low-level counterpart toEvent_Admin::validate_and_add_note()that works outside the admin UI (MCP, cron). Available for points 4/5 (email + admin notice) as well.wcpt-wordcamp/wordcamp-loader.phpRequires the new abilities class and hooks
register_vetting_abilitiestoinit.Notes for reviewers
WordCamp_Adminis only loaded whenis_admin()is true, so itstransition_post_statushook for status-change logging does not fire during MCP requests. The ability handler writes the log entry explicitly usingWordCamp_Loader::get_campus_connect_statuses()for correct CC labels.wp_register_ability(),wp_register_ability_category()).Files changed
wcpt-wordcamp/wordcamp-loader.phpwcpt-wordcamp/wordcamp-admin.phpis_campus_connect_post(); CC label override; enforce/schedule CC fallbackswcpt-event/class-event-admin.phpget_status_label()+ use inlog_status_changes()views/wordcamp/metabox-status.phpwcpt-wordcamp/class-wcpt-vetting-abilities.phpwcpt-functions.phpwcpt_add_private_note()helperTest plan
CC status dropdown (Point 1)
event_subtype = campusconnect). Confirm the status dropdown shows exactly the 10 CC statuses and no others.wcpt-approved-pre-plin the CC dropdown. Confirm non-CC posts still show "Approved for Pre-Planning Pending Agreement" for the same slug.wcpt-more-info-requein the CC dropdown. Confirm non-CC posts show the original label.wcpt-needs-actionandwcpt-needs-more-infoare absent, and transition enforcement is unchanged.post_status = wcpt-needs-actionvia direct POST. Confirmenforce_post_status()rejects it and the status is unchanged.MCP vetting server (Point 3)
GET /wp-json/mcp/wcpt-vetting(initialize) unauthenticated → 401wcpt/get-vetting-queuewith non-wrangler user → error / 403wcpt/get-vetting-queueas wrangler, no CC posts in needs-vetting → emptyitemsarraywcpt/get-vetting-queuewith a CC post inwcpt-needs-vetting→ item appears with correct fieldswcpt/get-vetting-queuewith a non-CC post inwcpt-needs-vetting→ does not appearwcpt/process-applicationwith unknownpost_id→ errorwcpt/process-applicationon a non-CC post → errorwcpt/process-applicationon a CC post not inwcpt-needs-vetting→ errorwcpt/process-applicationmissingnote→ errorwcpt/process-applicationon a valid CC post inwcpt-needs-vetting→ success; post status iswcpt-needs-action;_notemeta written;_status_changelog entry reads "Needs Vetting → Needs Action"wcpt/get-vetting-queueGenerated with Claude Code