false, 'align' => ['left', 'center', 'right'], 'typography' => [ 'fontSize' => true, 'lineHeight' => true ], 'spacing' => [ 'margin' => true, 'padding' => true ], 'color' => [ 'text' => true, ] ]; } public static function buttonBlockSupport(): array { return [ 'html' => false, 'align' => ['left', 'center', 'right'], 'typography' => [ 'fontSize' => true, 'lineHeight' => true, 'fontWeight' => true, 'textTransform' => true, ], 'spacing' => [ 'margin' => true, 'padding' => true, ], 'color' => [ 'text' => true, 'background' => true, ], 'border' => [ 'radius' => true, 'color' => true, 'width' => true, ], 'shadow' => true, ]; } public static function register() { $self = new self(); $blocks = $self->getInnerBlocks(); foreach ($blocks as $block) { register_block_type($block['slug'], [ 'apiVersion' => 3, 'api_version' => 3, 'version' => 3, 'title' => $block['title'], 'category' => 'fluent-cart', 'parent' => array_merge($block['parent'] ?? [], [static::$parentBlock]), 'render_callback' => $block['callback'], 'supports' => Arr::get($block, 'supports', []), 'attributes' => Arr::get($block, 'attributes', []), 'uses_context' => Arr::get($block, 'uses_context', []), ]); } add_action('enqueue_block_editor_assets', function () use ($self) { $self->enqueueScripts(); }); //$self->enqueueStyles(); } public function getInnerBlocks(): array { return [ [ 'title' => __('Product Title', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-title', 'callback' => [$this, 'renderTitle'], 'component' => 'ProductTitleBlock', 'icon' => 'heading', 'supports' => static::textBlockSupport(), 'parent' => [ 'fluent-cart/shopapp-product-loop', 'fluent-cart/shopapp-product-image', 'fluent-cart/product-info' ], ], [ 'title' => __('Product Excerpt', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-excerpt', 'callback' => [$this, 'renderExcerpt'], 'component' => 'ProductExcerptBlock', 'icon' => 'editor-code', 'supports' => static::textBlockSupport(), 'parent' => [ 'fluent-cart/shopapp-product-loop', 'fluent-cart/shopapp-product-image' ], ], [ 'title' => __('Product Price', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-price', 'callback' => [$this, 'renderPrice'], 'parent' => [ 'fluent-cart/shopapp-product-loop', 'fluent-cart/shopapp-product-image', 'fluent-cart/product-info', 'core/columns' ], 'component' => 'ProductPriceBlock', 'icon' => 'editor-code', 'supports' => static::textBlockSupport(), 'uses_context' => [ 'fluent-cart/price_format' ] ], [ 'title' => __('Product Image', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-image', 'parent' => [ 'fluent-cart/shopapp-product-loop', ], 'callback' => [$this, 'renderImage'], 'component' => 'ProductImageBlock', 'icon' => 'format-image' ], [ 'title' => __('Product Button', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-buttons', 'callback' => [$this, 'renderButtons'], 'component' => 'ProductButtonBlock', 'icon' => 'admin-links', 'supports' => [ 'html' => false, 'align' => ['left', 'center', 'right'], 'typography' => [ 'fontSize' => true, 'lineHeight' => true ], 'spacing' => [ 'margin' => true, 'padding' => true ], 'color' => [ 'text' => true, ], '__experimentalBorder' => [ 'radius' => true, 'color' => true, 'width' => true, 'style' => true, ] ], 'parent' => [ 'fluent-cart/shopapp-product-loop', 'fluent-cart/shopapp-product-image' ], ], //Product container -> filter, loop [ 'title' => __('Product Container', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-container', 'callback' => [$this, 'renderProductContainer'], 'component' => 'ProductContainerBlock', 'icon' => 'screenoptions', 'parent' => ['fluent-cart/products', 'core/column', 'core/group'], 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], 'supports' => [ 'align' => ['wide', 'full'], 'html' => false, ], 'attributes' => array_merge( \WP_Block_Type_Registry::get_instance()->get_registered('core/group')->attributes, [ 'customAttr' => ['type' => 'string'], ] ) ], [ 'title' => __('Product Filter', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter', 'callback' => [$this, 'renderFilter'], 'component' => 'ProductFilterBlock', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-container'], 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], ], [ 'title' => __('Product View Switcher', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-view-switcher', 'callback' => [$this, 'renderFilterViewSwitcher'], 'component' => 'ProductFilterViewSwitcherBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/shopapp-product-action-container' ], 'uses_context' => [ 'fluent-cart/view_mode', 'fluent-cart/enable_filter', 'fluent-cart/default_filters', ] ], [ 'title' => __('Filter Sort By', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-sort-by', 'callback' => [$this, 'renderFilterSortBy'], 'component' => 'ProductFilterSortByBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/shopapp-product-action-container' ], 'uses_context' => [ 'fluent-cart/view_mode', 'fluent-cart/enable_filter', 'fluent-cart/default_filters', ] ], [ 'title' => __('Product Action Container', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-action-container', 'callback' => [$this, 'renderProductActionContainer'], 'component' => 'ProductActionContainerBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/products' ], 'uses_context' => [ 'fluent-cart/enable_filter' ] ], [ 'title' => __('Product No Result', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-no-result', 'callback' => [$this, 'renderNoResultBlock'], 'component' => 'ProductNoResultBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/products', 'core/column', 'core/group', 'fluent-cart/shopapp-product-container' ], 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], ], [ 'title' => __('Product Filter Search Box', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-search-box', 'callback' => [$this, 'renderFilterSearchBox'], 'component' => 'ProductFilterSearchBlock', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-filter'], 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ] ], [ 'title' => __('Product Filter Filters', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-filters', 'callback' => [$this, 'renderFilterFilters'], 'component' => 'ProductFilterFilters', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-filter'] ], [ 'title' => __('Product Filter Button', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-button', 'callback' => [$this, 'renderFilterButton'], 'component' => 'ProductFilterButtonBlock', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-filter'], 'uses_context' => [ 'fluent-cart/live_filter' ] ], [ 'title' => __('Product Filter Apply Button', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-apply-button', 'callback' => [$this, 'renderFilterApplyButton'], 'component' => 'ProductFilterApplyButtonBlock', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-filter-button'], 'supports' => static::textBlockSupport() ], [ 'title' => __('Product Filter Reset Button', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-filter-reset-button', 'callback' => [$this, 'renderFilterResetButton'], 'component' => 'ProductFilterResetButtonBlock', 'icon' => 'editor-code', 'parent' => ['fluent-cart/shopapp-product-filter-button'], 'supports' => static::buttonBlockSupport() ], [ 'title' => __('Product Loop', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-loop', 'callback' => [$this, 'renderProductLoop'], 'component' => 'ProductLoopBlock', 'icon' => 'screenoptions', 'parent' => ['fluent-cart/products', 'core/column', 'core/group'], 'supports' => [ 'align' => ['wide', 'full'], 'html' => false, ], 'attributes' => array_merge( \WP_Block_Type_Registry::get_instance()->get_registered('core/group')->attributes, [ 'customAttr' => ['type' => 'string'], ] ), 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], ], [ 'title' => __('Product Paginator', 'fluent-cart'), 'slug' => 'fluent-cart/product-paginator', 'callback' => [$this, 'renderProductPaginator'], 'component' => 'InnerBlocks', 'icon' => 'editor-code', 'supports' => static::textBlockSupport(), 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page' ] ], [ 'title' => __('Product Paginator Info', 'fluent-cart'), 'slug' => 'fluent-cart/product-paginator-info', 'callback' => [$this, 'renderProductPaginatorInfo'], 'parent' => [ 'fluent-cart/product-paginator' ], 'component' => 'ProductPaginatorInfoBlock', 'icon' => 'editor-code', 'supports' => static::textBlockSupport(), 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], ], [ 'title' => __('Product Paginator Number', 'fluent-cart'), 'slug' => 'fluent-cart/product-paginator-number', 'callback' => [$this, 'renderProductPaginatorNumber'], 'parent' => [ 'fluent-cart/product-paginator' ], 'component' => 'ProductPaginatorNumberBlock', 'icon' => 'editor-code', 'supports' => static::textBlockSupport(), 'uses_context' => [ 'fluent-cart/paginator', 'fluent-cart/per_page', 'fluent-cart/enable_filter', 'fluent-cart/product_box_grid_size', 'fluent-cart/view_mode', 'fluent-cart/filters', 'fluent-cart/default_filters', 'fluent-cart/order_type', 'fluent-cart/order_by', 'fluent-cart/live_filter', 'fluent-cart/price_format', 'fluent-cart/enable_wildcard_filter' ], ], [ 'title' => __('Product Loader', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-loader', 'callback' => [$this, 'renderProductLoaderBlock'], 'component' => 'ProductLoaderBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/products', 'core/column', 'core/group', 'fluent-cart/shopapp-product-container' ], ], [ 'title' => __('Product Spinner', 'fluent-cart'), 'slug' => 'fluent-cart/shopapp-product-spinner', 'callback' => [$this, 'renderProductSpinnerBlock'], 'component' => 'ProductSpinnerBlock', 'icon' => 'editor-code', 'parent' => [ 'fluent-cart/products', 'core/column', 'core/group', 'fluent-cart/shopapp-product-loader' ], ], ]; } public function getProductFromBlockContext($block) { return fluent_cart_get_current_product(); } public function renderFilter($attributes, $content, $block) { $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false); $filters = Arr::get($block->context, 'fluent-cart/filters', []); if (!$enableFilter) { return ''; } $renderer = new \FluentCart\App\Services\Renderer\ProductFilterRender($filters); $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; $innerBlocksContent .= '
'; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $instance->context['filter_renderer'] = $renderer; $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; $innerBlocksContent .= '
'; $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderFilterSortBy($attributes, $content, $block) { $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid'); $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false); $orderBy = Arr::get($block->context, 'fluent-cart/order_by', 'id'); $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC'); $allProducts = $this->getInitialProducts($block); if (!$enableFilter) { return ''; } ob_start(); (new ShopAppRenderer($allProducts, [ 'view_mode' => $viewMode, 'custom_filters' => [ 'enabled' => $enableFilter, ], 'default_filters' => [ 'order_by' => $orderBy, 'order_type' => $orderType ], 'enabled' => $enableFilter, ]))->renderSortByFilter(); return ob_get_clean(); } public function renderProductActionContainer($attributes, $content, $block) { $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderFilterViewSwitcher($attributes, $content, $block) { $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid'); $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false); $allProducts = $this->getInitialProducts($block); ob_start(); (new ShopAppRenderer($allProducts, [ 'view_mode' => $viewMode, 'custom_filters' => [ 'enabled' => $enableFilter ] ]))->renderViewSwitcherButton(); return ob_get_clean(); } public function renderFilterSearchBox($attributes, $content, $block): string { $filterRenderer = Arr::get($block->context, 'filter_renderer'); $wildcardFilter = Arr::get($block->context, 'fluent-cart/enable_wildcard_filter', false); if (!$filterRenderer || !$wildcardFilter) { return ''; } ob_start(); $filterRenderer->renderSearch(); return ob_get_clean(); } public function renderFilterFilters($attributes, $content, $block) { $filterRenderer = Arr::get($block->context, 'filter_renderer'); if (!$filterRenderer) { return ''; } ob_start(); $filterRenderer->renderOptions(); return ob_get_clean(); } public function renderFilterButton($attributes, $content, $block) { $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true); if ($liveFilter) { return ''; } $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderFilterApplyButton($attributes, $content, $block) { $wrapper_attribute = get_block_wrapper_attributes([ 'class' => 'fct-shop-apply-filter-button', ]); return sprintf('', $wrapper_attribute, esc_html__('Apply Filter', 'fluent-cart') ); } public function renderFilterResetButton($attributes, $content, $block) { $wrapper_attribute = get_block_wrapper_attributes([ 'class' => 'fct-shop-reset-filter-button', 'data-fluent-cart-shop-app-reset-button' => '', ]); return sprintf( '', $wrapper_attribute ); } public function renderNoResultBlock($attributes, $content, $block) { $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderProductLoaderBlock($attributes, $content, $block) { $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderTitle($attributes, $content, $block): string { $product = $this->getProductFromBlockContext($block); $isLink = Arr::get($attributes, 'isLink', true); $target = Arr::get($attributes, 'linkTarget', '_self'); if (empty($product)) { return 'not found'; } $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => 'fct-product-card-title wc-block-grid__product-title', ] ); $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product); ob_start(); $render->renderTitle($wrapper_attributes, [ 'isLink' => $isLink, 'target' => $target, ]); return ob_get_clean(); } public function renderPrice($attributes, $content, $block): string { $product = $this->getProductFromBlockContext($block); $defaultPriceFormat = 'starts_from'; if (is_singular('fluent-products') && TemplateService::getCurrentFcPageType() === 'single_product') { $defaultPriceFormat = 'range'; } $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', $defaultPriceFormat); if (empty($product)) { return ''; } $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => 'fct-product-card-prices', ] ); $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product, [ 'price_format' => $priceFormat, ]); ob_start(); $render->renderPrices($wrapper_attributes); return ob_get_clean(); } public function renderImage($attributes, $content, $block): string { $product = $this->getProductFromBlockContext($block); if (empty($product)) { return ''; } $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product); $renderedImage = ''; $innerBlocksContent = ''; if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; $innerBlocksContent .= '
'; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } ob_start(); $render->renderProductImage(); $renderedImage = ob_get_clean(); if (!empty($innerBlocksContent)) { return sprintf( "
%s
%s
", $renderedImage, $innerBlocksContent ); } return $renderedImage; } public function renderButtons($attributes, $content, $block): string { $product = $this->getProductFromBlockContext($block); if (empty($product)) { return ''; } $wrapper_attributes = get_block_wrapper_attributes(); $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product); ob_start(); $render->showBuyButton($wrapper_attributes); return ob_get_clean(); } public function renderExcerpt($attributes, $content, $block): string { $product = $this->getProductFromBlockContext($block); if (empty($product) || empty($product->post_excerpt)) { return ''; } $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => 'fct-product-card-excerpt', ] ); $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product); ob_start(); $render->renderExcerpt($wrapper_attributes); return ob_get_clean(); } public function renderProductLoop($attributes, $content, $block): string { $lastChanged = Arr::get($attributes,'last_changed'); $clientId = Arr::get($attributes, 'wp_client_id', ''); if ($clientId) { $blockId = 'fct_product_loop_client_' . $attributes['wp_client_id']; $existingTransient = get_transient($blockId); $needsReset = false; if (empty($existingTransient) || !is_array($existingTransient)) { $needsReset = true; } else { // Check if last_changed has changed if (!empty($lastChanged) && $lastChanged !== $existingTransient['last_changed']) { $needsReset = true; } else { // Check age of the transient $setAt = strtotime($existingTransient['set_at'] ?? ''); if ($setAt && (time() - $setAt) > (4 * DAY_IN_SECONDS)) { $needsReset = true; } } } if ($needsReset || true) { $raw_block_markup = $block->parsed_block['blockName'] ? serialize_block($block->parsed_block) : ''; $newTransient = [ 'set_at' => gmdate('Y-m-d H:i:s'), 'last_changed' => $lastChanged, 'markup' => $raw_block_markup, ]; // Store for 7 days set_transient($blockId, $newTransient, 7 * DAY_IN_SECONDS); } } $products = $this->getInitialProducts($block); $products = $products['products']; $parsed = ''; if ($products instanceof CursorPaginator) { $parsed = wp_parse_args(wp_parse_url($products->nextPageUrl(), PHP_URL_QUERY)); } ProductDataSetup::setProductsCache($products); $innerBlocksContent = ''; $blockContext = $block->context; foreach ($products as $key => $product) { setup_postdata($product->ID); if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { if ($key === 0) { //add attribute provider with value wp-block $innerBlocksContent .= '
'; } else { $innerBlocksContent .= '
'; } foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } } wp_reset_postdata(); if ($products->count() === 0) { add_action('wp_footer', function () { ?> context, 'fluent-cart/enable_filter', false); $ff = Arr::get($block->context, 'fluent-cart/filters', []); $paginator = Arr::get($block->context, 'fluent-cart/paginator', 'scroll'); $productBoxSize = Arr::get($block->context, 'fluent-cart/product_box_grid_size', 4); $perPage = Arr::get($block->context, 'fluent-cart/per_page', 10); $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC'); $orderBy = Arr::get($block->context, 'fluent-cart/order_by', 'id'); $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true); $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', 'starts_from'); $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid'); $defaultFilters = Arr::get($block->context, 'fluent-cart/default_filters', []); $innerBlocksContent = ''; if (is_main_query()) { $taxonomy = TemplateLoader::$currentTaxonomy; if ($taxonomy) { $defaultFilters = [ $taxonomy->taxonomy => [$taxonomy->term_id], 'enabled' => true ]; } } Vite::enqueueStyle( 'fluentcart-add-to-cart-btn-css', 'public/buttons/add-to-cart/style/style.scss' ); Vite::enqueueStyle( 'fluentcart-direct-checkout-btn-css', 'public/buttons/direct-checkout/style/style.scss' ); Vite::enqueueStyle( 'fluentcart-single-product-page-css', 'public/single-product/single-product.scss', ); Vite::enqueueStyle( 'fluentcart-zoom-css', 'public/single-product/xzoom/xzoom.css', ); Vite::enqueueStyle( 'fluentcart-similar-product-page-css', 'public/single-product/similar-product.scss', ); Vite::enqueueStyle( 'fluentcart-product-card-page-css', 'public/product-card/style/product-card.scss', ); Vite::enqueueStyle( 'fluent-cart-sold-out-badge', 'admin/BlockEditor/SoldOutBadge/style/sold-out-badge-block-editor.scss' ); Vite::enqueueStyle( 'fluent-cart-sale-badge', 'admin/BlockEditor/SaleBadge/style/sale-badge-block-editor.scss' ); Vite::enqueueScript( 'fluentcart-zoom-js', 'public/single-product/xzoom/xzoom.js', [] ); Vite::enqueueScript( 'fluentcart-single-product-page-js', 'public/single-product/SingleProduct.js', [] )->with([ 'fluentcart_single_product_vars' => [ 'trans' => TransStrings::singleProductPageString(), 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []), // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')), 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_text', __('Not Available', 'fluent-cart'), []), 'in_stock_status' => Helper::IN_STOCK, 'out_of_stock_status' => Helper::OUT_OF_STOCK, 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product'), 'enable_image_zoom_in_modal' => (new StoreSettings())->get('enable_image_zoom_in_modal') ] ]); if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $blockContext = $block->context; // start wrapper $innerBlocksContent .= sprintf( '
', ($viewMode === 'list' ? 'mode-list' : 'mode-grid'), (!$isFilterEnabled ? ' fct-full-container-width' : ''), esc_attr($perPage), esc_attr($orderType), esc_attr($orderBy), esc_attr($liveFilter), esc_attr($priceFormat), esc_attr($paginator), esc_attr(wp_json_encode($defaultFilters)) ); foreach ($block->inner_blocks as $inner_block) { if ('fluent-cart/shopapp-product-filter' === $inner_block->name) { if (!$isFilterEnabled) { continue; } } $isProductLoop = 'fluent-cart/shopapp-product-loop' === $inner_block->name; if ($isProductLoop) { $styleAttr = $productBoxSize ? ' style="--grid-columns: ' . esc_attr($productBoxSize) . ';"' : ''; $innerBlocksContent .= '
'; } if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } if ($isProductLoop) { $innerBlocksContent .= '
'; } } // close wrapper $innerBlocksContent .= '
'; } return $innerBlocksContent; } protected function getLocalizationKey(): string { return 'fluent_cart_products_inner_blocks'; } public function localizeData(): array { return [ $this->getLocalizationKey() => [ 'blocks' => Arr::except($this->getInnerBlocks(), ['callback']), ], 'fluentcart_single_product_vars' => [ 'trans' => TransStrings::singleProductPageString(), 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []), // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')), 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_text', __('Not Available', 'fluent-cart'), []), 'in_stock_status' => Helper::IN_STOCK, 'out_of_stock_status' => Helper::OUT_OF_STOCK, 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product'), 'enable_image_zoom_in_modal' => (new StoreSettings())->get('enable_image_zoom_in_modal') ] ]; } public function getStyles(): array { return ['public/single-product/single-product.scss']; } public function getScripts(): array { $scripts = [ [ 'source' => 'admin/BlockEditor/ReactSupport.js', 'dependencies' => ['wp-blocks', 'wp-components'] ], [ 'source' => 'admin/BlockEditor/ShopApp/InnerBlocks/InnerBlocks.jsx', 'dependencies' => ['wp-blocks', 'wp-components', 'wp-block-editor'] ], // [ // 'source'=> 'public/single-product/SingleProduct.js', // 'dependencies' => [] // ] ]; if (App::isDevMode() || true) { $scripts[] = [ 'source' => 'admin/BlockEditor/ReactSupport.js', 'dependencies' => ['wp-blocks', 'wp-components'] ]; } return $scripts; } protected function generateEnqueueSlug(): string { return 'fluent_cart_shop_app_inner_blocks'; } public function renderProductPaginator($attributes, $content, $block) { $paginatorType = Arr::get($block->context, 'fluent-cart/paginator', 'scroll'); // $paginatorType = 'numbers'; $innerBlocksContent = ''; if ($paginatorType === 'scroll') { return $innerBlocksContent; } if ($block instanceof \WP_Block && !empty($block->inner_blocks)) { $innerBlocksContent .= '
'; $blockContext = $block->context; foreach ($block->inner_blocks as $inner_block) { if (isset($inner_block->parsed_block)) { $innerContext = array_merge($inner_block->context, $blockContext); $instance = new \WP_Block($inner_block->parsed_block, $innerContext); $innerBlocksContent .= $instance->render(); } } $innerBlocksContent .= '
'; } return $innerBlocksContent; } public function renderProductPaginatorInfo($attributes, $content, $block) { $allProducts = $this->getInitialProducts($block); $products = $allProducts['products']; if($products instanceof CursorPaginator){ return ''; } // $total = $allProducts['total']; $page = Arr::get($block->context, 'fluent-cart/per_page', 10); $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC'); $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID'); $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => 'fct-shop-paginator-results wc-block-grid__fluent-cart-shop-app-paginator-results', ] ); ob_start(); (new ShopAppRenderer($allProducts, [ 'default_filters' => [ 'per_page' => $page, 'sort_type' => $orderType, 'sort_by' => $orderBy, ] ]))->renderPaginatorResultWrapper($wrapper_attributes); return ob_get_clean(); } public function renderProductPaginatorNumber($attributes, $content, $block) { $allProducts = $this->getInitialProducts($block); $products = $allProducts['products']; if($products instanceof CursorPaginator){ return ''; } $currentPage = Arr::get(App::request()->all(), 'current_page', 1); $perPage = $products->perPage(); $total = $allProducts['total']; $page = Arr::get($block->context, 'fluent-cart/per_page', 10); $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC'); $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID'); ob_start(); (new ShopAppRenderer($allProducts, [ 'default_filters' => [ 'per_page' => $page, 'sort_type' => $orderType, 'sort_by' => $orderBy, ] ]))->renderPerPageSelector(); return ob_get_clean(); $lastPage = $total / $perPage; if ($total % $perPage > 0) { $lastPage++; } $lastPage = intval($lastPage); $paginator = [ 'currentPage' => $currentPage, 'lastPage' => $lastPage ]; $html = $this->renderPaginatorItems($paginator); return $html; } private function renderPaginatorItems($paginator, $paginationJump = 3) { if (!$paginator || !isset($paginator['lastPage'], $paginator['currentPage'])) { return ''; } $totalPages = (int)$paginator['lastPage']; $currentPage = (int)$paginator['currentPage']; $jumpSize = (int)$paginationJump; $svgLeft = ''; $svgRight = ''; $html = ''; // Helper to build button $createButton = function ($label, $page, $isActive = false, $isArrow = false) { $classes = []; if ($isActive) $classes[] = 'active'; if ($isArrow) $classes[] = 'arrow'; $classAttr = 'pager-number '; if ($classes) { $classAttr .= implode(' ', $classes); } return '
  • ' . $label . '
  • '; }; // ← Previous if ($currentPage > 1) { $html .= $createButton($svgLeft, $currentPage - 1, false, true); } // First page $html .= $createButton('1', 1, $currentPage == '1'); // « Jump back if ($currentPage > $jumpSize + 1) { $jumpBack = max(2, $currentPage - $jumpSize); $html .= $createButton('«', $jumpBack, false, true); } // Pages before current for ($i = $currentPage - 2; $i < $currentPage; $i++) { if ($i > 1) { $html .= $createButton($i, $i); } } // Current page if ($currentPage !== 1 && $currentPage !== $totalPages) { $html .= $createButton($currentPage, $currentPage, true); } // Pages after current for ($i = $currentPage + 1; $i <= $currentPage + 2; $i++) { if ($i < $totalPages) { $html .= $createButton($i, $i); } } // » Jump forward if ($currentPage < $totalPages - $jumpSize) { $jumpForward = min($totalPages - 1, $currentPage + $jumpSize); $html .= $createButton('»', $jumpForward, false, true); } // Last page if ($totalPages > 1) { $html .= $createButton($totalPages, $totalPages, $currentPage === $totalPages); } // → Next if ($currentPage < $totalPages) { $html .= $createButton($svgRight, $currentPage + 1, false, true); } return ''; } public function getInitialProducts($block = null) { $request = App::request()->all(); $paginator = Arr::get($block->context, 'fluent-cart/paginator', 'scroll'); if (App::request()->has('current_page')) { $paginator = 'numbers'; } $perPage = Arr::get($block->context, 'fluent-cart/per_page', 10); $perPage = Arr::get($request, 'per_page', $perPage); $defaultFilters = Arr::get($block->context, 'fluent-cart/default_filters', []); $defaultFilters = Arr::get($request, 'default_filters', $defaultFilters); $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC'); $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID'); $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true); $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', 'starts_from'); $priceFormat = Arr::get($request, 'price_format', $priceFormat); $wildCardFilter = Arr::get($block->context, 'fluent-cart/enable_wildcard_filter', false); $currentPage = Arr::get(App::request()->all(), 'current_page', 1); $paginatorMethod = $paginator === 'numbers' ? 'simple' : 'cursor'; $defaultFilterEnable = Arr::get($defaultFilters, 'enabled', false) ? true : false; $allowOutOfStock = ($defaultFilterEnable === true && Arr::get($defaultFilters, 'allow_out_of_stock', false) === true) || Arr::get($request, 'allow_out_of_stock', false) == true; if (!$defaultFilterEnable) { $defaultFilters = []; } $urlFilters = Arr::get(App::request()->all(), 'filters', []); $status = ["post_status" => ["column" => "post_status", "operator" => "in", "value" => ["publish"]]]; $urlTerms = Helper::parseTermIdsForFilter($urlFilters); $defaultTerms = Helper::parseTermIdsForFilter($defaultFilters); $mergedTerms = Helper::mergeTermIdsForFilter($defaultTerms, $urlTerms); $mergedTerms = apply_filters('fluent_cart/shop_app_product_query_taxonomy_filters', $mergedTerms, [ 'default_terms' => $defaultTerms, 'url_terms' => $urlTerms, 'url_filters' => $urlFilters, 'default_filters' => $defaultFilters, 'block' => $block, 'is_main_query' => is_main_query(), ]); if (is_main_query()) { $taxonomy = TemplateLoader::$currentTaxonomy; if ($taxonomy) { $mergedTerms = [ $taxonomy->taxonomy => [$taxonomy->term_id], ]; } } $params = [ "select" => '*', "with" => ['detail', 'variants', 'categories', 'licensesMeta'], "selected_status" => true, "status" => $status, "shop_app_default_filters" => $defaultFilters, "taxonomy_filters" => $mergedTerms, "paginate" => $perPage, "per_page" => $perPage, "page" => $currentPage, 'filters' => $urlFilters, 'paginate_using' => $paginatorMethod, 'allow_out_of_stock' => $allowOutOfStock, 'order_type' => $orderType, 'order_by' => $orderBy, 'live_filter' => $liveFilter ]; $products = ShopResource::get($params); return [ 'products' => ($products['products']->setCollection( $products['products']->getCollection()->transform(function ($product) { $product->setAppends(['view_url', 'has_subscription']); return $product; }) )), 'total' => $products['total'] ]; } }