← All changes
|
includes/admin/importers/class-block-converter.php
+57
-9
2.10.0
→
2.13.0
View file →
| @@ -388,9 +388,9 @@ | ||
| 388 | 388 | if ('' === $content || !class_exists('WP_Block_Parser')) { |
| 389 | 389 | return $unchanged; |
| 390 | 390 | } |
| 391 | 391 | |
| 392 | - $spans = self::find_source_blocks($content); | |
| 392 | + $spans = self::find_blocks($content, [Rank_Math_Blocks::class, 'is_source_block']); | |
| 393 | 393 | |
| 394 | 394 | if (is_string($spans)) { |
| 395 | 395 | return ['content' => $content, 'converted' => 0, 'error' => $spans]; |
| 396 | 396 | } |
| @@ -425,15 +425,63 @@ | ||
| 425 | 425 | ]; |
| 426 | 426 | } |
| 427 | 427 | |
| 428 | 428 | /** |
| 429 | - * Byte spans of every convertible block, in document order. | |
| 429 | + * Byte spans of every `thinkrank/faq` block in a post, in document order. | |
| 430 | 430 | * |
| 431 | + * Exposed so the FAQ abilities can edit FAQ blocks the same way this class | |
| 432 | + * edits Rank Math ones: by replacing exact byte ranges. A | |
| 433 | + * parse_blocks()/serialize_blocks() round trip would rewrite every other | |
| 434 | + * block in the post as a side effect, which is the property the class | |
| 435 | + * docblock above opens with (#767). | |
| 436 | + * | |
| 437 | + * @since 2.10.1 | |
| 431 | 438 | * @param string $content Post content. |
| 432 | 439 | * @return array<int,array{start:int,end:int,name:string,attrs:array<string,mixed>}>|string |
| 440 | + * The spans, or a message describing why the content is unsafe to edit. | |
| 441 | + */ | |
| 442 | + public static function find_faq_blocks(string $content) { | |
| 443 | + if (!class_exists('WP_Block_Parser')) { | |
| 444 | + return []; | |
| 445 | + } | |
| 446 | + | |
| 447 | + return self::find_blocks( | |
| 448 | + $content, | |
| 449 | + static fn(string $name): bool => \ThinkRank\SEO\FAQ_Content::FAQ_BLOCK === $name | |
| 450 | + ); | |
| 451 | + } | |
| 452 | + | |
| 453 | + /** | |
| 454 | + * A `thinkrank/faq` block, serialized exactly as the editor would save it. | |
| 455 | + * | |
| 456 | + * Only `faqs` is written: Gutenberg omits attributes that equal their | |
| 457 | + * registered default, so a block carrying anything else would not match | |
| 458 | + * what the editor regenerates on the next save. | |
| 459 | + * | |
| 460 | + * @since 2.10.1 | |
| 461 | + * @param array<int,array<string,mixed>> $faqs Repeater rows. | |
| 462 | + * @return string Block markup, or '' when no row carries anything. | |
| 463 | + */ | |
| 464 | + public static function serialize_faq_block(array $faqs): string { | |
| 465 | + $attrs = ['faqs' => array_values($faqs)]; | |
| 466 | + $html = self::render_faq_html($attrs); | |
| 467 | + | |
| 468 | + if ('' === $html) { | |
| 469 | + return ''; | |
| 470 | + } | |
| 471 | + | |
| 472 | + return self::serialize_block('thinkrank/faq', $attrs, $html); | |
| 473 | + } | |
| 474 | + | |
| 475 | + /** | |
| 476 | + * Byte spans of every matching block, in document order. | |
| 477 | + * | |
| 478 | + * @param string $content Post content. | |
| 479 | + * @param callable $matches Receives a block name, returns whether to collect it. | |
| 480 | + * @return array<int,array{start:int,end:int,name:string,attrs:array<string,mixed>}>|string | |
| 433 | 481 | * The spans, or a message describing why the content is unsafe to convert. |
| 434 | 482 | */ |
| 435 | - private static function find_source_blocks(string $content) { | |
| 483 | + private static function find_blocks(string $content, callable $matches) { | |
| 436 | 484 | $parser = new \WP_Block_Parser(); |
| 437 | 485 | $parser->document = $content; |
| 438 | 486 | $parser->offset = 0; |
| 439 | 487 | |
| @@ -454,17 +502,17 @@ | ||
| 454 | 502 | } |
| 455 | 503 | |
| 456 | 504 | $parser->offset = $start + $length; |
| 457 | 505 | |
| 458 | - // A stray Rank Math closer with no opener has nothing to convert, | |
| 459 | - // and removing it is not ours to decide. | |
| 460 | - if ('block-closer' === $type || !Rank_Math_Blocks::is_source_block((string) $name)) { | |
| 506 | + // A stray closer with no opener has nothing to convert, and | |
| 507 | + // removing it is not ours to decide. | |
| 508 | + if ('block-closer' === $type || !$matches((string) $name)) { | |
| 461 | 509 | continue; |
| 462 | 510 | } |
| 463 | 511 | |
| 464 | - // Attribute text that is not valid JSON decodes to null. Mapping | |
| 465 | - // that as "no attributes" would convert a block with questions in | |
| 466 | - // it into an empty one and delete them. | |
| 512 | + // Attribute text that is not valid JSON decodes to null. Treating | |
| 513 | + // that as "no attributes" would turn a block with questions in it | |
| 514 | + // into an empty one and delete them. | |
| 467 | 515 | if (!is_array($attrs)) { |
| 468 | 516 | return sprintf('%s at byte %d has attributes that are not valid JSON.', $name, $start); |
| 469 | 517 | } |
| 470 | 518 | |