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; } }