@@ -57,6 +57,13 @@ class Content_Gate {
5757 */
5858 private static $ restricted_content = [];
5959
60+ /**
61+ * Whether the overlay gate markup has been output in this execution.
62+ *
63+ * @var boolean
64+ */
65+ private static $ overlay_gate_output = false ;
66+
6067 /**
6168 * Initialize hooks and filters.
6269 */
@@ -66,7 +73,7 @@ public static function init() {
6673 add_action ( 'admin_init ' , [ __CLASS__ , 'handle_edit_gate_layout ' ] );
6774 add_action ( 'wp_enqueue_scripts ' , [ __CLASS__ , 'enqueue_scripts ' ] );
6875 add_action ( 'enqueue_block_editor_assets ' , [ __CLASS__ , 'enqueue_block_editor_assets ' ] );
69- add_action ( 'wp_footer ' , [ __CLASS__ , 'render_overlay_gate ' ], 1 );
76+ add_action ( 'after_setup_theme ' , [ __CLASS__ , 'register_overlay_gate_hooks ' ] );
7077 add_action ( 'before_delete_post ' , [ __CLASS__ , 'delete_gate_layouts ' ], 10 , 2 );
7178 add_filter ( 'newspack_popups_assess_has_disabled_popups ' , [ __CLASS__ , 'disable_popups ' ] );
7279 add_filter ( 'newspack_reader_activity_article_view ' , [ __CLASS__ , 'suppress_article_view_activity ' ], 100 );
@@ -851,14 +858,57 @@ public static function render_overlay_gate() {
851858 $ _post = $ post ;
852859 $ post = \get_post ( $ gate_layout_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
853860 setup_postdata ( $ post );
854-
855861 self ::render_overlay_gate_html ( $ gate_layout_id );
862+ self ::$ overlay_gate_output = true ;
856863
857864 self ::mark_gate_as_rendered ();
858865 wp_reset_postdata ();
859866 $ post = $ _post ; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
860867 }
861868
869+ /**
870+ * Register overlay gate hooks after the theme has been set up.
871+ *
872+ * Deferred to after_setup_theme so that wp_is_block_theme() can be called safely,
873+ * after theme directories have been registered.
874+ */
875+ public static function register_overlay_gate_hooks () {
876+ if ( self ::is_block_theme () ) {
877+ add_filter ( 'render_block ' , [ __CLASS__ , 'inject_overlay_gate_after_post_content_block ' ], 10 , 2 );
878+ } else {
879+ add_action ( 'get_footer ' , [ __CLASS__ , 'render_overlay_gate ' ], 1 );
880+ }
881+ }
882+
883+ /**
884+ * Inject overlay gate markup right after the post content block.
885+ *
886+ * Used for block themes where there aren't hooks to use in time to get do_blocks() to run.
887+ *
888+ * @param string $block_content Block content.
889+ * @param array $block Parsed block.
890+ *
891+ * @return string
892+ */
893+ public static function inject_overlay_gate_after_post_content_block ( $ block_content , $ block ) {
894+ static $ injected = false ;
895+
896+ // $injected prevents re-entry even if render_overlay_gate() bails early (e.g. gate style is not "overlay").
897+ // $overlay_gate_output is only set when HTML is actually rendered. Both guards are needed.
898+ if ( $ injected || self ::$ overlay_gate_output || ! is_singular () ) {
899+ return $ block_content ;
900+ }
901+
902+ if ( 'core/post-content ' !== ( $ block ['blockName ' ] ?? '' ) ) {
903+ return $ block_content ;
904+ }
905+
906+ $ injected = true ;
907+ ob_start ();
908+ self ::render_overlay_gate ();
909+ return $ block_content . ob_get_clean ();
910+ }
911+
862912 /**
863913 * Disable popups if rendering a restricted post.
864914 *
0 commit comments