cpt = $cpt; $this->blocks = $blocks; } public function init() { add_action( 'init', array( $this, 'boot_modules' ) ); add_action( 'wp', array( $this, 'detect_frontend_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets_conditionally' ), 20 ); add_shortcode( 'stacklar', array( $this, 'shortcode_render' ) ); } public function boot_modules() { call_user_func( array( $this->cpt, 'register' ) ); call_user_func( array( $this->blocks, 'register' ) ); } public function detect_frontend_assets() { if ( is_admin() || $this->should_enqueue_frontend_assets ) { return; } $posts = $this->get_query_posts(); $requirements = $this->create_frontend_requirements(); if ( is_singular( 'stacklar' ) ) { $requirements['has_form'] = true; $queried_object = get_queried_object(); if ( $queried_object instanceof WP_Post ) { $requirements = $this->merge_frontend_requirements( $requirements, $this->get_post_frontend_requirements( $queried_object ) ); } } else { $requirements = $this->get_posts_frontend_requirements( $posts ); } $this->apply_frontend_requirements( $requirements ); $this->should_enqueue_frontend_assets = apply_filters( 'stacklar_should_enqueue_assets', $this->should_enqueue_frontend_assets, $posts ); $this->should_enqueue_fontawesome = apply_filters( 'stacklar_should_enqueue_fontawesome', $this->should_enqueue_fontawesome, $posts ); $this->should_enqueue_advanced_styles = apply_filters( 'stacklar_should_enqueue_advanced_styles', $this->should_enqueue_advanced_styles, $posts ); } protected function get_query_posts() { if ( isset( $GLOBALS['wp_query'] ) && ! empty( $GLOBALS['wp_query']->posts ) && is_array( $GLOBALS['wp_query']->posts ) ) { return $GLOBALS['wp_query']->posts; } return array(); } protected function create_frontend_requirements() { return array( 'has_form' => false, 'needs_fontawesome' => false, 'needs_advanced_styles' => false, ); } protected function apply_frontend_requirements( array $requirements ) { if ( ! empty( $requirements['has_form'] ) ) { $this->should_enqueue_frontend_assets = true; } if ( ! empty( $requirements['needs_fontawesome'] ) ) { $this->should_enqueue_fontawesome = true; } if ( ! empty( $requirements['needs_advanced_styles'] ) ) { $this->should_enqueue_advanced_styles = true; } } protected function frontend_requirements_complete( array $requirements ) { return ! empty( $requirements['has_form'] ) && ! empty( $requirements['needs_fontawesome'] ) && ! empty( $requirements['needs_advanced_styles'] ); } protected function get_posts_frontend_requirements( array $posts ) { $requirements = $this->create_frontend_requirements(); foreach ( $posts as $post ) { $requirements = $this->merge_frontend_requirements( $requirements, $this->get_post_frontend_requirements( $post ) ); if ( $this->frontend_requirements_complete( $requirements ) ) { break; } } return $requirements; } protected function get_post_frontend_requirements( $post, array &$visited_posts = array() ) { $requirements = $this->create_frontend_requirements(); if ( ! $post instanceof WP_Post || empty( $post->post_content ) ) { return $requirements; } $post_id = intval( $post->ID ); if ( $post_id && isset( $visited_posts[ $post_id ] ) ) { return $requirements; } if ( $post_id ) { $visited_posts[ $post_id ] = true; } return $this->get_content_frontend_requirements( $post->post_content, $visited_posts ); } protected function get_content_frontend_requirements( $content, array &$visited_posts = array() ) { $requirements = $this->create_frontend_requirements(); if ( ! is_string( $content ) || '' === trim( $content ) ) { return $requirements; } if ( function_exists( 'has_shortcode' ) && has_shortcode( $content, 'stacklar' ) ) { $requirements['has_form'] = true; foreach ( $this->get_referenced_form_posts_from_content( $content ) as $referenced_post ) { $requirements = $this->merge_frontend_requirements( $requirements, $this->get_post_frontend_requirements( $referenced_post, $visited_posts ) ); if ( $requirements['needs_fontawesome'] && $requirements['needs_advanced_styles'] ) { break; } } } if ( function_exists( 'has_block' ) && strpos( $content, 'wp:stacklar/' ) !== false ) { $requirements = $this->merge_frontend_requirements( $requirements, $this->get_block_frontend_requirements( parse_blocks( $content ) ) ); } return $requirements; } protected function get_referenced_form_posts_from_content( $content ) { $posts = array(); if ( ! is_string( $content ) || '' === trim( $content ) ) { return $posts; } if ( ! function_exists( 'get_shortcode_regex' ) || ! function_exists( 'shortcode_parse_atts' ) ) { return $posts; } $pattern = get_shortcode_regex( array( 'stacklar' ) ); if ( ! preg_match_all( '/' . $pattern . '/', $content, $matches, PREG_SET_ORDER ) ) { return $posts; } foreach ( $matches as $shortcode_match ) { $tag = isset( $shortcode_match[2] ) ? $shortcode_match[2] : ''; if ( 'stacklar' !== $tag ) { continue; } $atts = shortcode_parse_atts( isset( $shortcode_match[3] ) ? $shortcode_match[3] : '' ); if ( ! is_array( $atts ) ) { $atts = array(); } $post_id = isset( $atts['id'] ) ? absint( $atts['id'] ) : 0; if ( ! $post_id ) { continue; } $post = get_post( $post_id ); if ( ! $post instanceof WP_Post ) { continue; } if ( 'stacklar' !== $post->post_type ) { continue; } $posts[ $post_id ] = $post; } return array_values( $posts ); } protected function get_block_frontend_requirements( array $blocks ) { $requirements = $this->create_frontend_requirements(); foreach ( $blocks as $block ) { $block_name = isset( $block['blockName'] ) ? (string) $block['blockName'] : ''; if ( '' !== $block_name && strpos( $block_name, 'stacklar/' ) === 0 ) { $requirements['has_form'] = true; if ( $this->block_needs_advanced_styles( $block_name, isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array() ) ) { $requirements['needs_advanced_styles'] = true; } if ( 'stacklar/rating' === $block_name && $this->rating_block_uses_fontawesome( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array() ) ) { $requirements['needs_fontawesome'] = true; } } if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { $requirements = $this->merge_frontend_requirements( $requirements, $this->get_block_frontend_requirements( $block['innerBlocks'] ) ); } if ( $requirements['has_form'] && $requirements['needs_fontawesome'] && $requirements['needs_advanced_styles'] ) { break; } } return $requirements; } protected function block_needs_advanced_styles( $block_name, array $attrs ) { static $advanced_blocks = array( 'stacklar/acceptance', 'stacklar/address-autocomplete', 'stacklar/checkbox', 'stacklar/color', 'stacklar/form-step', 'stacklar/image-select', 'stacklar/image-upload', 'stacklar/range', 'stacklar/rating', 'stacklar/repeater', 'stacklar/select-autocomplete', 'stacklar/signature', 'stacklar/tel', 'stacklar/toggle', ); if ( in_array( $block_name, $advanced_blocks, true ) ) { return true; } if ( 'stacklar/submit' === $block_name ) { return $this->submit_block_uses_payment( $attrs ); } return false; } protected function submit_block_uses_payment( array $attrs ) { if ( empty( $attrs['actions'] ) || ! is_array( $attrs['actions'] ) ) { return false; } foreach ( $attrs['actions'] as $action_slug ) { if ( in_array( sanitize_key( (string) $action_slug ), array( 'stacklar_paypal', 'stacklar_stripe', 'stacklar_woocommerce' ), true ) ) { return true; } } return false; } protected function rating_block_uses_fontawesome( array $attrs ) { $star_icon = isset( $attrs['starIcon'] ) ? trim( (string) $attrs['starIcon'] ) : ''; return '' !== $star_icon && 1 === preg_match( '/\bfa[srbld]?\b|\bfa-(solid|regular|brands)\b|fa-[a-z0-9-]+/i', $star_icon ); } protected function merge_frontend_requirements( array $base, array $extra ) { $merged = $this->create_frontend_requirements(); foreach ( $merged as $key => $value ) { $merged[ $key ] = ! empty( $base[ $key ] ) || ! empty( $extra[ $key ] ); } return $merged; } public function enqueue_frontend_assets_conditionally() { if ( is_admin() ) { return; } if ( ! $this->should_enqueue_frontend_assets ) { return; } $this->enqueue_frontend_styles(); $this->register_frontend_script(); wp_localize_script( 'stacklar-frontend', 'StacklarCfg', $this->build_frontend_config() ); wp_enqueue_script( 'stacklar-frontend' ); } protected function enqueue_frontend_styles() { $fe_css = STACKLAR_PATH . 'build/frontend.css'; if ( file_exists( $fe_css ) ) { wp_register_style( 'stacklar-frontend-style', STACKLAR_URL . 'build/frontend.css', array(), filemtime( $fe_css ) ); wp_enqueue_style( 'stacklar-frontend-style' ); } wp_enqueue_style( 'stacklar-style' ); if ( $this->should_enqueue_fontawesome && wp_style_is( 'stacklar-fontawesome', 'registered' ) ) { wp_enqueue_style( 'stacklar-fontawesome' ); } if ( $this->should_enqueue_advanced_styles && wp_style_is( 'stacklar-style-advanced', 'registered' ) ) { wp_enqueue_style( 'stacklar-style-advanced' ); } } protected function register_frontend_script() { $fe_js = STACKLAR_PATH . 'build/frontend.js'; wp_register_script( 'stacklar-frontend', STACKLAR_URL . 'build/frontend.js', $this->get_frontend_script_dependencies(), file_exists( $fe_js ) ? filemtime( $fe_js ) : STACKLAR_VERSION, false ); if ( function_exists( 'stacklar_set_script_translations' ) ) { stacklar_set_script_translations( 'stacklar-frontend' ); } } protected function get_frontend_script_dependencies() { // The phone field enhancer runs on window.jQuery, which the build does not declare. $dependencies = array( 'jquery' ); $fe_asset_path = STACKLAR_PATH . 'build/frontend.asset.php'; if ( file_exists( $fe_asset_path ) ) { $loaded_asset = require $fe_asset_path; if ( is_array( $loaded_asset ) && isset( $loaded_asset['dependencies'] ) && is_array( $loaded_asset['dependencies'] ) ) { $dependencies = array_values( array_unique( array_merge( $dependencies, $loaded_asset['dependencies'] ) ) ); } } return $dependencies; } protected function build_frontend_config() { return array( 'rest' => array( 'nonce' => wp_create_nonce( 'wp_rest' ), 'baseUrl' => esc_url_raw( rest_url( 'stacklar/v1' ) ), ), 'i18n' => array( 'sending' => __( 'Sending...', 'stacklar-forms' ), 'success' => __( 'Submitted successfully.', 'stacklar-forms' ), 'error' => __( 'Submission failed.', 'stacklar-forms' ), 'requiredCheckbox' => __( 'Please select at least one option.', 'stacklar-forms' ), ), ); } public function shortcode_render( $atts ) { $atts = shortcode_atts( array( 'id' => 0, 'locale' => '', ), $atts ); $post_id = intval( $atts['id'] ); if ( ! $post_id ) { return ''; } $locale = function_exists( 'stacklar_multilingual' ) ? stacklar_multilingual()->normalize_locale( $atts['locale'] ) : preg_replace( '/[^A-Za-z0-9_]/', '', str_replace( '-', '_', (string) $atts['locale'] ) ); return $this->render_form_markup( $post_id, array( 'locale' => $locale ) ); } protected function render_form_markup( $post_id, array $args = array() ) { $post_id = intval( $post_id ); if ( ! $post_id ) { return ''; } $post = get_post( $post_id ); if ( ! $post || 'stacklar' !== $post->post_type || ! function_exists( 'stacklar_can_execute_form' ) || ! stacklar_can_execute_form( $post_id ) ) { return ''; } $had_previous_form_id = array_key_exists( 'stacklar_current_form_id', $GLOBALS ); $previous_form_id = $had_previous_form_id ? $GLOBALS['stacklar_current_form_id'] : null; $had_previous_locale = array_key_exists( 'stacklar_current_locale', $GLOBALS ); $previous_locale = $had_previous_locale ? $GLOBALS['stacklar_current_locale'] : null; $GLOBALS['stacklar_current_form_id'] = $post_id; if ( ! empty( $args['locale'] ) ) { $GLOBALS['stacklar_current_locale'] = (string) $args['locale']; } elseif ( $had_previous_locale ) { unset( $GLOBALS['stacklar_current_locale'] ); } $content = apply_filters( 'the_content', $post->post_content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core filter. if ( $had_previous_form_id ) { $GLOBALS['stacklar_current_form_id'] = $previous_form_id; } else { unset( $GLOBALS['stacklar_current_form_id'] ); } if ( $had_previous_locale ) { $GLOBALS['stacklar_current_locale'] = $previous_locale; } else { unset( $GLOBALS['stacklar_current_locale'] ); } // Pages that hold the shortcode enqueue this in the head (detect_frontend_assets()); // elsewhere, such as a widget, WordPress prints it with the footer styles. wp_enqueue_style( 'stacklar-style' ); return '