ID ) ) { return; } // Only Elementor-built posts. We check the same flag the engine // uses (_elementor_edit_mode === 'builder') via the linker helper // when available, with a direct meta fallback. $is_elementor = ( get_post_meta( $post->ID, '_elementor_edit_mode', true ) === 'builder' ); if ( ! $is_elementor ) { return; } add_meta_box( 'flink_elementor_panel', __( 'Internal linking (Elementor)', '4linking-free' ), array( $this, 'render' ), $post_type, 'side', 'default', array( '__back_compat_meta_box' => false, '__block_editor_compatible_meta_box' => true, ) ); } /** * Render the box: an intro line, two buttons (preview / apply), and an * empty results area the JS fills in. The nonce is embedded for the * AJAX call (same nonce action the rest of the admin uses). * * @param WP_Post $post The current post. */ public function render( $post ) { $post_id = (int) $post->ID; $nonce = wp_create_nonce( 'flink_nonce' ); $ajax_url = admin_url( 'admin-ajax.php' ); // Inform the user whether APPLY will actually link from this page // under current settings. Dry-run always previews, but apply obeys // the origen-type filter. This avoids the "preview shows links but // apply does nothing" confusion documented in the Elementor brief. $origen_tipo = get_option( 'flink_origen_tipo', 'post' ); $can_apply = ( $origen_tipo === 'any' ); if ( ! $can_apply ) { $tipos = array_map( 'trim', explode( ',', (string) $origen_tipo ) ); $can_apply = in_array( get_post_type( $post_id ), $tipos, true ); } ?>