context['query']['objekaRelated'] ) || str_contains( $block->parsed_block['attrs']['className'] ?? '', 'objeka-related-products' ); if ( ! is_singular( 'product' ) || ! $is_related ) { return $query; } $product = wc_get_product( get_queried_object_id() ); if ( ! $product ) { return $query; } $bump = absint( $product->get_meta( '_objeka_bump_id' ) ); $related = wc_get_related_products( $product->get_id(), 4 ); $other = wc_get_products( array( 'status' => 'publish', 'limit' => 5, 'return' => 'ids', ) ); $ids = array_filter( array_unique( array_merge( array( $bump ), $related, $other ) ) ); $query['post__in'] = array_slice( array_diff( $ids, array( $product->get_id() ) ), 0, 4 ); $query['post__in'] = $query['post__in'] ? $query['post__in'] : array( 0 ); $query['orderby'] = 'post__in'; return $query; } /** Register metadata; templates can be rearranged in the Site Editor. */ public static function register() { foreach ( array( 'product-brief', 'product-story', 'product-policies', 'product-offer' ) as $name ) { register_block_type( OBJEKA_TOOLKIT_DIR . 'blocks/' . $name, array( 'render_callback' => array( __CLASS__, 'render' ) ) ); } } /** * Read an original demo profile without affecting unrelated products. * * @param WC_Product $product Product being displayed. * @return array Structured editorial profile or an empty array. */ public static function profile( $product ) { static $profiles = null; if ( null === $profiles ) { $file = OBJEKA_TOOLKIT_DIR . 'data/editorial.json'; $profiles = file_exists( $file ) ? json_decode( file_get_contents( $file ), true ) : array(); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Bundled local manifest. } $key = $product->get_meta( '_objeka_demo_key' ); $profile = $profiles[ $key ] ?? array(); $custom = $product->get_meta( '_objeka_editorial' ); if ( ! $profile && ! $custom ) { return array(); } $profile = array_replace( array( 'name' => $product->get_name(), 'kind' => __( 'Product details', 'objeka-toolkit' ), 'category' => __( 'The collection', 'objeka-toolkit' ), 'tone' => 'neutral', 'accent' => '#2448ef', 'intro' => '', 'promise' => $product->get_name(), 'lead' => wp_strip_all_tags( $product->get_description() ), 'caption' => __( 'A closer look.', 'objeka-toolkit' ), 'scene' => $product->get_name(), 'benefits' => array(), 'steps' => array(), 'specs' => array(), 'box' => array(), 'compare' => array(), 'faq' => array(), ), $profile ); if ( is_array( $custom ) ) { $profile = array_replace( $profile, $custom ); } if ( ! $profile ) { return array(); } if ( $product->get_short_description() ) { $profile['intro'] = wp_strip_all_tags( $product->get_short_description() ); } $specs = $product->get_meta( '_objeka_specs' ); if ( $specs ) { $rows = array(); foreach ( explode( "\n", $specs ) as $line ) { $pair = array_map( 'trim', explode( ':', $line, 2 ) ); if ( 2 === count( $pair ) && $pair[0] && $pair[1] ) { $rows[] = $pair; } } if ( $rows ) { $profile['specs'] = $rows; } } return $profile; } /** * Render a section using actual product data and clearly labeled concept specs. * * @param array $attributes Block attributes. * @param string $content Saved content. * @param WP_Block $block Current block context. * @return string Escaped frontend markup. */ public static function render( $attributes, $content, $block ) { if ( ! function_exists( 'wc_get_product' ) ) { return ''; } if ( 'objeka-toolkit/product-offer' === $block->name ) { return self::offer( $attributes, $block ); } $product = wc_get_product( $block->context['postId'] ?? get_the_ID() ); if ( ! $product ) { return ''; } $profile = self::profile( $product ); ob_start(); if ( 'objeka-toolkit/product-brief' === $block->name && $profile ) { ?>
'objeka-product-brief', 'data-product-direction' => $profile['tone'], ) ); ?> >

name ) { ?>
'objeka-purchase-policies' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WordPress escapes wrapper attributes. ?>>

·

name && $profile ) { $section = sanitize_key( $attributes['section'] ?? 'all' ); $parts = array( 'navigation', 'manifesto', 'scene', 'features', 'how', 'specifications', 'box', 'faq', 'final' ); $layout = Objeka_Toolkit_Editorial::layout( $product ); $gallery = $product->get_gallery_image_ids(); $scene = $gallery ? $gallery[0] : $product->get_image_id(); if ( 'all' !== $section && ! in_array( $section, $parts, true ) ) { return ''; } ?>
'objeka-product-experience objeka-experience-' . $layout . ' objeka-part-' . $section, 'style' => '--objeka-product-accent:' . sanitize_hex_color( $profile['accent'] ?? '#2448ef' ), ) ); ?> >
context['postId'] ?? get_the_ID() ) ); if ( ! $offer || ( 'publish' !== $offer->get_status() && ! current_user_can( 'edit_post', $offer->get_id() ) ) ) { return ''; } $mode = 'purchase' === ( $attributes['mode'] ?? 'summary' ) ? 'purchase' : 'summary'; $ready = $offer->is_purchasable() && $offer->is_in_stock(); $anchor = sanitize_html_class( $attributes['targetAnchor'] ?? 'forma-buy' ); ob_start(); ?>
'objeka-product-offer objeka-offer-' . $mode ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WordPress escapes supported wrapper attributes. ?>>

get_price_html() ); ?>

is_in_stock() ? __( 'In stock', 'objeka-toolkit' ) : __( 'Currently unavailable', 'objeka-toolkit' ) ); ?>

is_type( 'simple' ) ) { global $product; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce requires its own product global for native purchasing. $previous_product = $product; $product = $offer; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Scoped WooCommerce global, restored in finally. try { woocommerce_template_single_add_to_cart(); } finally { $product = $previous_product; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Restore WooCommerce's required global after rendering. } } else { ?>