PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.6
ShopBuilder – WooCommerce Builder For Elementor v3.2.6
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/Hooks/BuilderHooks.php +1 -204 3.4.23.2.6 View file →
@@ -10,10 +10,8 @@
10 10 defined( 'ABSPATH' ) || exit();
11 11
12 12 use RadiusTheme\SB\Helpers\Fns;
13 13 use RadiusTheme\SB\Helpers\BuilderFns;
14 -use RadiusTheme\SB\Helpers\ElementorDataMap;
15 -use RadiusTheme\SB\Models\TemplateSettings;
16 14 use RadiusTheme\SB\Traits\SingletonTrait;
17 15
18 16 /**
19 17 * Builder Frontend.
@@ -33,15 +31,8 @@
33 31 */
34 32 private $page_edit_with = '';
35 33
36 34 /**
37 - * Internal cache.
38 - *
39 - * @var array
40 - */
41 - private static $cache = [];
42 -
43 - /**
44 35 * Singleton.
45 36 */
46 37 use SingletonTrait;
47 38
@@ -52,106 +43,11 @@
52 43 // Template Redirect hook run after the global query.
53 44 add_action( 'template_redirect', [ $this, 'frontend_init' ] );
54 45 add_filter( 'rtsb/builder/set/current/page/type', [ $this, 'builder_page_type' ] );
55 46 add_filter( 'body_class', [ $this, 'product_page_body_class' ] );
56 - add_action( 'pre_get_posts', [ $this, 'override_archive_posts_per_page' ] );
57 47 }
58 48
59 49 /**
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 50 * Builder page Body class.
155 51 *
156 52 * @param array $classes class list.
157 53 * @return array.
@@ -156,17 +52,9 @@
156 52 * @param array $classes class list.
157 53 * @return array.
158 54 */
159 55 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 -
166 - if ( defined( 'ELEMENTOR_PRO_VERSION' ) ) {
167 - $classes[] = 'rtsb-elementor-pro-v-' . ELEMENTOR_PRO_VERSION;
168 - }
56 + $classes[] = 'rtsb-shopbuilder-plugin rtsb_theme_' . rtsb()->current_theme;
169 57 if ( Fns::is_woocommerce() || BuilderFns::is_builder_preview() ) {
170 58 $classes[] = 'woocommerce-page';
171 59 }
172 60
@@ -226,99 +114,13 @@
226 114 *
227 115 * @return void
228 116 */
229 117 public function elementor_frontend() {
230 - // Priority 15: after Elementor registers style handles (priority 5) and before
231 - // its enqueue_styles / after_enqueue_post_styles pass (priority 20) that renders
232 - // the atomic-widget (V4) styles collected via the elementor/post/render action.
233 - add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_elementor_template_css' ], 15 );
234 118 add_action( 'rtsb/builder/before/header', [ $this, 'el_body_class' ] );
235 119 add_action( 'rtsb/builder/template/main/content', [ $this, 'elementor_template_main_content' ] );
236 120 }
237 121
238 122 /**
239 - * Enqueue the Elementor builder template CSS in the document head.
240 - *
241 - * ShopBuilder renders the Elementor template on a WooCommerce object (e.g. a product),
242 - * so Elementor's own wp_enqueue_scripts pass only fires elementor/post/render for the
243 - * queried object — never for the builder template. As a result the template's
244 - * document CSS (post-{id}.css) and, on Elementor V4, its atomic-widget styles
245 - * (local-{id}-*.css rendered on elementor/frontend/after_enqueue_post_styles) are
246 - * never loaded on builder pages although they load on Elementor's own preview.
247 - *
248 - * Firing elementor/post/render for the template id here — before Elementor's
249 - * priority-20 style pass — registers the template with the atomic styles manager so
250 - * its styles are generated and enqueued. enqueue_styles() loads the classic post CSS.
251 - *
252 - * @return void
253 - */
254 - public function enqueue_elementor_template_css() {
255 - if ( ! $this->builder_page_id || ! class_exists( '\Elementor\Plugin' ) ) {
256 - return;
257 - }
258 -
259 - $documents = \Elementor\Plugin::$instance->documents;
260 -
261 - if ( ! $documents ) {
262 - return;
263 - }
264 -
265 - $document = $documents->get( $this->builder_page_id );
266 -
267 - if ( ! $document ) {
268 - return;
269 - }
270 -
271 - // Register the template with Elementor's render pipeline (atomic V4 styles manager
272 - // collects these post ids and renders their styles on after_enqueue_post_styles).
273 - do_action( 'elementor/post/render', $this->builder_page_id );
274 -
275 - // Classic per-post document CSS (post-{id}.css) and font dependencies.
276 - if ( method_exists( $document, 'enqueue_styles' ) ) {
277 - $document->enqueue_styles();
278 - }
279 -
280 - // Elementor hooks its own frontend enqueue_styles() pass only when the
281 - // *queried* object is a singular Elementor document (Frontend::init).
282 - // On ShopBuilder frontend pages the queried object (shop/archive/cart/
283 - // checkout/product) is never the builder template, so that pass never
284 - // runs — leaving the elementor-frontend base stylesheet and, critically,
285 - // the V4 atomic styles manager (which renders the template's flexbox/
286 - // container CSS on elementor/frontend/after_enqueue_post_styles)
287 - // un-triggered, so containers collapse to full width. We drive the pass
288 - // manually below, but ONLY on the real frontend: in the Elementor editor
289 - // preview and the ShopBuilder single-template preview the queried object
290 - // *is* the template (singular + built with Elementor), so Elementor runs
291 - // its own pass and firing ours early would interfere with the editor
292 - // preview load (the panel waits on that iframe). Skip both cases.
293 - $preview = \Elementor\Plugin::$instance->preview ?? null;
294 - $is_editor_view = $preview && method_exists( $preview, 'is_editor_or_preview' ) && $preview->is_editor_or_preview();
295 - $queried_document = $documents->get( get_the_ID() );
296 - $elementor_will_load = is_singular() && $queried_document && $queried_document->is_built_with_elementor();
297 - $frontend = \Elementor\Plugin::$instance->frontend ?? null;
298 -
299 - if ( $is_editor_view || $elementor_will_load || ! $frontend || ! method_exists( $frontend, 'enqueue_styles' ) ) {
300 - return;
301 - }
302 -
303 - // Enqueues the elementor-frontend base stylesheet; one-shot (internal
304 - // static guard) so it is a no-op if something already ran it.
305 - $frontend->enqueue_styles();
306 -
307 - // The V4 Atomic_Styles_Manager only renders CSS for the post ids collected
308 - // via elementor/post/render *before* the style pass runs. When another
309 - // plugin triggers that pass first — e.g. Elementor Pro's Theme Builder
310 - // rendering its own archive location — our builder template id is
311 - // registered too late, so its per-template file (local-{id}-frontend-*.css)
312 - // is written to disk but never enqueued, and containers stay full width.
313 - // Re-firing the atomic trigger after registering the template above makes
314 - // the manager enqueue that file. This action's only listener is the atomic
315 - // styles manager, and wp_enqueue_style() de-duplicates handles, so the
316 - // extra pass is safe.
317 - do_action( 'elementor/frontend/after_enqueue_post_styles' );
318 - }
319 -
320 - /**
321 123 * Appl for Elementor.
322 124 *
323 125 * @return void
324 126 */
@@ -356,14 +158,9 @@
356 158 *
357 159 * @return void
358 160 */
359 161 public function el_body_class() {
360 - if ( ! class_exists( '\Elementor\Plugin' ) || empty( \Elementor\Plugin::$instance->frontend ) ) {
361 - return;
362 - }
363 -
364 162 $page_template = get_post_meta( $this->builder_page_id, '_wp_page_template', true );
365 -
366 163 if ( 'elementor_canvas' === $page_template ) {
367 164 \Elementor\Plugin::$instance->frontend->add_body_class( 'elementor-template-canvas' );
368 165 } elseif ( 'elementor_header_footer' === $page_template ) {
369 166 \Elementor\Plugin::$instance->frontend->add_body_class( 'elementor-template-full-width' );