PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.4.0
ShopBuilder – WooCommerce Builder For Elementor v3.4.0
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
shopbuilder / app / Controllers / Hooks / BuilderHooks.php

BuilderHooks.php in ShopBuilder – WooCommerce Builder For Elementor 3.4.0, at app/Controllers/Hooks/BuilderHooks.php

360 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main initialization class.
4 *
5 * @package RadiusTheme\SB
6 */
7
8 namespace RadiusTheme\SB\Controllers\Hooks;
9
10 defined( 'ABSPATH' ) || exit();
11
12 use RadiusTheme\SB\Helpers\Fns;
13 use RadiusTheme\SB\Helpers\BuilderFns;
14 use RadiusTheme\SB\Helpers\ElementorDataMap;
15 use RadiusTheme\SB\Models\TemplateSettings;
16 use RadiusTheme\SB\Traits\SingletonTrait;
17
18 /**
19 * Builder Frontend.
20 */
21 class BuilderHooks {
22 /**
23 * Builder page id.
24 *
25 * @var integer
26 */
27 private $builder_page_id = 0;
28
29 /**
30 * Page Edit By.
31 *
32 * @var string
33 */
34 private $page_edit_with = '';
35
36 /**
37 * Internal cache.
38 *
39 * @var array
40 */
41 private static $cache = [];
42
43 /**
44 * Singleton.
45 */
46 use SingletonTrait;
47
48 /**
49 * Final constructor.
50 */
51 private function __construct() {
52 // Template Redirect hook run after the global query.
53 add_action( 'template_redirect', [ $this, 'frontend_init' ] );
54 add_filter( 'rtsb/builder/set/current/page/type', [ $this, 'builder_page_type' ] );
55 add_filter( 'body_class', [ $this, 'product_page_body_class' ] );
56 add_action( 'pre_get_posts', [ $this, 'override_archive_posts_per_page' ] );
57 }
58
59 /**
60 * Override posts_per_page for shop/archive pages based on widget setting.
61 *
62 * @param \WP_Query $query The WP_Query instance.
63 *
64 * @return void
65 */
66 public function override_archive_posts_per_page( $query ) {
67 if ( is_admin() || ! $query->is_main_query() ) {
68 return;
69 }
70
71 if ( ! ( is_shop() || is_product_taxonomy() ) ) {
72 return;
73 }
74
75 $type = is_shop() ? 'shop' : 'archive';
76 $option_name = BuilderFns::option_name( $type );
77 $builder_page_id = TemplateSettings::instance()->get_option( $option_name );
78
79 if ( empty( $builder_page_id ) ) {
80 $option_name = BuilderFns::option_name( $type, true );
81 $builder_page_id = TemplateSettings::instance()->get_option( $option_name );
82 }
83
84 // Fallback: use shop template for archive pages when no archive template exists.
85 if ( empty( $builder_page_id ) && 'archive' === $type ) {
86 $option_name = BuilderFns::option_name( 'shop' );
87 $builder_page_id = TemplateSettings::instance()->get_option( $option_name );
88
89 if ( empty( $builder_page_id ) ) {
90 $option_name = BuilderFns::option_name( 'shop', true );
91 $builder_page_id = TemplateSettings::instance()->get_option( $option_name );
92 }
93 }
94
95 if ( empty( $builder_page_id ) ) {
96 return;
97 }
98
99 $per_page = $this->get_archive_widget_per_page( $builder_page_id );
100
101 if ( $per_page ) {
102 $query->set( 'posts_per_page', $per_page );
103 }
104 }
105
106 /**
107 * Get posts_per_page from the ProductsArchive widget in Elementor data.
108 *
109 * @param int $post_id Builder page ID.
110 *
111 * @return int|false The per page value or false if not set.
112 */
113 private function get_archive_widget_per_page( $post_id ) {
114 $cache_key = 'rtsb_archive_per_page_' . $post_id;
115
116 if ( isset( self::$cache[ $cache_key ] ) ) {
117 return self::$cache[ $cache_key ];
118 }
119
120 // Check default layout widget first.
121 $widgets = ElementorDataMap::instance()->get_widget_data( 'rtsb-products-archive', null, $post_id );
122
123 if ( ! empty( $widgets ) ) {
124 $widget = reset( $widgets );
125 $per_page = isset( $widget['settings']['posts_per_page'] ) && '' !== $widget['settings']['posts_per_page']
126 ? absint( $widget['settings']['posts_per_page'] )
127 : false;
128
129 self::$cache[ $cache_key ] = $per_page;
130
131 return $per_page;
132 }
133
134 // Check custom layout widget.
135 $widgets = ElementorDataMap::instance()->get_widget_data( 'rtsb-products-archive-custom', null, $post_id );
136
137 if ( ! empty( $widgets ) ) {
138 $widget = reset( $widgets );
139 $per_page = isset( $widget['settings']['posts_per_page'] ) && '' !== $widget['settings']['posts_per_page']
140 ? absint( $widget['settings']['posts_per_page'] )
141 : false;
142
143 self::$cache[ $cache_key ] = $per_page;
144
145 return $per_page;
146 }
147
148 self::$cache[ $cache_key ] = false;
149
150 return false;
151 }
152
153 /**
154 * Builder page Body class.
155 *
156 * @param array $classes class list.
157 * @return array.
158 */
159 public function product_page_body_class( $classes ) {
160 $classes[] = 'rtsb-shopbuilder-plugin rtsb-shopbuilder-v-' . RTSB_VERSION . ' rtsb_theme_' . rtsb()->current_theme;
161
162 if ( defined( 'RTSBPRO_VERSION' ) ) {
163 $classes[] = 'rtsb-shopbuilder-pro-v-' . RTSBPRO_VERSION;
164 }
165 if ( Fns::is_woocommerce() || BuilderFns::is_builder_preview() ) {
166 $classes[] = 'woocommerce-page';
167 }
168
169 if ( BuilderFns::is_product() ) {
170 $classes[] = 'single-product';
171 }
172
173 if ( BuilderFns::is_shop() ) {
174 $classes[] = 'woocommerce-shop';
175 }
176
177 if ( BuilderFns::is_cart() ) {
178 $classes[] = 'woocommerce-cart';
179 } else {
180 $classes[] = 'woocommerce';
181 }
182
183 if ( BuilderFns::is_checkout() ) {
184 $classes[] = 'woocommerce-checkout ';
185 }
186
187 if ( rtsb()->has_pro() ) {
188 $disable_cart = Fns::is_guest_feature_disabled( 'hide_cart_page', '' );
189 $disable_checkout = Fns::is_guest_feature_disabled( 'hide_checkout_page', '' );
190
191 if ( $disable_cart || $disable_checkout ) {
192 $classes[] = 'rtsb-not-visible';
193 }
194 }
195
196 $hide_notification = Fns::get_option( 'general', 'notification', 'hide_notification', '' );
197
198 if ( 'on' === $hide_notification ) {
199 $classes[] = 'rtsb-notifications-hidden';
200 }
201
202 return $classes;
203 }
204 /**
205 * Undocumented function
206 *
207 * @return void
208 */
209 public function frontend_init() {
210 $this->builder_page_id = BuilderFns::builder_page_id_by_page();
211 $this->page_edit_with = Fns::page_edit_with( $this->builder_page_id );
212 if ( $this->builder_page_id ) {
213 if ( did_action( 'elementor/loaded' ) && 'elementor' === $this->page_edit_with ) {
214 $this->elementor_frontend();
215 } elseif ( 'gutenberg' === $this->page_edit_with ) {
216 $this->gutenberg_frontend();
217 }
218 }
219 }
220 /**
221 * Appl for gutenberg.
222 *
223 * @return void
224 */
225 public function elementor_frontend() {
226 // Priority 15: after Elementor registers style handles (priority 5) and before
227 // its enqueue_styles / after_enqueue_post_styles pass (priority 20) that renders
228 // the atomic-widget (V4) styles collected via the elementor/post/render action.
229 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_elementor_template_css' ], 15 );
230 add_action( 'rtsb/builder/before/header', [ $this, 'el_body_class' ] );
231 add_action( 'rtsb/builder/template/main/content', [ $this, 'elementor_template_main_content' ] );
232 }
233
234 /**
235 * Enqueue the Elementor builder template CSS in the document head.
236 *
237 * ShopBuilder renders the Elementor template on a WooCommerce object (e.g. a product),
238 * so Elementor's own wp_enqueue_scripts pass only fires elementor/post/render for the
239 * queried object — never for the builder template. As a result the template's
240 * document CSS (post-{id}.css) and, on Elementor V4, its atomic-widget styles
241 * (local-{id}-*.css rendered on elementor/frontend/after_enqueue_post_styles) are
242 * never loaded on builder pages although they load on Elementor's own preview.
243 *
244 * Firing elementor/post/render for the template id here — before Elementor's
245 * priority-20 style pass — registers the template with the atomic styles manager so
246 * its styles are generated and enqueued. enqueue_styles() loads the classic post CSS.
247 *
248 * @return void
249 */
250 public function enqueue_elementor_template_css() {
251 if ( ! $this->builder_page_id || ! class_exists( '\Elementor\Plugin' ) ) {
252 return;
253 }
254
255 $documents = \Elementor\Plugin::$instance->documents;
256
257 if ( ! $documents ) {
258 return;
259 }
260
261 $document = $documents->get( $this->builder_page_id );
262
263 if ( ! $document ) {
264 return;
265 }
266
267 // Register the template with Elementor's render pipeline (atomic V4 styles manager
268 // collects these post ids and renders their styles on after_enqueue_post_styles).
269 do_action( 'elementor/post/render', $this->builder_page_id );
270
271 // Classic per-post document CSS (post-{id}.css) and font dependencies.
272 if ( method_exists( $document, 'enqueue_styles' ) ) {
273 $document->enqueue_styles();
274 }
275 }
276
277 /**
278 * Appl for Elementor.
279 *
280 * @return void
281 */
282 public function gutenberg_frontend() {
283 add_action( 'rtsb/builder/template/main/content', [ $this, 'gutenberg_template_main_content' ] );
284 }
285 /**
286 * Apply For the supported builder page.
287 *
288 * @param string $type string.
289 * @return string
290 */
291 public function builder_page_type( $type ) {
292
293 if ( BuilderFns::is_shop() ) {
294 $type = 'shop';
295 } elseif ( BuilderFns::is_archive() ) {
296 $type = 'archive';
297 } elseif ( BuilderFns::is_product() ) {
298 $type = 'product';
299 } elseif ( BuilderFns::is_checkout() ) {
300 $type = 'checkout';
301 wp_enqueue_script( 'wc-checkout' );
302 } elseif ( BuilderFns::is_cart() ) {
303 $type = 'cart';
304 }
305 wp_enqueue_style( Fns::optimized_handle( 'rtsb-frontend' ) );
306 wp_enqueue_script( Fns::optimized_handle( 'rtsb-public' ) );
307
308 return apply_filters( 'rtsb/builder/set/current/page/type/external', $type );
309 }
310
311 /**
312 * Appl for Elementor.
313 *
314 * @return void
315 */
316 public function el_body_class() {
317 if ( ! class_exists( '\Elementor\Plugin' ) || empty( \Elementor\Plugin::$instance->frontend ) ) {
318 return;
319 }
320
321 $page_template = get_post_meta( $this->builder_page_id, '_wp_page_template', true );
322
323 if ( 'elementor_canvas' === $page_template ) {
324 \Elementor\Plugin::$instance->frontend->add_body_class( 'elementor-template-canvas' );
325 } elseif ( 'elementor_header_footer' === $page_template ) {
326 \Elementor\Plugin::$instance->frontend->add_body_class( 'elementor-template-full-width' );
327 }
328 }
329
330 /**
331 * Apply for Elementor.
332 *
333 * @return void
334 */
335 public function elementor_template_main_content() {
336 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
337 echo BuilderFns::get_builder_content( $this->builder_page_id );
338 }
339 /**
340 * Apply for Gutenberg.
341 *
342 * @return void
343 */
344 public function gutenberg_template_main_content() {
345 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
346 $output = '';
347 $content = get_the_content( null, false, $this->builder_page_id );
348 if ( has_blocks( $content ) ) {
349 $blocks = parse_blocks( $content );
350 foreach ( $blocks as $block ) {
351 $output .= render_block( $block );
352 }
353 } else {
354 $content = apply_filters( 'the_content', $content );
355 $output = str_replace( ']]>', ']]&gt;', $content );
356 }
357 echo wp_kses_post( $output );
358 }
359 }
360