Created
March 30, 2026 00:00
-
-
Save mizner/3cabb4ef2e920dd20bf95ac9cfe90c6b to your computer and use it in GitHub Desktop.
MCP Adapter Example Feb 2026
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
| <?php | |
| /** | |
| * Summit abilities for the WordPress Abilities API. | |
| * | |
| * @package Summit | |
| * @author Michael Mizner | |
| * @license GNU General Public License v3 | |
| * @link https://logocentric.net/ | |
| */ | |
| class Summit_Abilities { | |
| /** | |
| * Bootstrap hooks. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return void | |
| */ | |
| public static function init() { | |
| add_action( 'wp_abilities_api_categories_init', array( __CLASS__, 'register_categories' ) ); | |
| add_action( 'wp_abilities_api_init', array( __CLASS__, 'register_abilities' ) ); | |
| } | |
| /** | |
| * Register ability categories. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return void | |
| */ | |
| public static function register_categories() { | |
| wp_register_ability_category( | |
| 'summit-read', | |
| array( | |
| 'label' => __( 'Summit Read', 'summit' ), | |
| 'description' => __( 'Read-only abilities for Summit event data.', 'summit' ), | |
| ) | |
| ); | |
| wp_register_ability_category( | |
| 'summit-manage', | |
| array( | |
| 'label' => __( 'Summit Manage', 'summit' ), | |
| 'description' => __( 'Create, update, and delete Summit data.', 'summit' ), | |
| ) | |
| ); | |
| } | |
| /** | |
| * Register abilities. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return void | |
| */ | |
| public static function register_abilities() { | |
| wp_register_ability( | |
| 'summit/get-event-info', | |
| array( | |
| 'label' => __( 'Get Event Info', 'summit' ), | |
| 'description' => __( 'Retrieves global Summit event settings.', 'summit' ), | |
| 'category' => 'summit-read', | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'event_name' => array( 'type' => 'string' ), | |
| 'event_tagline' => array( 'type' => 'string' ), | |
| 'event_dates' => array( 'type' => 'string' ), | |
| 'venue_name' => array( 'type' => 'string' ), | |
| 'venue_address' => array( 'type' => 'string' ), | |
| 'venue_url' => array( 'type' => 'string' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'get_event_info' ), | |
| 'permission_callback' => '__return_true', | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'readonly' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/update-event-info', | |
| array( | |
| 'label' => __( 'Update Event Info', 'summit' ), | |
| 'description' => __( 'Updates global Summit event settings.', 'summit' ), | |
| 'category' => 'summit-manage', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'event_name' => array( 'type' => 'string' ), | |
| 'event_tagline' => array( 'type' => 'string' ), | |
| 'event_dates' => array( 'type' => 'string' ), | |
| 'venue_name' => array( 'type' => 'string' ), | |
| 'venue_address' => array( 'type' => 'string' ), | |
| 'venue_url' => array( 'type' => 'string' ), | |
| ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'success' => array( 'type' => 'boolean' ), | |
| 'updated' => array( 'type' => 'array' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'update_event_info' ), | |
| 'permission_callback' => array( __CLASS__, 'can_manage_options' ), | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'destructive' => false, 'idempotent' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/get-speakers', | |
| array( | |
| 'label' => __( 'Get Speakers', 'summit' ), | |
| 'description' => __( 'Retrieves Summit speakers with role and thumbnail.', 'summit' ), | |
| 'category' => 'summit-read', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'limit' => array( 'type' => 'integer', 'default' => 20 ), | |
| 'orderby' => array( 'type' => 'string', 'default' => 'menu_order' ), | |
| ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'array', | |
| 'items' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'id' => array( 'type' => 'integer' ), | |
| 'name' => array( 'type' => 'string' ), | |
| 'role' => array( 'type' => 'string' ), | |
| 'slug' => array( 'type' => 'string' ), | |
| 'url' => array( 'type' => 'string' ), | |
| 'thumbnail' => array( 'type' => 'string' ), | |
| ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'get_speakers' ), | |
| 'permission_callback' => '__return_true', | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'readonly' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/get-speaker', | |
| array( | |
| 'label' => __( 'Get Speaker', 'summit' ), | |
| 'description' => __( 'Retrieves a speaker with bio, social links, and role.', 'summit' ), | |
| 'category' => 'summit-read', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'id' => array( 'type' => 'integer' ), | |
| 'slug' => array( 'type' => 'string' ), | |
| ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'id' => array( 'type' => 'integer' ), | |
| 'name' => array( 'type' => 'string' ), | |
| 'role' => array( 'type' => 'string' ), | |
| 'bio' => array( 'type' => 'string' ), | |
| 'social_links' => array( 'type' => 'array' ), | |
| 'accordions' => array( 'type' => 'array' ), | |
| 'thumbnail' => array( 'type' => 'string' ), | |
| 'url' => array( 'type' => 'string' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'get_speaker' ), | |
| 'permission_callback' => '__return_true', | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'readonly' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/create-speaker', | |
| array( | |
| 'label' => __( 'Create Speaker', 'summit' ), | |
| 'description' => __( 'Creates a new Summit speaker post.', 'summit' ), | |
| 'category' => 'summit-manage', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'name' => array( 'type' => 'string' ), | |
| 'role' => array( 'type' => 'string' ), | |
| 'bio' => array( 'type' => 'string' ), | |
| 'social_links' => array( 'type' => 'array' ), | |
| 'accordions' => array( 'type' => 'array' ), | |
| ), | |
| 'required' => array( 'name' ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'success' => array( 'type' => 'boolean' ), | |
| 'id' => array( 'type' => 'integer' ), | |
| 'url' => array( 'type' => 'string' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'create_speaker' ), | |
| 'permission_callback' => array( __CLASS__, 'can_edit_posts' ), | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/update-speaker', | |
| array( | |
| 'label' => __( 'Update Speaker', 'summit' ), | |
| 'description' => __( 'Updates an existing Summit speaker.', 'summit' ), | |
| 'category' => 'summit-manage', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'id' => array( 'type' => 'integer' ), | |
| 'name' => array( 'type' => 'string' ), | |
| 'role' => array( 'type' => 'string' ), | |
| 'bio' => array( 'type' => 'string' ), | |
| 'social_links' => array( 'type' => 'array' ), | |
| 'accordions' => array( 'type' => 'array' ), | |
| ), | |
| 'required' => array( 'id' ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'success' => array( 'type' => 'boolean' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'update_speaker' ), | |
| 'permission_callback' => array( __CLASS__, 'can_edit_posts' ), | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/delete-speaker', | |
| array( | |
| 'label' => __( 'Delete Speaker', 'summit' ), | |
| 'description' => __( 'Deletes a Summit speaker.', 'summit' ), | |
| 'category' => 'summit-manage', | |
| 'input_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'id' => array( 'type' => 'integer' ), | |
| ), | |
| 'required' => array( 'id' ), | |
| 'additionalProperties' => false, | |
| ), | |
| 'output_schema' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'success' => array( 'type' => 'boolean' ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'delete_speaker' ), | |
| 'permission_callback' => array( __CLASS__, 'can_delete_posts' ), | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'destructive' => true ), | |
| ), | |
| ) | |
| ); | |
| wp_register_ability( | |
| 'summit/get-schedule', | |
| array( | |
| 'label' => __( 'Get Schedule', 'summit' ), | |
| 'description' => __( 'Parses the schedule template part and returns structured data.', 'summit' ), | |
| 'category' => 'summit-read', | |
| 'output_schema' => array( | |
| 'type' => 'array', | |
| 'items' => array( | |
| 'type' => 'object', | |
| 'properties' => array( | |
| 'day' => array( 'type' => 'string' ), | |
| 'date' => array( 'type' => 'string' ), | |
| 'entries' => array( 'type' => 'array' ), | |
| ), | |
| ), | |
| ), | |
| 'execute_callback' => array( __CLASS__, 'get_schedule' ), | |
| 'permission_callback' => '__return_true', | |
| 'meta' => array( | |
| 'mcp' => array( 'public' => true ), | |
| 'annotations' => array( 'readonly' => true ), | |
| ), | |
| ) | |
| ); | |
| } | |
| /** | |
| * Check manage options capability. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return bool | |
| */ | |
| public static function can_manage_options() { | |
| return current_user_can( 'manage_options' ); | |
| } | |
| /** | |
| * Check edit posts capability. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return bool | |
| */ | |
| public static function can_edit_posts() { | |
| return current_user_can( 'edit_posts' ); | |
| } | |
| /** | |
| * Check delete posts capability. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return bool | |
| */ | |
| public static function can_delete_posts() { | |
| return current_user_can( 'delete_posts' ); | |
| } | |
| /** | |
| * Get event info options. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return array | |
| */ | |
| public static function get_event_info() { | |
| return array( | |
| 'event_name' => get_option( 'summit_event_name', '' ), | |
| 'event_tagline' => get_option( 'summit_event_tagline', '' ), | |
| 'event_dates' => get_option( 'summit_event_dates', '' ), | |
| 'venue_name' => get_option( 'summit_venue_name', '' ), | |
| 'venue_address' => get_option( 'summit_venue_address', '' ), | |
| 'venue_url' => get_option( 'summit_venue_url', '' ), | |
| ); | |
| } | |
| /** | |
| * Update event info options. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Input values. | |
| * @return array | |
| */ | |
| public static function update_event_info( $input ) { | |
| $allowed = array( | |
| 'event_name' => 'summit_event_name', | |
| 'event_tagline' => 'summit_event_tagline', | |
| 'event_dates' => 'summit_event_dates', | |
| 'venue_name' => 'summit_venue_name', | |
| 'venue_address' => 'summit_venue_address', | |
| 'venue_url' => 'summit_venue_url', | |
| ); | |
| $updated = array(); | |
| foreach ( $allowed as $key => $option ) { | |
| if ( isset( $input[ $key ] ) ) { | |
| $updated[ $key ] = $input[ $key ]; | |
| update_option( $option, sanitize_text_field( $input[ $key ] ) ); | |
| } | |
| } | |
| return array( | |
| 'success' => true, | |
| 'updated' => array_keys( $updated ), | |
| ); | |
| } | |
| /** | |
| * Get speakers list. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array|null $input Input args. | |
| * @return array | |
| */ | |
| public static function get_speakers( $input = null ) { | |
| if ( ! is_array( $input ) ) { | |
| $input = array(); | |
| } | |
| $args = array( | |
| 'post_type' => 'summit_speaker', | |
| 'posts_per_page' => isset( $input['limit'] ) ? absint( $input['limit'] ) : 20, | |
| 'orderby' => isset( $input['orderby'] ) ? sanitize_key( $input['orderby'] ) : 'menu_order', | |
| 'order' => 'ASC', | |
| 'post_status' => 'publish', | |
| ); | |
| $posts = get_posts( $args ); | |
| $results = array(); | |
| foreach ( $posts as $post ) { | |
| $results[] = array( | |
| 'id' => $post->ID, | |
| 'name' => $post->post_title, | |
| 'role' => get_post_meta( $post->ID, '_summit_speaker_role', true ), | |
| 'slug' => $post->post_name, | |
| 'url' => get_permalink( $post->ID ), | |
| 'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'full' ), | |
| ); | |
| } | |
| return $results; | |
| } | |
| /** | |
| * Get a single speaker. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Input args. | |
| * @return array|WP_Error | |
| */ | |
| public static function get_speaker( $input ) { | |
| $post = null; | |
| if ( ! empty( $input['id'] ) ) { | |
| $post = get_post( absint( $input['id'] ) ); | |
| } elseif ( ! empty( $input['slug'] ) ) { | |
| $post = get_page_by_path( sanitize_title( $input['slug'] ), OBJECT, 'summit_speaker' ); | |
| } | |
| if ( ! $post instanceof WP_Post ) { | |
| return new WP_Error( 'summit_speaker_not_found', __( 'Speaker not found.', 'summit' ) ); | |
| } | |
| $data = self::parse_speaker_content( $post->post_content ); | |
| return array( | |
| 'id' => $post->ID, | |
| 'name' => $post->post_title, | |
| 'role' => get_post_meta( $post->ID, '_summit_speaker_role', true ), | |
| 'bio' => $data['bio'], | |
| 'social_links' => $data['social_links'], | |
| 'accordions' => $data['accordions'], | |
| 'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'full' ), | |
| 'url' => get_permalink( $post->ID ), | |
| ); | |
| } | |
| /** | |
| * Create a speaker post. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Input values. | |
| * @return array|WP_Error | |
| */ | |
| public static function create_speaker( $input ) { | |
| $content = self::build_speaker_content( $input ); | |
| $post_id = wp_insert_post( | |
| array( | |
| 'post_type' => 'summit_speaker', | |
| 'post_status' => 'publish', | |
| 'post_title' => isset( $input['name'] ) ? sanitize_text_field( $input['name'] ) : '', | |
| 'post_content' => $content, | |
| ) | |
| ); | |
| if ( is_wp_error( $post_id ) ) { | |
| return $post_id; | |
| } | |
| if ( ! empty( $input['role'] ) ) { | |
| update_post_meta( $post_id, '_summit_speaker_role', sanitize_text_field( $input['role'] ) ); | |
| } | |
| return array( | |
| 'success' => true, | |
| 'id' => $post_id, | |
| 'url' => get_permalink( $post_id ), | |
| ); | |
| } | |
| /** | |
| * Update a speaker post. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Input values. | |
| * @return array|WP_Error | |
| */ | |
| public static function update_speaker( $input ) { | |
| $post = get_post( absint( $input['id'] ) ); | |
| if ( ! $post instanceof WP_Post ) { | |
| return new WP_Error( 'summit_speaker_not_found', __( 'Speaker not found.', 'summit' ) ); | |
| } | |
| $update = array( | |
| 'ID' => $post->ID, | |
| ); | |
| if ( ! empty( $input['name'] ) ) { | |
| $update['post_title'] = sanitize_text_field( $input['name'] ); | |
| } | |
| if ( ! empty( $input['role'] ) ) { | |
| update_post_meta( $post->ID, '_summit_speaker_role', sanitize_text_field( $input['role'] ) ); | |
| } | |
| if ( isset( $input['bio'] ) || isset( $input['social_links'] ) || isset( $input['accordions'] ) ) { | |
| $current = self::parse_speaker_content( $post->post_content ); | |
| $merged = array( | |
| 'bio' => isset( $input['bio'] ) ? $input['bio'] : $current['bio'], | |
| 'social_links' => isset( $input['social_links'] ) ? $input['social_links'] : $current['social_links'], | |
| 'accordions' => isset( $input['accordions'] ) ? $input['accordions'] : $current['accordions'], | |
| ); | |
| $update['post_content'] = self::build_speaker_content( $merged ); | |
| } | |
| $result = wp_update_post( $update, true ); | |
| if ( is_wp_error( $result ) ) { | |
| return $result; | |
| } | |
| return array( 'success' => true ); | |
| } | |
| /** | |
| * Delete a speaker. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Input values. | |
| * @return array|WP_Error | |
| */ | |
| public static function delete_speaker( $input ) { | |
| $post_id = absint( $input['id'] ); | |
| $result = wp_delete_post( $post_id, true ); | |
| if ( ! $result ) { | |
| return new WP_Error( 'summit_speaker_delete_failed', __( 'Unable to delete speaker.', 'summit' ) ); | |
| } | |
| return array( 'success' => true ); | |
| } | |
| /** | |
| * Parse speaker content blocks. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param string $content Post content. | |
| * @return array | |
| */ | |
| public static function parse_speaker_content( $content ) { | |
| $blocks = parse_blocks( $content ); | |
| $bio = ''; | |
| $social_links = array(); | |
| $accordions = array(); | |
| foreach ( $blocks as $block ) { | |
| if ( 'core/group' === $block['blockName'] && ! empty( $block['attrs']['className'] ) && 'speaker-bio' === $block['attrs']['className'] ) { | |
| foreach ( $block['innerBlocks'] as $inner_block ) { | |
| if ( 'core/paragraph' === $inner_block['blockName'] ) { | |
| $bio = self::block_text( $inner_block ); | |
| break; | |
| } | |
| } | |
| } | |
| if ( 'core/social-links' === $block['blockName'] ) { | |
| foreach ( $block['innerBlocks'] as $social_block ) { | |
| if ( 'core/social-link' === $social_block['blockName'] ) { | |
| $social_links[] = array( | |
| 'service' => isset( $social_block['attrs']['service'] ) ? $social_block['attrs']['service'] : '', | |
| 'url' => isset( $social_block['attrs']['url'] ) ? $social_block['attrs']['url'] : '', | |
| ); | |
| } | |
| } | |
| } | |
| if ( 'core/details' === $block['blockName'] ) { | |
| $accordions[] = array( | |
| 'summary' => isset( $block['attrs']['summary'] ) ? $block['attrs']['summary'] : '', | |
| 'content' => self::block_text( $block ), | |
| ); | |
| } | |
| } | |
| return array( | |
| 'bio' => $bio, | |
| 'social_links' => $social_links, | |
| 'accordions' => $accordions, | |
| ); | |
| } | |
| /** | |
| * Build speaker block content. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $input Content pieces. | |
| * @return string | |
| */ | |
| public static function build_speaker_content( $input ) { | |
| $bio = isset( $input['bio'] ) ? $input['bio'] : ''; | |
| $social_links = isset( $input['social_links'] ) ? $input['social_links'] : array(); | |
| $accordions = isset( $input['accordions'] ) ? $input['accordions'] : array(); | |
| $bio_text = esc_html( $bio ); | |
| $bio_html = '<p>' . $bio_text . '</p>'; | |
| $group_inner_html = '<div class="wp-block-group speaker-bio"></div>'; | |
| $social_list_class = 'wp-block-social-links is-style-logos-only'; | |
| $social_inner_html = '<ul class="' . $social_list_class . '"></ul>'; | |
| $heading_text = esc_html__( 'More About This Speaker', 'summit' ); | |
| $heading_html = '<h2>' . $heading_text . '</h2>'; | |
| $blocks = array( | |
| array( | |
| 'blockName' => 'core/group', | |
| 'attrs' => array( 'className' => 'speaker-bio' ), | |
| 'innerBlocks' => array( | |
| array( | |
| 'blockName' => 'core/paragraph', | |
| 'attrs' => array(), | |
| 'innerBlocks' => array(), | |
| 'innerHTML' => $bio_html, | |
| 'innerContent' => array( $bio_html ), | |
| ), | |
| ), | |
| 'innerHTML' => $group_inner_html, | |
| 'innerContent' => array( | |
| '<div class="wp-block-group speaker-bio">', | |
| null, | |
| '</div>', | |
| ), | |
| ), | |
| array( | |
| 'blockName' => 'core/social-links', | |
| 'attrs' => array( 'className' => 'is-style-logos-only' ), | |
| 'innerBlocks' => self::build_social_blocks( $social_links ), | |
| 'innerHTML' => $social_inner_html, | |
| 'innerContent' => array( | |
| '<ul class="' . $social_list_class . '">', | |
| null, | |
| '</ul>', | |
| ), | |
| ), | |
| array( | |
| 'blockName' => 'core/heading', | |
| 'attrs' => array( | |
| 'level' => 2, | |
| 'content' => $heading_text, | |
| ), | |
| 'innerBlocks' => array(), | |
| 'innerHTML' => $heading_html, | |
| 'innerContent' => array( $heading_html ), | |
| ), | |
| ); | |
| foreach ( self::normalize_accordions( $accordions ) as $accordion ) { | |
| $summary_text = esc_html( $accordion['summary'] ); | |
| $summary_html = '<details><summary>' . $summary_text . '</summary></details>'; | |
| $summary_content = '<details><summary>' . $summary_text . '</summary>'; | |
| $accordion_content = '<p>' . esc_html( $accordion['content'] ) . '</p>'; | |
| $blocks[] = array( | |
| 'blockName' => 'core/details', | |
| 'attrs' => array( 'summary' => $accordion['summary'] ), | |
| 'innerBlocks' => array( | |
| array( | |
| 'blockName' => 'core/paragraph', | |
| 'attrs' => array(), | |
| 'innerBlocks' => array(), | |
| 'innerHTML' => $accordion_content, | |
| 'innerContent' => array( $accordion_content ), | |
| ), | |
| ), | |
| 'innerHTML' => $summary_html, | |
| 'innerContent' => array( | |
| $summary_content, | |
| null, | |
| '</details>', | |
| ), | |
| ); | |
| } | |
| return serialize_blocks( $blocks ); | |
| } | |
| /** | |
| * Build social link blocks. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $links Social links. | |
| * @return array | |
| */ | |
| public static function build_social_blocks( $links ) { | |
| $blocks = array(); | |
| if ( empty( $links ) ) { | |
| $links = array( | |
| array( 'service' => 'x' ), | |
| array( 'service' => 'youtube' ), | |
| array( 'service' => 'instagram' ), | |
| array( 'service' => 'facebook' ), | |
| ); | |
| } | |
| foreach ( $links as $link ) { | |
| $attrs = array(); | |
| if ( ! empty( $link['service'] ) ) { | |
| $attrs['service'] = sanitize_key( $link['service'] ); | |
| } | |
| if ( ! empty( $link['url'] ) ) { | |
| $attrs['url'] = esc_url_raw( $link['url'] ); | |
| } | |
| $blocks[] = array( | |
| 'blockName' => 'core/social-link', | |
| 'attrs' => $attrs, | |
| 'innerBlocks' => array(), | |
| 'innerHTML' => '', | |
| 'innerContent' => array(), | |
| ); | |
| } | |
| return $blocks; | |
| } | |
| /** | |
| * Normalize accordion inputs. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $accordions Accordion data. | |
| * @return array | |
| */ | |
| public static function normalize_accordions( $accordions ) { | |
| if ( empty( $accordions ) ) { | |
| return array( | |
| array( | |
| 'summary' => __( 'Books & Publications', 'summit' ), | |
| 'content' => '', | |
| ), | |
| array( | |
| 'summary' => __( 'Speaking Topics', 'summit' ), | |
| 'content' => '', | |
| ), | |
| ); | |
| } | |
| $normalized = array(); | |
| foreach ( $accordions as $accordion ) { | |
| $normalized[] = array( | |
| 'summary' => isset( $accordion['summary'] ) ? sanitize_text_field( $accordion['summary'] ) : '', | |
| 'content' => isset( $accordion['content'] ) ? sanitize_text_field( $accordion['content'] ) : '', | |
| ); | |
| } | |
| return $normalized; | |
| } | |
| /** | |
| * Extract text from a block. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $block Block data. | |
| * @return string | |
| */ | |
| public static function block_text( $block ) { | |
| if ( empty( $block['innerHTML'] ) ) { | |
| return ''; | |
| } | |
| return trim( wp_strip_all_tags( $block['innerHTML'] ) ); | |
| } | |
| /** | |
| * Parse schedule template part into structured data. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return array | |
| */ | |
| public static function get_schedule() { | |
| $content = self::get_schedule_template_content(); | |
| if ( '' === $content ) { | |
| return array(); | |
| } | |
| $blocks = parse_blocks( $content ); | |
| $days = array(); | |
| self::extract_schedule_days( $blocks, $days ); | |
| return $days; | |
| } | |
| /** | |
| * Get schedule template part content. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @return string | |
| */ | |
| public static function get_schedule_template_content() { | |
| $path = get_theme_file_path( 'parts/schedule.html' ); | |
| if ( ! file_exists( $path ) ) { | |
| return ''; | |
| } | |
| return (string) file_get_contents( $path ); | |
| } | |
| /** | |
| * Recursively extract schedule days. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $blocks Blocks array. | |
| * @param array $days Days array passed by reference. | |
| * @return void | |
| */ | |
| public static function extract_schedule_days( $blocks, &$days ) { | |
| foreach ( $blocks as $block ) { | |
| if ( 'core/columns' === $block['blockName'] ) { | |
| $day = self::parse_schedule_columns( $block ); | |
| if ( ! empty( $day['day'] ) && ! empty( $day['date'] ) ) { | |
| $days[] = $day; | |
| } | |
| } | |
| if ( ! empty( $block['innerBlocks'] ) ) { | |
| self::extract_schedule_days( $block['innerBlocks'], $days ); | |
| } | |
| } | |
| } | |
| /** | |
| * Parse a schedule columns block. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $block Columns block. | |
| * @return array | |
| */ | |
| public static function parse_schedule_columns( $block ) { | |
| $columns = isset( $block['innerBlocks'] ) ? $block['innerBlocks'] : array(); | |
| if ( count( $columns ) < 2 ) { | |
| return array(); | |
| } | |
| $day_column = $columns[0]; | |
| $detail_column = $columns[1]; | |
| $day_label = self::find_heading_text( $day_column ); | |
| $date_label = self::find_heading_text( $detail_column ); | |
| $entries = self::extract_schedule_entries( $detail_column ); | |
| return array( | |
| 'day' => $day_label, | |
| 'date' => $date_label, | |
| 'entries' => $entries, | |
| ); | |
| } | |
| /** | |
| * Find the first heading text in a block tree. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $block Block data. | |
| * @return string | |
| */ | |
| public static function find_heading_text( $block ) { | |
| if ( 'core/heading' === $block['blockName'] ) { | |
| return self::block_text( $block ); | |
| } | |
| if ( empty( $block['innerBlocks'] ) ) { | |
| return ''; | |
| } | |
| foreach ( $block['innerBlocks'] as $inner_block ) { | |
| $found = self::find_heading_text( $inner_block ); | |
| if ( '' !== $found ) { | |
| return $found; | |
| } | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Extract schedule entries from a block tree. | |
| * | |
| * @since 1.0.0 | |
| * | |
| * @param array $block Block data. | |
| * @return array | |
| */ | |
| public static function extract_schedule_entries( $block ) { | |
| $entries = array(); | |
| if ( 'core/group' === $block['blockName'] ) { | |
| $paragraphs = array(); | |
| foreach ( $block['innerBlocks'] as $inner_block ) { | |
| if ( 'core/paragraph' === $inner_block['blockName'] ) { | |
| $paragraphs[] = self::block_text( $inner_block ); | |
| } | |
| } | |
| if ( count( $paragraphs ) >= 2 ) { | |
| $entries[] = array( | |
| 'time' => $paragraphs[0], | |
| 'description' => $paragraphs[1], | |
| ); | |
| } | |
| } | |
| if ( ! empty( $block['innerBlocks'] ) ) { | |
| foreach ( $block['innerBlocks'] as $inner_block ) { | |
| $entries = array_merge( $entries, self::extract_schedule_entries( $inner_block ) ); | |
| } | |
| } | |
| return $entries; | |
| } | |
| } | |
| Summit_Abilities::init(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment