ID, 'synthseo_post_id', true);
$demo_mode = true; // Temporarily show for all posts to demo the feature
if ($has_synthseo_data || $demo_mode) {
add_meta_box(
'synthseo-info',
'SynthSEO Information',
array($this, 'render_seo_meta_box'),
'post',
'side',
'high',
array(
// The block editor gets a real sidebar panel from the editor
// bundle (PluginDocumentSettingPanel); this classic box would
// otherwise ALSO render in the bottom meta-box strip there.
'__block_editor_compatible_meta_box' => true,
'__back_compat_meta_box' => true,
)
);
// The detailed analysis box at the BOTTOM of the editor (Yoast-style).
// Deliberately NOT __back_compat: this one should render in the block
// editor's meta-box strip — the sidebar panel is the quick view, this
// is the full workbench.
add_meta_box(
'synthseo-detail',
'SynthSEO Analysis',
array($this, 'render_detail_meta_box'),
'post',
'normal',
'high',
array(
'__block_editor_compatible_meta_box' => true,
)
);
// Ensure meta box is compatible with Gutenberg
add_filter('is_protected_meta', array($this, 'protect_synthseo_meta'), 10, 2);
}
}
/**
* Register meta fields for REST API access
*/
public function register_meta_fields() {
// Register SynthSEO meta fields
register_post_meta('post', '_synthseo_meta_title', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
));
register_post_meta('post', '_synthseo_meta_description', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
));
register_post_meta('post', '_synthseo_focus_keyword', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
));
register_post_meta('post', '_synthseo_slug', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
));
register_post_meta('post', '_synthseo_canonical_url', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
));
}
/**
* Render the SEO information meta box
*/
public function render_detail_meta_box($post) {
// React mount point for the detailed analysis box; the editor bundle
// mounts EditorDetailApp here (retrying, readyState-safe).
?>
array(), 'version' => SYNTHSEO_VERSION);
}
/**
* Enqueue scripts for the post editor
*/
public function enqueue_editor_scripts($hook) {
if ($hook !== 'post.php' && $hook !== 'post-new.php') {
return;
}
// Enqueue base styles for editor
wp_enqueue_style(
'synthseo-editor-styles',
SYNTHSEO_PUBLISHER_URL . 'assets/editor.css',
array(),
SYNTHSEO_VERSION
);
// Add inline styles for meta box
$this->enqueue_meta_box_styles();
// Check if React build exists
$editor_build = $this->get_build_file('editor');
if ($editor_build) {
// Load React bundle
$asset_file = $this->get_asset_file('editor');
wp_enqueue_script(
'synthseo-editor-react',
SYNTHSEO_PUBLISHER_URL . 'build/' . $editor_build,
$asset_file['dependencies'],
$asset_file['version'],
true
);
// Check for CSS file
$css_file = str_replace('.js', '.css', $editor_build);
if (file_exists(SYNTHSEO_PUBLISHER_PATH . 'build/' . $css_file)) {
wp_enqueue_style(
'synthseo-editor-react',
SYNTHSEO_PUBLISHER_URL . 'build/' . $css_file,
array('wp-components'),
$asset_file['version']
);
}
// Pass data to React
global $post;
wp_localize_script('synthseo-editor-react', 'synthseoEditor', array(
'nonce' => wp_create_nonce('wp_rest'),
'apiUrl' => rest_url('synthseo/v2/'),
'ajaxUrl' => admin_url('admin-ajax.php'),
'postId' => $post ? $post->ID : null,
'restUrl' => rest_url(),
'restNonce' => wp_create_nonce('wp_rest')
));
} else {
// React build not found — the broken state is user-visible (empty panel);
// this log is a debug breadcrumb only.
if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- gated behind WP_DEBUG_LOG
error_log('SynthSEO: React build files not found. Please run npm run build.');
}
}
}
/**
* Enqueue block editor assets
*/
public function enqueue_block_editor_assets() {
// Check if React build exists for block editor
$editor_build = $this->get_build_file('editor');
if ($editor_build) {
$asset_file = $this->get_asset_file('editor');
wp_enqueue_script(
'synthseo-block-editor',
SYNTHSEO_PUBLISHER_URL . 'build/' . $editor_build,
array_merge($asset_file['dependencies'], array('wp-blocks', 'wp-dom-ready', 'wp-edit-post')),
$asset_file['version'],
true
);
wp_localize_script('synthseo-block-editor', 'synthseo_editor', array(
'nonce' => wp_create_nonce('synthseo_editor'),
'ajax_url' => admin_url('admin-ajax.php'),
'restUrl' => rest_url(),
'restNonce' => wp_create_nonce('wp_rest')
));
}
}
/**
* Protect SynthSEO meta from showing in Gutenberg meta fields
*/
public function protect_synthseo_meta($protected, $meta_key) {
$synthseo_meta_keys = array(
'synthseo_post_id',
'synthseo_metadata',
'synthseo_content_metrics',
'synthseo_seo_metrics',
'synthseo_target_keywords'
);
if (in_array($meta_key, $synthseo_meta_keys)) {
return true;
}
return $protected;
}
}