Last active
March 10, 2024 14:27
-
-
Save jessedyck/c5a318f69680b3a3cacf253f4c6ff040 to your computer and use it in GitHub Desktop.
Convert from the (abandoned) footnotes plugin to WordPress core footnotes.
This file contains 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 | |
/** | |
* A script to convert from the (abandoned) footnotes plugin to WP core footnotes. | |
* This script is intended to be ran from wp-cli. | |
* Requires WP 6.3. Does not require that the original footnotes plugin still be installed, | |
* but does require that the options in `wp_options` are still in place. | |
* | |
* BACKUP YOUR DATABASE! | |
* This script is not well-tested. It works for my (small) use-case. It should support | |
* the original plugin's various short codes, but has only been tested with the | |
* default `[ref]` and similar default settings. | |
* | |
* Proceed with caution! | |
* | |
* Usage: | |
* wp eval-file convert-footnotes.php | |
* | |
* @see https://wordpress.org/plugins/footnotes/ | |
*/ | |
// phpcs:set WordPress.NamingConventions.PrefixAllGlobals prefixes[] jdf | |
/** | |
* Encapsulate in function to namespace variables. | |
*/ | |
function jdf_run(): void { | |
/** | |
* Ensure we have the footnotes block | |
*/ | |
if ( ! WP_Block_Type_Registry::get_instance()->is_registered( 'core/footnotes' ) ) { | |
echo 'Core footnotes block is not registered'; | |
die(); | |
} | |
/** | |
* Get the plugin's settings. | |
*/ | |
$footnotes_options = get_option( 'footnotes_storage' ); | |
if ( ! is_array( $footnotes_options ) ) { | |
echo "Could not find 'footnotes' plugin options"; | |
die(); | |
} | |
// Get the short code used by footnotes plugin. | |
$start_tag = $footnotes_options['footnote_inputfield_placeholder_start']; | |
$end_tag = $footnotes_options['footnote_inputfield_placeholder_end']; | |
$escaped_start_tag = preg_quote( $start_tag ); | |
$escaped_end_tag = preg_quote( $end_tag, '/' ); | |
// Get all posts to check. | |
// Prior to 6.5, footnotes were only supported on core post types. | |
$post_types = 'any'; | |
if ( ! is_wp_version_compatible( '6.5' ) ) { | |
echo "Note: Prior to WP 6.5, footnotes were not supported on CPTs. Re-run the script after upgrading to convert footnotes on more post types.\n"; | |
$post_types = array( 'post', 'page' ); | |
} | |
$args = array( | |
'post_type' => $post_types, | |
'post_status' => 'publish', | |
'posts_per_page' => '-1', | |
); | |
$all_posts = new WP_Query( $args ); | |
echo "Found {$all_posts->found_posts} posts\n"; | |
if ( $all_posts->have_posts() ) { | |
while ( $all_posts->have_posts() ) { | |
$all_posts->the_post(); | |
$content = get_the_content(); | |
$matches = array(); | |
$regex = "/$escaped_start_tag(.*?)$escaped_end_tag/i"; | |
if ( preg_match_all( $regex, $content, $matches ) ) { | |
$pid = get_the_ID(); | |
$permalink = get_the_permalink(); | |
echo "Found plugin footnote on post ID: {$pid}, permalink: {$permalink}\n"; | |
/** | |
* In my case, there are duplicate matches because of the `source` block | |
* attribute from Jetpack's Markdown block. | |
* Deduping ensures no extra metadata is created. | |
* */ | |
$matches[0] = array_unique( $matches[0] ); | |
$matches[1] = array_unique( $matches[1] ); | |
// Get existing core footnotes. | |
$footnotes = get_post_meta( $pid, 'footnotes', true ); | |
if ( empty( $footnotes ) ) { | |
$footnotes = array(); | |
} else { | |
$footnotes = json_decode( $footnotes ); | |
} | |
foreach ( $matches[0] as $key => $match ) { | |
$fn = new stdClass(); | |
$uid = strtolower( jdf_GUID() ); | |
$fn->content = $matches[1][ $key ]; | |
$fn->id = $uid; | |
$footnotes[] = $fn; | |
$fn_num = count( $footnotes ); | |
// Markup format is as of WP6.4.1. | |
$content = str_replace( | |
$matches[0][ $key ], | |
sprintf( '<sup data-fn="%1$s" class="fn"><a href="#%1$s" id="%1$s-link">%2$d</a></sup>', $uid, $fn_num ), | |
$content | |
); | |
} | |
// Add the footnotes block, if it doesn't exist. | |
if ( ! has_block( 'core/footnotes' ) ) { | |
$content .= '<!-- wp:footnotes /-->'; | |
} | |
// Confirm before updating. | |
$prompt = readline( "Update post ID: {$pid}? (y) " ); | |
if ( $prompt === 'y' ) { | |
echo "Updating $pid...\n"; | |
wp_update_post( | |
array( | |
'ID' => $pid, | |
'post_content' => $content, | |
'meta_input' => array( | |
'footnotes' => wp_json_encode( $footnotes, JSON_UNESCAPED_UNICODE ), | |
), | |
) | |
); | |
} else { | |
echo "Skipping $pid.\n"; | |
} | |
} | |
} | |
} | |
} | |
jdf_run(); | |
/** | |
* Generates a GUID for use in linking footnotes. | |
* | |
* @link https://stackoverflow.com/a/26163679 | |
* @return string | |
*/ | |
/* phpcs:disable */ | |
function jdf_GUID() | |
{ | |
if (function_exists('com_create_guid') === true) | |
{ | |
return trim(com_create_guid(), '{}'); | |
} | |
return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)); | |
} | |
/* phpcs:enable */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment