Skip to content

Instantly share code, notes, and snippets.

@davidfcarr
Created August 22, 2026 15:09
Show Gist options
  • Select an option

  • Save davidfcarr/4a7dd41101ab3e4975cade91d69a6c12 to your computer and use it in GitHub Desktop.

Select an option

Save davidfcarr/4a7dd41101ab3e4975cade91d69a6c12 to your computer and use it in GitHub Desktop.
The Advanced Editor Remover
<?php
/**
* Plugin Name: The Advanced Editor Remover
* Plugin URI: https://example.com/
* Description: Removes Advanced Editor (TinyMCE) block comments like <!-- wp:tadv/classic-paragraph --> without altering surrounding content. Includes test mode, bulk processing, undo functionality, and backup cleanup.
* Version: 1.1.0
* Author: WordPress Developer
* License: GPL-2.0+
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class TADV_Remover_Plugin {
private static $instance = null;
private $meta_key = '_tadv_original_content';
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct() {
add_action( 'admin_menu', array( $this, 'register_admin_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
// AJAX hooks
add_action( 'wp_ajax_tadv_remover_process', array( $this, 'handle_ajax_process' ) );
add_action( 'wp_ajax_tadv_remover_undo', array( $this, 'handle_ajax_undo' ) );
add_action( 'wp_ajax_tadv_remover_delete_meta', array( $this, 'handle_ajax_delete_meta' ) );
}
/**
* Register Tools sub-menu page
*/
public function register_admin_menu() {
add_submenu_page(
'tools.php',
'Advanced Editor Remover',
'Advanced Editor Remover',
'manage_options',
'tadv-remover',
array( $this, 'render_admin_page' )
);
}
/**
* Enqueue JavaScript & Inline CSS
*/
public function enqueue_admin_scripts( $hook ) {
if ( 'tools_page_tadv-remover' !== $hook ) {
return;
}
wp_enqueue_script( 'jquery' );
}
/**
* Clean block comment tags from content
*/
private function strip_tadv_tags( $content ) {
// Matches opening, closing, and self-closing wp:tadv tags:
// - Opening: <!-- wp:tadv/... -->
// - Closing: <!-- /wp:tadv/... -->
// - Self-closing: <!-- wp:tadv/... /-->
$pattern = '/<!--\s*\/?wp:tadv\/[a-zA-Z0-9_.-]+(?:\s+\{[\s\S]*?\})?\s*\/?-->\r?\n?/i';
return preg_replace( $pattern, '', $content );
}
/**
* Main Admin Interface
*/
public function render_admin_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Count affected posts
global $wpdb;
$affected_count = $wpdb->get_var(
"SELECT COUNT(ID) FROM {$wpdb->posts}
WHERE post_status NOT IN ('auto-draft', 'trash')
AND post_content LIKE '%wp:tadv/%'"
);
// Count posts with undo meta
$undo_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(DISTINCT post_id) FROM {$wpdb->postmeta} WHERE meta_key = %s",
$this->meta_key
)
);
?>
<div class="wrap">
<h1>The Advanced Editor Remover</h1>
<p>Find and clean <code>&lt;!-- wp:tadv/classic-paragraph --&gt;</code> comments left over from the Advanced Editor / TinyMCE plugin, preserving your actual content.</p>
<hr>
<div class="card" style="max-width: 800px; padding: 20px; margin-top: 20px;">
<h2>Operation Mode</h2>
<p>Found <strong><?php echo esc_html( $affected_count ); ?></strong> post(s) containing Advanced Editor code comments.</p>
<form id="tadv-remover-form">
<p>
<label>
<input type="radio" name="mode" value="single" checked="checked">
<strong>Test Mode:</strong> Convert a single post first to verify results
</label>
</p>
<p>
<label>
<input type="radio" name="mode" value="all">
<strong>Convert All Mode:</strong> Convert all remaining posts (<?php echo esc_html( $affected_count ); ?> total)
</label>
</p>
<p style="margin-top: 20px;">
<button type="submit" id="tadv-submit-btn" class="button button-primary" <?php disabled( $affected_count, 0 ); ?>>
Execute Cleanup
</button>
</p>
</form>
</div>
<div class="card" style="max-width: 800px; padding: 20px; margin-top: 20px;">
<h2>Backup Maintenance & Undo Operations</h2>
<p>Currently <strong><?php echo esc_html( $undo_count ); ?></strong> post(s) have backup data saved in postmeta (<code><?php echo esc_html( $this->meta_key ); ?></code>).</p>
<p>
<button type="button" id="tadv-undo-btn" class="button button-secondary" <?php disabled( $undo_count, 0 ); ?>>
Undo All Conversions
</button>
<button type="button" id="tadv-purge-btn" class="button button-secondary" style="color: #b32d2e; border-color: #b32d2e;" <?php disabled( $undo_count, 0 ); ?>>
Purge Backup Meta
</button>
</p>
</div>
<div id="tadv-status" style="margin-top: 20px; display: none;">
<div class="notice notice-info inline"><p id="tadv-status-text">Processing...</p></div>
</div>
<div id="tadv-results" style="margin-top: 20px; max-width: 1000px;"></div>
</div>
<script>
jQuery(document).ready(function($) {
$('#tadv-remover-form').on('submit', function(e) {
e.preventDefault();
var mode = $('input[name="mode"]:checked').val();
$('#tadv-submit-btn').prop('disabled', true);
$('#tadv-status').show();
$('#tadv-status-text').text('Processing posts, please wait...');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'tadv_remover_process',
mode: mode,
nonce: '<?php echo wp_create_nonce( "tadv_remover_nonce" ); ?>'
},
success: function(response) {
$('#tadv-submit-btn').prop('disabled', false);
$('#tadv-status').hide();
if (response.success) {
renderResultsTable(response.data.posts, response.data.message);
} else {
alert('Error: ' + response.data);
}
},
error: function() {
$('#tadv-submit-btn').prop('disabled', false);
$('#tadv-status').hide();
alert('An unexpected AJAX error occurred.');
}
});
});
$('#tadv-undo-btn').on('click', function(e) {
if (!confirm('Are you sure you want to restore original content for all processed posts?')) {
return;
}
var $btn = $(this);
$btn.prop('disabled', true);
$('#tadv-status').show();
$('#tadv-status-text').text('Restoring post content from meta backups...');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'tadv_remover_undo',
nonce: '<?php echo wp_create_nonce( "tadv_remover_nonce" ); ?>'
},
success: function(response) {
$('#tadv-status').hide();
if (response.success) {
alert(response.data.message);
location.reload();
} else {
$btn.prop('disabled', false);
alert('Error: ' + response.data);
}
},
error: function() {
$btn.prop('disabled', false);
$('#tadv-status').hide();
alert('An unexpected AJAX error occurred.');
}
});
});
$('#tadv-purge-btn').on('click', function(e) {
if (!confirm('Are you sure you want to permanently delete all postmeta backups? You will no longer be able to undo conversions.')) {
return;
}
var $btn = $(this);
$btn.prop('disabled', true);
$('#tadv-status').show();
$('#tadv-status-text').text('Purging backup postmeta fields...');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'tadv_remover_delete_meta',
nonce: '<?php echo wp_create_nonce( "tadv_remover_nonce" ); ?>'
},
success: function(response) {
$('#tadv-status').hide();
if (response.success) {
alert(response.data.message);
location.reload();
} else {
$btn.prop('disabled', false);
alert('Error: ' + response.data);
}
},
error: function() {
$btn.prop('disabled', false);
$('#tadv-status').hide();
alert('An unexpected AJAX error occurred.');
}
});
});
function renderResultsTable(posts, message) {
if (!posts || posts.length === 0) {
$('#tadv-results').html('<div class="notice notice-warning"><p>No posts were modified.</p></div>');
return;
}
var html = '<div class="notice notice-success"><p>' + message + '</p></div>';
html += '<table class="wp-list-table widefat fixed striped" style="margin-top:15px;">';
html += '<thead><tr><th>Title</th><th>Post Type</th><th>Actions</th></tr></thead><tbody>';
$.each(posts, function(i, post) {
html += '<tr>';
html += '<td><strong>' + post.title + '</strong> (ID: ' + post.id + ')</td>';
html += '<td>' + post.type + '</td>';
html += '<td>';
html += '<a href="' + post.view_url + '" target="_blank" class="button button-small">View Post</a> ';
html += '<a href="' + post.edit_url + '" target="_blank" class="button button-small">Edit Post</a>';
html += '</td>';
html += '</tr>';
});
html += '</tbody></table>';
$('#tadv-results').html(html);
}
});
</script>
<?php
}
/**
* AJAX Handler: Convert Posts
*/
public function handle_ajax_process() {
check_ajax_referer( 'tadv_remover_nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Insufficient permissions.' );
}
$mode = isset( $_POST['mode'] ) ? sanitize_text_field( $_POST['mode'] ) : 'single';
global $wpdb;
$limit = ( 'single' === $mode ) ? 1 : -1;
$query = "SELECT ID, post_title, post_type, post_content
FROM {$wpdb->posts}
WHERE post_status NOT IN ('auto-draft', 'trash')
AND post_content LIKE '%wp:tadv/%'";
if ( $limit > 0 ) {
$query .= " LIMIT " . intval( $limit );
}
$posts = $wpdb->get_results( $query );
if ( empty( $posts ) ) {
wp_send_json_error( 'No posts found with Advanced Editor code comments.' );
}
$processed = array();
foreach ( $posts as $post ) {
$original_content = $post->post_content;
$cleaned_content = $this->strip_tadv_tags( $original_content );
// Save backup meta only if it hasn't been saved before
if ( ! get_post_meta( $post->ID, $this->meta_key, true ) ) {
update_post_meta( $post->ID, $this->meta_key, $original_content );
}
// Update post content
$wpdb->update(
$wpdb->posts,
array( 'post_content' => $cleaned_content ),
array( 'ID' => $post->ID ),
array( '%s' ),
array( '%d' )
);
clean_post_cache( $post->ID );
$processed[] = array(
'id' => $post->ID,
'title' => esc_html( $post->post_title ? $post->post_title : '(No Title)' ),
'type' => esc_html( $post->post_type ),
'view_url' => esc_url( get_permalink( $post->ID ) ),
'edit_url' => esc_url( get_edit_post_link( $post->ID, 'raw' ) ),
);
}
$message = sprintf( 'Successfully processed %d post(s).', count( $processed ) );
wp_send_json_success( array(
'message' => $message,
'posts' => $processed,
) );
}
/**
* AJAX Handler: Undo All Conversions
*/
public function handle_ajax_undo() {
check_ajax_referer( 'tadv_remover_nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Insufficient permissions.' );
}
global $wpdb;
$meta_records = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s",
$this->meta_key
)
);
if ( empty( $meta_records ) ) {
wp_send_json_error( 'No backup content found to restore.' );
}
$restored_count = 0;
foreach ( $meta_records as $record ) {
$post_id = intval( $record->post_id );
$original_content = $record->meta_value;
$wpdb->update(
$wpdb->posts,
array( 'post_content' => $original_content ),
array( 'ID' => $post_id ),
array( '%s' ),
array( '%d' )
);
delete_post_meta( $post_id, $this->meta_key );
clean_post_cache( $post_id );
$restored_count++;
}
wp_send_json_success( array(
'message' => sprintf( 'Successfully restored %d post(s) to their original state.', $restored_count ),
) );
}
/**
* AJAX Handler: Delete Backup Postmeta
*/
public function handle_ajax_delete_meta() {
check_ajax_referer( 'tadv_remover_nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Insufficient permissions.' );
}
global $wpdb;
$deleted = $wpdb->delete(
$wpdb->postmeta,
array( 'meta_key' => $this->meta_key ),
array( '%s' )
);
if ( false === $deleted ) {
wp_send_json_error( 'Failed to remove backup meta from the database.' );
}
wp_send_json_success( array(
'message' => sprintf( 'Successfully purged %d backup meta record(s).', $deleted ),
) );
}
}
// Initialize the plugin
add_action( 'plugins_loaded', array( 'TADV_Remover_Plugin', 'get_instance' ) );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment