PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.4.2
ShopBuilder – WooCommerce Builder For Elementor v3.4.2
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
← All changes | app/Controllers/BuilderController.php +135 -67 2.2.23.4.2 View file →
@@ -8,14 +8,14 @@
8 8 namespace RadiusTheme\SB\Controllers;
9 9
10 10 use RadiusTheme\SB\Helpers\Fns;
11 11 use RadiusTheme\SB\Helpers\BuilderFns;
12 +use RadiusTheme\SB\Models\ElementList;
12 13 use RadiusTheme\SB\Traits\SingletonTrait;
13 14 use RadiusTheme\SB\Controllers\Builder\BuilderCpt;
14 15 use RadiusTheme\SB\Controllers\Hooks\BuilderHooks;
15 16 use RadiusTheme\SB\Elementor\Controls\ImageSelectorControl;
16 17 use RadiusTheme\SB\Elementor\Controls\Select2AjaxControl;
17 -use RadiusTheme\SB\Models\ElementList;
18 18
19 19 // Do not allow directly accessing this file.
20 20 if ( ! defined( 'ABSPATH' ) ) {
21 21 exit( 'This script cannot be accessed directly.' );
@@ -54,22 +54,18 @@
54 54 */
55 55 private function __construct() {
56 56 $this->builder_page_id = BuilderFns::builder_page_id_by_page();
57 57 $this->page_edit_with = Fns::page_edit_with( $this->builder_page_id );
58 -
59 58 if ( ! is_admin() ) {
60 59 BuilderHooks::instance();
61 60 }
62 61
63 62 BuilderCpt::instance();
64 -
65 - $this->elementor_init();
66 - $this->both_builder_frontend();
67 -
68 - // add_action( 'elementor/init', [ $this, 'elementor_init' ] );
69 - add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'editor_scripts' ] );
70 - // add_action( 'template_redirect', [ $this, 'both_builder_frontend' ], 99 );
71 -
63 + if ( defined( 'ELEMENTOR_VERSION' ) ) {
64 + $this->elementor_init();
65 + $this->both_builder_frontend();
66 + add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'editor_scripts' ] );
67 + }
72 68 // RT Select2 Ajax.
73 69 add_action( 'wp_ajax_rtsb_select2_object_search', [ $this, 'select2_ajax_posts_filter_autocomplete' ] );
74 70 add_action( 'wp_ajax_nopriv_rtsb_select2_object_search', [ $this, 'select2_ajax_posts_filter_autocomplete' ] );
75 71 // Select2 ajax save data.
@@ -92,13 +88,20 @@
92 88 *
93 89 * @return void
94 90 */
95 91 public function elementor_init() {
96 - add_action( 'elementor/elements/categories_registered', [ $this, 'add_elementor_widget_categories' ] );
97 - add_action( 'elementor/widgets/register', [ $this, 'init_widgets' ] );
98 - add_action( 'elementor/controls/register', [ $this, 'init_controls' ] );
99 - add_filter( 'elementor/editor/localize_settings', [ $this, 'promotePremiumWidgets' ] );
100 - add_action( 'elementor/icons_manager/additional_tabs', [ $this, 'rtsb_icons' ] );
92 + add_action(
93 + 'init',
94 + function () {
95 + if ( Fns::should_load_elementor_scripts() ) {
96 + add_action( 'elementor/elements/categories_registered', [ $this, 'add_elementor_widget_categories' ] );
97 + add_action( 'elementor/widgets/register', [ $this, 'init_widgets' ] );
98 + add_action( 'elementor/controls/register', [ $this, 'init_controls' ] );
99 + add_filter( 'elementor/editor/localize_settings', [ $this, 'promotePremiumWidgets' ] );
100 + add_action( 'elementor/icons_manager/additional_tabs', [ $this, 'rtsb_icons' ] );
101 + }
102 + }
103 + );
101 104 }
102 105
103 106 /**
104 107 * Register Category
@@ -112,9 +115,9 @@
112 115 $builder_id = get_queried_object_id();
113 116 $id = Fns::wpml_object_id( $builder_id, BuilderFns::$post_type_tb, 'default' );
114 117 $type = BuilderFns::builder_type( $id );
115 118
116 - if ( $type ) {
119 + if ( $type && 'popup-builder' !== $type ) {
117 120 $builder_type = ! empty( BuilderFns::builder_page_types()[ $type ] ) ? BuilderFns::builder_page_types()[ $type ] : '';
118 121 $builder_type = str_replace( [ 'My Account - ', 'Endpoint: ' ], '', $builder_type );
119 122 $categories['rtsb-shopbuilder'] = [
120 123 'title' => esc_html__( 'ShopBuilder', 'shopbuilder' ) . ' - ' . ucfirst( $builder_type ),
@@ -159,10 +162,14 @@
159 162
160 163 /**
161 164 * Init Widgets
162 165 *
163 - * Include widgets files and register them
166 + * Include widget files and register them
164 167 *
168 + * @param object $widgets_manager Widgets Manager.
169 + *
170 + * @return void
171 + *
165 172 * @since 1.0.0
166 173 */
167 174 public function init_widgets( $widgets_manager ) {
168 175
@@ -180,45 +187,24 @@
180 187 continue;
181 188 }
182 189
183 190 if ( ! empty( $element['category'] ) ) {
184 - $widget = false;
185 - $import = apply_filters( 'rtsb_import_status', false );
186 - switch ( $element['category'] ) {
187 - case 'product':
188 - if ( BuilderFns::is_product() || $import ) {
189 - $widget = new $element['base_class']();
190 - }
191 - break;
192 - case 'shop':
193 - if ( BuilderFns::is_archive() || BuilderFns::is_shop() || $import ) {
194 - $widget = new $element['base_class']();
195 - }
196 - break;
197 - case 'archive':
198 - if ( BuilderFns::is_archive() || $import ) {
199 - $widget = new $element['base_class']();
200 - }
201 - break;
202 - case 'cart':
203 - if ( BuilderFns::is_cart() || $import ) {
204 - $widget = new $element['base_class']();
205 - }
206 - break;
207 - case 'checkout':
208 - if ( BuilderFns::is_checkout() || $import ) {
209 - $widget = new $element['base_class']();
210 - }
211 - break;
212 - case 'general':
191 + try {
192 + $widget = false;
193 + $import = apply_filters( 'rtsb_import_status', false );
194 +
195 + if ( $this->should_register_widget( $element['category'], $import ) ) {
213 196 $widget = new $element['base_class']();
214 - break;
215 - default:
216 - }
217 - $widget = apply_filters( 'rtsb/element/list/init/widgets', $widget, $element );
197 + }
218 198
219 - if ( $widget && is_object( $widget ) ) {
220 - $widgets_manager->register( $widget );
199 + $widget = apply_filters( 'rtsb/element/list/init/widgets', $widget, $element );
200 +
201 + if ( $widget && is_object( $widget ) ) {
202 + $widgets_manager->register( $widget );
203 + }
204 + } catch ( \Throwable $e ) {
205 + error_log( 'ShopBuilder widget error (' . $element['base_class'] . '): ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
206 + continue;
221 207 }
222 208 }
223 209 }
224 210 }
@@ -223,8 +209,48 @@
223 209 }
224 210 }
225 211
226 212 /**
213 + * Determine whether a widget for the given category should be registered
214 + * in the current request context.
215 + *
216 + * @param string $category Element category.
217 + * @param bool $import Whether an import is in progress.
218 + *
219 + * @return bool
220 + */
221 + protected function should_register_widget( $category, $import ) {
222 + // On the frontend the Elementor Widgets Manager can initialise at an
223 + // unpredictable time — e.g. Elementor Pro's Theme Builder boots it on
224 + // template_redirect — when WooCommerce query conditionals such as
225 + // is_shop()/is_archive() are not yet reliable. Widget registration is
226 + // cached for the whole request, so gating it by those conditionals can
227 + // permanently skip ShopBuilder widgets, leaving the builder template to
228 + // render empty container shells. Register unconditionally on the
229 + // frontend; keep the per-category gate only for the editor widget panel.
230 + if ( ! is_admin() ) {
231 + return true;
232 + }
233 +
234 + switch ( $category ) {
235 + case 'product':
236 + return BuilderFns::is_product() || $import;
237 + case 'shop':
238 + return BuilderFns::is_archive() || BuilderFns::is_shop() || $import;
239 + case 'archive':
240 + return BuilderFns::is_archive() || $import;
241 + case 'cart':
242 + return BuilderFns::is_cart() || $import;
243 + case 'checkout':
244 + return BuilderFns::is_checkout() || $import;
245 + case 'general':
246 + return true;
247 + default:
248 + return false;
249 + }
250 + }
251 +
252 + /**
227 253 * Adding custom icons in Elementor
228 254 *
229 255 * @param array $tabs Tabs.
230 256 *
@@ -289,18 +315,26 @@
289 315 *
290 316 * @since 2.0.0
291 317 */
292 318 if ( did_action( 'elementor/loaded' ) && 'elementor' === $this->page_edit_with ) {
293 - do_action( 'elementor/page_templates/header-footer/before_content' );
319 + do_action( 'elementor/page_templates/header-footer/before_content' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
294 320 }
295 321
296 322 $parent_class = apply_filters( 'rtsb/builder/content/parent_class', [] );
297 323
298 324 if ( BuilderFns::is_product() ) {
299 - $product_id = Fns::get_prepared_product_id();
325 + $product_id = Fns::get_prepared_product_id();
326 +
327 + // Ensure the WordPress global $product is a valid WC_Product before
328 + // wc_get_product_class() fires the `woocommerce_post_class` filter.
329 + // Some themes (e.g. Blocksy) read the global $product directly inside
330 + // that filter and call methods like get_type() on it, which throws a
331 + // fatal error when the global is still an unresolved string.
332 + $_product = Fns::get_product();
333 +
300 334 $parent_class = wc_get_product_class( $parent_class, $product_id );
301 - if ( function_exists( 'rtwpvs' ) ) {
302 - $_product = Fns::get_product();
335 +
336 + if ( function_exists( 'rtwpvs' ) && $_product instanceof \WC_Product ) {
303 337 if ( $_product->is_type( 'variable' ) ) {
304 338 $parent_class[] = 'rtwpvs-product';
305 339 $beside_label = function_exists( 'rtwpvsp' ) && rtwpvs()->get_option( 'attribute_on_click_behavior' );
306 340 if ( $beside_label ) {
@@ -329,10 +363,34 @@
329 363 if ( BuilderFns::is_checkout() ) {
330 364 if ( ! apply_filters( 'rtsb/multi/step/checkout/widget', true ) ) {
331 365 return;
332 366 }
367 +
368 + $checkout = WC()->checkout();
369 +
370 + // Temporarily remove callbacks that Elementor checkout
371 + // handles via separate widgets to prevent duplicates.
372 + remove_action( 'woocommerce_before_checkout_form', [ Hooks\ActionHooks::class, 'woocommerce_checkout_login_form' ], 10 );
373 + remove_action( 'woocommerce_before_checkout_form', [ Hooks\ActionHooks::class, 'woocommerce_checkout_coupon_form' ], 10 );
374 + remove_action( 'woocommerce_before_checkout_form', [ Fns::class, 'woocommerce_output_all_notices' ], 10 );
375 +
376 + do_action( 'woocommerce_before_checkout_form', $checkout );
377 +
378 + // Restore callbacks for non-Elementor contexts.
379 + add_action( 'woocommerce_before_checkout_form', [ Hooks\ActionHooks::class, 'woocommerce_checkout_login_form' ], 10 );
380 + add_action( 'woocommerce_before_checkout_form', [ Hooks\ActionHooks::class, 'woocommerce_checkout_coupon_form' ], 10 );
381 + add_action( 'woocommerce_before_checkout_form', [ Fns::class, 'woocommerce_output_all_notices' ], 10 );
382 +
383 + // If checkout registration is disabled and not logged in, the user cannot checkout.
384 + if ( ! $checkout->is_registration_enabled() && $checkout->is_registration_required() && ! is_user_logged_in() ) {
385 + echo esc_html( apply_filters( 'woocommerce_checkout_must_be_logged_in_message', __( 'You must be logged in to checkout.', 'shopbuilder' ) ) );
386 + return;
387 + }
333 388 ?>
334 - <form name="checkout" method="post" class="rtsb-woocommerce-checkout checkout" action="<?php echo esc_url( wc_get_checkout_url() ); ?>" enctype="multipart/form-data">
389 + <!--
390 + woocommerce-checkout added for fix issue with payment method ( Revolut Pay )
391 + -->
392 + <form name="checkout" method="post" class="rtsb-woocommerce-checkout woocommerce-checkout checkout" action="<?php echo esc_url( wc_get_checkout_url() ); ?>" enctype="multipart/form-data">
335 393 <?php
336 394 }
337 395 }
338 396
@@ -349,9 +407,9 @@
349 407 *
350 408 * @since 2.0.0
351 409 */
352 410 if ( did_action( 'elementor/loaded' ) && 'elementor' === $this->page_edit_with ) {
353 - do_action( 'elementor/page_templates/header-footer/after_content' );
411 + do_action( 'elementor/page_templates/header-footer/after_content' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
354 412 }
355 413
356 414 if ( BuilderFns::is_checkout() ) {
357 415 if ( ! apply_filters( 'rtsb/multi/step/checkout/widget', true ) ) {
@@ -359,8 +417,9 @@
359 417 }
360 418 ?>
361 419 </form>
362 420 <?php
421 + do_action( 'woocommerce_after_checkout_form', WC()->checkout() );
363 422 }
364 423 ?>
365 424 </div>
366 425 <!-- After Content end -->
@@ -391,9 +450,9 @@
391 450 }
392 451
393 452 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
394 453 $search = ! empty( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
395 - $results = $post_list = [];
454 + $results = $post_list = []; // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
396 455 switch ( $source_name ) {
397 456 case 'taxonomy':
398 457 $args = [
399 458 'hide_empty' => false,
@@ -398,13 +457,13 @@
398 457 $args = [
399 458 'hide_empty' => false,
400 459 'orderby' => 'name',
401 460 'order' => 'ASC',
402 - 'search' => $search,
461 + 'name__like' => $search,
403 462 'number' => '5',
404 463 ];
405 464
406 - if ( $post_type !== 'all' ) {
465 + if ( 'all' !== $post_type ) {
407 466 $args['taxonomy'] = $post_type;
408 467 }
409 468
410 469 $post_list = wp_list_pluck( get_terms( $args ), 'name', 'term_id' );
@@ -476,10 +535,10 @@
476 535 'include' => implode( ',', $ids ),
477 536 ];
478 537
479 538 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
480 - if ( $_POST['post_type'] !== 'all' ) {
481 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
539 + if ( 'all' !== $_POST['post_type'] ) {
540 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
482 541 $args['taxonomy'] = sanitize_text_field( $_POST['post_type'] );
483 542 }
484 543
485 544 $response = wp_list_pluck( get_terms( $args ), 'name', 'term_id' );
@@ -515,12 +574,12 @@
515 574
516 575 /**
517 576 * Ajax callback for rt-select2
518 577 *
519 - * @param $post_type
520 - * @param $limit
521 - * @param $search
522 - * @param $paged
578 + * @param string $post_type Post type.
579 + * @param int $limit Limit.
580 + * @param string $search Search.
581 + * @param int $paged Paged.
523 582 *
524 583 * @return array
525 584 */
526 585 public function get_query_data( $post_type = 'any', $limit = 10, $search = '', $paged = 1 ) {
@@ -555,8 +614,17 @@
555 614 }
556 615
557 616 if ( ! empty( $search ) ) {
558 617 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_title LIKE %s", '%' . esc_sql( $search ) . '%' );
618 + }
619 +
620 + // Add a query for shop_coupon post-type to check 'show on frontend' meta.
621 + if ( 'shop_coupon' === $post_type ) {
622 + $where .= " AND {$wpdb->posts}.ID IN (
623 + SELECT post_id FROM {$wpdb->postmeta}
624 + WHERE meta_key = 'rtsb_show_on_frontend'
625 + AND meta_value = 'on'
626 + )";
559 627 }
560 628
561 629 $query = "select post_title,ID from $wpdb->posts where post_status = 'publish' {$where} {$limit}";
562 630 $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching