post_content ?? ''; $existing_blocks = parse_blocks( $raw_content ); $index = self::resolve_insert_index( $position, $existing_blocks, $selection ); if ( is_wp_error( $index ) ) { return $index; } array_splice( $existing_blocks, $index, 0, $blocks ); $new_content = serialize_blocks( BlocksToHtml::normalize_blocks( $existing_blocks ) ); // $updated = wp_update_post( // array( // 'ID' => $post_id, // 'post_content' => $new_content, // ), // true // ); // if ( is_wp_error( $updated ) ) { // return $updated; // } $result_html = BlocksToHtml::convert_blocks( $blocks ); $replacement_html = serialize_blocks( BlocksToHtml::normalize_blocks( $blocks ) ); $result = array( 'action' => 'insert', 'editor' => 'gutenberg', 'position' => $position, 'insert_index' => $index, 'post_id' => $post_id, 'blocks' => $blocks, 'html' => $result_html, 'replacement_html' => $replacement_html, 'post_content' => $new_content, ); if ( $stage_result ) { SelectionBridge::store_result( $result ); } return array_merge( $result, array( 'success' => true, 'message' => self::insert_success_message( $position ), ) ); } /** * Resolve the absolute top-level index where new blocks should be inserted. * * @param string $position Requested position. * @param array $existing_blocks Current top-level blocks. * @param array|null $selection Live selection supplied by the active request. * @return int|WP_Error Absolute index (0..count) or error when a required * selection is missing. */ private static function resolve_insert_index( string $position, array $existing_blocks, ?array $selection = null ) { $count = count( $existing_blocks ); if ( 'start' === $position ) { return 0; } if ( 'end' === $position || '' === $position ) { return $count; } if ( 'before_selection' === $position || 'after_selection' === $position ) { if ( ! $selection ) { $selection = SelectionBridge::read_selection(); if ( $selection && ! SelectionBridge::is_fresh( $selection ) ) { $selection = null; } } if ( ! $selection || empty( $selection['indices'] ) ) { return new WP_Error( 'no_selection', __( 'No block selected in the editor. Select a block to use as the anchor, then ask again to insert before/after it.', 'advanced-animation' ) ); } $indices = array_filter( array_map( 'intval', (array) $selection['indices'] ), function ( $i ) use ( $count ) { return $i >= 0 && $i < $count; } ); if ( empty( $indices ) ) { return new WP_Error( 'invalid_selection', __( 'The selected block could not be located on this page. Please reselect a block and try again.', 'advanced-animation' ) ); } if ( 'before_selection' === $position ) { return min( $indices ); } return max( $indices ) + 1; } // Unknown position falls back to the end. return $count; } /** * Human-friendly success message for the chosen position. */ private static function insert_success_message( string $position ): string { switch ( $position ) { case 'start': return __( 'Blocks added at the top of the page.', 'advanced-animation' ); case 'before_selection': return __( 'Blocks added before the selected block.', 'advanced-animation' ); case 'after_selection': return __( 'Blocks added after the selected block.', 'advanced-animation' ); default: return __( 'Blocks added to the page.', 'advanced-animation' ); } } /** * Replace the currently selected blocks with new blocks. * * @param array $blocks Block data arrays to insert in place of the selection. * @param string|null $page_content Optional live page content from the editor. When provided, * used instead of the database post_content for resolving * selection indices, ensuring they match the editor state. * @param bool $stage_result Whether to stage the result for explicit editor application. * @param array|null $selection Live selection supplied by the active request. * @return array|WP_Error Result data or error. */ public static function replace_selection( array $blocks, ?string $page_content = null, bool $stage_result = true, ?array $selection = null ) { if ( ! $selection ) { $selection = SelectionBridge::read_selection(); if ( $selection && ! SelectionBridge::is_fresh( $selection ) ) { $selection = null; } } if ( ! $selection ) { return new WP_Error( 'no_selection', __( 'No blocks selected. Please select one or more blocks in the editor to replace them.', 'advanced-animation' ) ); } $post_id = intval( $selection['post_id'] ?? 0 ); $indices = $selection['indices'] ?? array(); $client_ids = $selection['clientIds'] ?? array(); if ( ! $post_id ) { return new WP_Error( 'invalid_post', __( 'Selection is missing a post ID.', 'advanced-animation' ) ); } if ( ! Security::can_edit_post( $post_id ) ) { return new WP_Error( 'edit_forbidden', __( 'Sorry, you are not allowed to edit this post.', 'advanced-animation' ) ); } $post = get_post( $post_id ); if ( ! $post ) { return new WP_Error( 'post_not_found', __( 'Post not found.', 'advanced-animation' ) ); } $raw_content = $page_content ?? $post->post_content ?? ''; $existing_blocks = parse_blocks( $raw_content ); if ( empty( $existing_blocks ) ) { $existing_blocks = $blocks; } elseif ( ! empty( $indices ) ) { sort( $indices, SORT_NUMERIC ); $indices = array_reverse( $indices ); foreach ( $indices as $idx ) { if ( isset( $existing_blocks[ $idx ] ) ) { array_splice( $existing_blocks, $idx, 1, $blocks ); break; } } if ( ! in_array( $blocks, $existing_blocks, true ) ) { $first_idx = min( $indices ); if ( isset( $existing_blocks[ $first_idx ] ) ) { array_splice( $existing_blocks, $first_idx, count( $indices ), $blocks ); } } } else { $existing_blocks = $blocks; } $new_content = serialize_blocks( BlocksToHtml::normalize_blocks( $existing_blocks ) ); // $updated = wp_update_post( // array( // 'ID' => $post_id, // 'post_content' => $new_content, // ), // true // ); // if ( is_wp_error( $updated ) ) { // return $updated; // } $result_html = BlocksToHtml::convert_blocks( $blocks ); $replacement_html = serialize_blocks( BlocksToHtml::normalize_blocks( $blocks ) ); $result = array( 'action' => 'replace', 'editor' => 'gutenberg', 'post_id' => $post_id, 'clientIds' => $client_ids, 'blocks' => $blocks, 'html' => $result_html, 'indices' => $indices, 'post_content' => $new_content, 'replacement_html' => $replacement_html, ); if ( $stage_result ) { SelectionBridge::store_result( $result ); } return array_merge( $result, array( 'success' => true, 'message' => __( 'Selected blocks replaced.', 'advanced-animation' ), ) ); } }