File: /var/www/html/triad-infosec/wp-content/plugins/genesis-blocks/lib/Migration/PostContent.php
<?php
/**
* Genesis Blocks post content migration.
*
* @package Genesis\Blocks\Migration
* @since 1.1.0
* @author StudioPress
* @license GPL-2.0-or-later
* @link https://github.com/studiopress/genesis-blocks/
*/
declare(strict_types=1);
namespace Genesis\Blocks\Migration;
use WP_Error;
/**
* Migrates the post content.
*
* @since 1.1.0
*/
final class PostContent {
/**
* Migrates batches of Atomic Blocks blocks in posts that have them.
*
* Sets a timeout to avoid PHP timeouts. The migration app will request
* migrations in batches until the number of posts found is zero.
*
* @return array|WP_Error The result of the migration, or a WP_Error if it failed.
*/
public function migrate_batch() {
$success_count = 0;
$error_count = 0;
$errors = new WP_Error();
$max_allowed_errors = 20;
$posts = $this->query_for_posts();
$time_start = microtime( true );
$timeout = 10;
while (
! empty( $posts )
&& $error_count < $max_allowed_errors
&& microtime( true ) - $time_start <= $timeout
) {
foreach ( $posts as $post ) {
if ( isset( $post->ID ) ) {
$migrated_post = $this->migrate_single( $post->ID );
if ( is_wp_error( $migrated_post ) ) {
$error_count++;
$errors->add( $migrated_post->get_error_code(), $migrated_post->get_error_message() );
} else {
$success_count++;
}
}
}
$posts = $this->query_for_posts();
}
$is_success = $error_count < $max_allowed_errors;
if ( ! $is_success ) {
return $errors;
}
$results = [
'successCount' => $success_count,
'errorCount' => $error_count,
'postsFound' => $success_count + $error_count,
];
if ( $errors->has_errors() ) {
$results['errorMessage'] = $errors->get_error_message();
}
return $results;
}
/**
* Migrates the block namespaces in post_content.
*
* Blocks are stored in the post_content of a post with a namespace,
* like '<!-- wp:atomic-blocks/ab-accordion /-->'.
* In that case, 'atomic-blocks/ab' needs to be changed to the new namespace.
* But nothing else in the block should be changed.
* The block pattern is mainly taken from Core.
*
* @see https://github.com/WordPress/wordpress-develop/blob/78d1ab2ed40093a5bd2a75b01ceea37811739f55/src/wp-includes/class-wp-block-parser.php#L413
*
* @param int $post_id The ID of the post to convert.
* @return int|WP_Error The post ID that was changed, or a WP_Error on failure.
*/
public function migrate_single( $post_id ) {
$post = get_post( $post_id );
if ( ! isset( $post->ID ) ) {
return new WP_Error(
'invalid_post_id',
__( 'Invalid post ID', 'genesis-blocks' )
);
}
$new_content = $this->migrate_post_content( $post->post_content );
return wp_update_post(
[
'ID' => $post->ID,
'post_content' => wp_slash( $new_content ),
],
true
);
}
/**
* Migrates the existing post content to the
* format used by Genesis Blocks.
*
* @param string $content The post content to migrate.
*
* @return string
*/
public function migrate_post_content( string $content ): string {
$new_content = $content;
// The newsletter block didn't follow the existing naming convention, so let's target it specifically.
$new_content = str_replace( 'atomic-blocks/newsletter', 'genesis-blocks/gb-newsletter', $new_content );
// Block names.
$new_content = str_replace( 'atomic-blocks/ab', 'genesis-blocks/gb', $new_content );
// HTML class name generated by WordPress.
$new_content = str_replace( 'wp-block-atomic-blocks-ab', 'wp-block-genesis-blocks-gb', $new_content );
// Catch all.
$new_content = str_replace( 'atomic-blocks', 'genesis-blocks', $new_content );
// Replace exact matches for known Atomic Blocks strings prefixed with "ab-".
$known_strings = [
'ab-1-col',
'ab-2-col',
'ab-3-col',
'ab-4-col',
'ab-5-col',
'ab-6-col',
'ab-accordion',
'ab-add-image',
'ab-align',
'ab-author-profile',
'ab-background',
'ab-block',
'ab-button',
'ab-change-image',
'ab-column',
'ab-container',
'ab-cta',
'ab-dismissable',
'ab-divider',
'ab-drop-cap',
'ab-font-size',
'ab-form-styles',
'ab-has-avatar',
'ab-has-background-dim',
'ab-has-custom',
'ab-has-parallax',
'ab-is-responsive-column',
'ab-is-vertically-aligned',
'ab-layout',
'ab-list',
'ab-newsletter',
'ab-notice',
'ab-post-grid',
'ab-pricing',
'ab-profile',
'ab-share',
'ab-social-links',
'ab-social-text',
'ab-spacer',
'ab-testimonial',
'ab-white-text',
];
$replacements = str_replace( 'ab-', 'gb-', $known_strings );
$new_content = str_replace( $known_strings, $replacements, $new_content );
return $new_content;
}
/**
* Gets posts that have Atomic Blocks blocks in their post_content.
*
* Queries for posts that have wp:atomic-blocks/ in the post content,
* meaning they probably have an Atomic Blocks block.
* Excludes revision posts, as this could overwrite the entire history.
* This will allow users to go back to the content before it was migrated.
*
* @return array The posts that were found.
*/
public function query_for_posts() {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_type != %s AND post_content LIKE %s LIMIT %d",
'revision',
'%wp:atomic-blocks/%',
10
)
);
}
}