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
← All changes | app/Controllers/Builder/BuilderCpt.php +280 -24 2.2.23.4.0 View file →
@@ -34,19 +34,53 @@
34 34 add_filter( 'manage_edit-' . BuilderFns::$post_type_tb . '_columns', [ $this, 'add_new_columns' ] );
35 35 add_action( 'manage_' . BuilderFns::$post_type_tb . '_posts_custom_column', [ $this, 'custom_columns' ], 10, 2 );
36 36 add_filter( 'manage_edit-' . BuilderFns::$post_type_tb . '_sortable_columns', [ $this, 'register_sortable_columns' ] );
37 37 add_action( 'pre_get_posts', [ $this, 'sortable_columns_query' ] );
38 - add_filter( 'template_include', [ $this, 'builder_template' ], 99 );
38 + // Priority 99999 ensures ShopBuilder wins over Elementor Pro Theme Builder,
39 + // which also filters `template_include` at 99.
40 + add_filter( 'template_include', [ $this, 'builder_template' ], 99999 );
39 41 add_filter( 'template_redirect', [ $this, 'restrict_preview_access' ], 99 );
40 42
41 43 add_filter( 'parse_query', [ $this, 'query_filter' ] );
42 - // add filter for search.
44 + // Add filter for search.
43 45 add_action( 'restrict_manage_posts', [ $this, 'add_filter' ] );
46 + // Post Content Remove For Elementor Builder.
47 + add_action( 'save_post_' . BuilderFns::$post_type_tb, [ $this, 'on_save_post' ], 10, 2 );
48 + }
44 49
45 - add_action( 'in_admin_header', [ $this, 'remove_all_notices' ], 1000 );
50 + /**
51 + * Hook into post save to clear post content for specific post type and editor.
52 + *
53 + * This method checks if the post is being updated (not newly created),
54 + * and if the post type matches the target type defined in BuilderFns::$post_type_tb.
55 + * If the post is edited with Elementor, it clears the post_content directly
56 + * from the database and clears the post cache.
57 + *
58 + * @param int $post_ID ID of the post being saved.
59 + * @param WP_Post $post Post object.
60 + *
61 + * @return void
62 + */
63 + public function on_save_post( $post_ID, $post ) {
64 + // Only proceed on update and specific post type.
65 + if ( BuilderFns::$post_type_tb !== $post->post_type ) {
66 + return;
67 + }
68 + if ( 'elementor' !== Fns::page_edit_with( $post_ID ) ) {
69 + return;
70 + }
71 + global $wpdb;
72 + // Directly update the post_content to empty.
73 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
74 + $wpdb->update(
75 + $wpdb->posts,
76 + [ 'post_content' => '' ],
77 + [ 'ID' => $post_ID ]
78 + );
79 + // Clear cache so changes reflect properly.
80 + clean_post_cache( $post_ID );
46 81 }
47 82
48 -
49 83 /**
50 84 * Public function add_filter.
51 85 * Added search filter for type of template
52 86 *
@@ -59,10 +93,10 @@
59 93 if ( BuilderFns::$post_type_tb != $typenow ) {
60 94 return;
61 95 }
62 96 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
63 - $selected = isset( $_GET['type'] ) ? sanitize_key( $_GET['type'] ) : '';
64 - $builder_page_types = BuilderFns::builder_page_types();
97 + $selected = isset( $_GET['type'] ) ? sanitize_key( $_GET['type'] ) : '';
98 + $builder_page_types = BuilderFns::builder_page_types();
65 99 ?>
66 100 <select name="template_type" id="type">
67 101 <option value="all" <?php selected( 'all', $selected ); ?>><?php esc_html_e( 'Template Type ', 'shopbuilder' ); ?></option>
68 102 <?php foreach ( $builder_page_types as $key => $value ) { ?>
@@ -131,9 +165,9 @@
131 165 'feeds' => false,
132 166 ],
133 167 'query_var' => true,
134 168 'publicly_queryable' => true,
135 - 'capability_type' => 'product',
169 + // 'capability_type' => 'product', Will Remove this line in the future.
136 170 'show_in_rest' => true,
137 171 'rest_base' => BuilderFns::$post_type_tb,
138 172 ];
139 173
@@ -187,9 +221,9 @@
187 221 * @param string $post_id Post id.
188 222 *
189 223 * @return void
190 224 */
191 - public function custom_columns( $column, $post_id ) {
225 + public function custom_columns( $column, $post_id ) { // phpcs:ignore Generic.Metrics.NestingLevel.TooHigh
192 226 $post_id = absint( $post_id );
193 227 $types = BuilderFns::builder_page_types();
194 228 $template_type = BuilderFns::builder_type( $post_id ) ? BuilderFns::builder_type( $post_id ) : array_key_first( $types );
195 229
@@ -195,8 +229,9 @@
195 229
196 230 switch ( $column ) {
197 231 case 'type':
198 232 $the_type = ! empty( $types[ $template_type ] ) ? '<b>' . esc_html( ucwords( $types[ $template_type ] ) ) . '</b>' : '';
233 + $the_type = $this->template_type_suffix( $the_type, $template_type, $post_id );
199 234 Fns::print_html( apply_filters( 'rtsb/columns/template/types/text', $the_type, $template_type, $post_id ) );
200 235 break;
201 236 case 'edit_with':
202 237 $edit_by = Fns::page_edit_with( $post_id );
@@ -202,11 +237,20 @@
202 237 $edit_by = Fns::page_edit_with( $post_id );
203 238 echo esc_html( ucfirst( $edit_by ) );
204 239 break;
205 240 case 'set_default':
206 - $is_set_default = absint( BuilderFns::builder_page_id_by_type( $template_type ) );
207 - $is_published = 'publish' === get_post_status( $post_id );
208 - $page_type_for = $template_type;
241 + $singleton_types = apply_filters(
242 + 'rtsb/builder/singleton_types',
243 + array_keys( BuilderFns::builder_page_types() )
244 + );
245 + if ( in_array( $template_type, $singleton_types, true ) ) {
246 + $is_set_default = absint( BuilderFns::builder_page_id_by_type( $template_type ) );
247 + } else {
248 + // Pool type: read per-post meta flag — multiple templates of the same type can be enabled simultaneously.
249 + $is_set_default = 'on' === get_post_meta( $post_id, '_rtsb_tb_enabled_in_pool', true ) ? $post_id : 0;
250 + }
251 + $is_published = 'publish' === get_post_status( $post_id );
252 + $page_type_for = $template_type;
209 253 if ( 'product' === $template_type ) {
210 254 $product_page_for = get_post_meta( $post_id, '_is_product_page_template_for', true );
211 255 switch ( $product_page_for ) {
212 256 case 'specific_products':
@@ -214,8 +258,11 @@
214 258 $set_default = BuilderFns::get_specific_product_as_default( $post_id );
215 259 if ( $set_default ) {
216 260 $is_set_default = $post_id;
217 261 }
262 + if ( ! $set_default && $is_set_default === $post_id ) {
263 + $is_set_default = '';
264 + }
218 265 break;
219 266 case 'product_cats':
220 267 $page_type_for = 'product-page-template-' . $post_id . '-specific-category';
221 268 $set_default_option_name = BuilderFns::option_name_product_page_specific_cat_set_default( $post_id );
@@ -231,8 +278,16 @@
231 278 if ( $pp_tag_set_default ) {
232 279 $is_set_default = $post_id;
233 280 }
234 281 break;
282 + case 'product_brands':
283 + $page_type_for = 'product-page-template-' . $post_id . '-specific-brand';
284 + $set_default_option_name = BuilderFns::option_name_product_page_specific_brand_set_default( $post_id );
285 + $pp_brand_set_default = TemplateSettings::instance()->get_option( $set_default_option_name );
286 + if ( $pp_brand_set_default ) {
287 + $is_set_default = $post_id;
288 + }
289 + break;
235 290 default:
236 291 break;
237 292 }
238 293 } elseif ( 'archive' === $template_type ) {
@@ -240,10 +295,20 @@
240 295 $set_default = BuilderFns::get_specific_category_as_default( $post_id );
241 296 if ( ! empty( $categories_name ) && $set_default ) {
242 297 $is_set_default = $post_id;
243 298 $page_type_for = 'template-' . $post_id . '-specific-category';
299 + } else {
300 + $option_name = BuilderFns::option_name( $template_type );
301 + $is_set_default = TemplateSettings::instance()->get_option( $option_name );
302 + $set_default = $is_set_default;
244 303 }
304 + if ( ! $set_default && $is_set_default === $post_id ) {
305 + $is_set_default = '';
306 + }
245 307 }
308 +
309 + $is_set_default = apply_filters( 'rtsb/builder/columns/set_default', $is_set_default, $post_id, $template_type );
310 + $page_type_for = apply_filters( 'rtsb/builder/columns/page_type_for', $page_type_for, $post_id, $template_type );
246 311 ?>
247 312 <span class="rtsb-switch-wrapper page-type-<?php echo esc_attr( $page_type_for ); ?>" <?php echo ! $is_published ? 'style="pointer-events: none;"' : null; ?> title="<?php esc_html_e( 'Only publish post can set default ', 'shopbuilder' ); ?>">
248 313 <label class="rtsb-switch">
249 314 <?php do_action( 'rtsb/set/default/column/extra/field', $post_id, $template_type, $is_set_default, $is_published ); ?>
@@ -259,8 +324,211 @@
259 324 }
260 325 }
261 326
262 327 /**
328 + * Appends template sub-type suffix and selected item details to the type column text.
329 + *
330 + * For product templates: shows "Template For Selected Products/Categories/Tags"
331 + * with linked item names below.
332 + * For archive templates: shows "Template For Selected Category" with linked names.
333 + *
334 + * @param string $the_type The current type column HTML.
335 + * @param string $template_type The template type slug.
336 + * @param int $post_id The builder post ID.
337 + *
338 + * @return string
339 + */
340 + private function template_type_suffix( $the_type, $template_type, $post_id ) {
341 + if ( 'product' === $template_type ) {
342 + $the_type = $this->product_template_type_suffix( $the_type, $post_id );
343 + } elseif ( 'archive' === $template_type ) {
344 + $the_type = $this->archive_template_type_suffix( $the_type, $post_id );
345 + }
346 +
347 + return $the_type;
348 + }
349 +
350 + /**
351 + * Appends product template sub-type suffix with linked product/category/tag names.
352 + *
353 + * @param string $the_type The current type column HTML.
354 + * @param int $post_id The builder post ID.
355 + *
356 + * @return string
357 + */
358 + private function product_template_type_suffix( $the_type, $post_id ) {
359 + $product_page_for = get_post_meta( $post_id, '_is_product_page_template_for', true );
360 + $has_pro = rtsb()->has_pro();
361 + $pro_label = ! $has_pro ? ' <span class="rtsb-pro-label" style="color:#fff;background:#ff5722;padding:1px 6px;border-radius:3px;font-size:11px;font-weight:600;">' . esc_html__( 'Pro', 'shopbuilder' ) . '</span>' : '';
362 +
363 + switch ( $product_page_for ) {
364 + case 'specific_products':
365 + $the_type .= ' - ' . esc_html__( 'Template For Selected Products', 'shopbuilder' ) . $pro_label;
366 + if ( $has_pro ) {
367 + $the_type .= $this->get_selected_products_html( $post_id );
368 + }
369 + break;
370 + case 'product_cats':
371 + $the_type .= ' - ' . esc_html__( 'Template For Selected Categories', 'shopbuilder' ) . $pro_label;
372 + if ( $has_pro ) {
373 + $the_type .= $this->get_selected_terms_html( $post_id, 'product_cat' );
374 + }
375 + break;
376 + case 'product_tags':
377 + $the_type .= ' - ' . esc_html__( 'Template For Selected Tags', 'shopbuilder' ) . $pro_label;
378 + if ( $has_pro ) {
379 + $the_type .= $this->get_selected_terms_html( $post_id, 'product_tag' );
380 + }
381 + break;
382 + }
383 +
384 + return $the_type;
385 + }
386 +
387 + /**
388 + * Appends archive template sub-type suffix with linked category names.
389 + *
390 + * @param string $the_type The current type column HTML.
391 + * @param int $post_id The builder post ID.
392 + *
393 + * @return string
394 + */
395 + private function archive_template_type_suffix( $the_type, $post_id ) {
396 + $option_name = BuilderFns::archive_option_name_by_template_id( $post_id );
397 + $categories = TemplateSettings::instance()->get_option( $option_name );
398 +
399 + if ( ! empty( $categories ) && is_array( $categories ) ) {
400 + $has_pro = rtsb()->has_pro();
401 + $pro_label = ! $has_pro ? ' <span class="rtsb-pro-label" style="color:#fff;background:#ff5722;padding:1px 6px;border-radius:3px;font-size:11px;font-weight:600;">' . esc_html__( 'Pro', 'shopbuilder' ) . '</span>' : '';
402 + $the_type .= ' - ' . esc_html__( 'Template For Selected Category', 'shopbuilder' ) . $pro_label;
403 + $the_type .= $this->get_selected_archive_categories_html( $categories );
404 + }
405 +
406 + return $the_type;
407 + }
408 +
409 + /**
410 + * Gets HTML list of selected archive category names with links.
411 + *
412 + * @param array $category_slugs Array of term slugs.
413 + *
414 + * @return string
415 + */
416 + private function get_selected_archive_categories_html( $category_slugs ) {
417 + if ( empty( $category_slugs ) || ! is_array( $category_slugs ) ) {
418 + return '';
419 + }
420 +
421 + $links = [];
422 +
423 + foreach ( $category_slugs as $cat_slug ) {
424 + $term = get_term_by( 'slug', $cat_slug, 'product_cat' );
425 +
426 + if ( is_wp_error( $term ) || ! $term ) {
427 + continue;
428 + }
429 +
430 + $term_link = get_term_link( $term );
431 +
432 + if ( is_wp_error( $term_link ) ) {
433 + continue;
434 + }
435 +
436 + $links[] = '<a href="' . esc_url( $term_link ) . '" target="_blank">'
437 + . esc_html( $term->name ) . '</a>';
438 + }
439 +
440 + if ( empty( $links ) ) {
441 + return '';
442 + }
443 +
444 + return '<br><small>' . implode( ', ', $links ) . '</small>';
445 + }
446 +
447 + /**
448 + * Gets HTML list of selected product names with links to their detail pages.
449 + *
450 + * @param int $post_id The builder post ID.
451 + *
452 + * @return string
453 + */
454 + private function get_selected_products_html( $post_id ) {
455 + $option_name = BuilderFns::option_name_by_template_id( $post_id );
456 + $product_ids = TemplateSettings::instance()->get_option( $option_name );
457 +
458 + if ( empty( $product_ids ) || ! is_array( $product_ids ) ) {
459 + return '';
460 + }
461 +
462 + $links = [];
463 +
464 + foreach ( $product_ids as $product_id ) {
465 + $product_id = absint( $product_id );
466 + $product = wc_get_product( $product_id );
467 +
468 + if ( ! $product ) {
469 + continue;
470 + }
471 +
472 + $links[] = '<a href="' . esc_url( get_permalink( $product_id ) ) . '" target="_blank">'
473 + . esc_html( $product->get_name() ) . '</a>';
474 + }
475 +
476 + if ( empty( $links ) ) {
477 + return '';
478 + }
479 +
480 + return '<br><small>' . implode( ', ', $links ) . '</small>';
481 + }
482 +
483 + /**
484 + * Gets HTML list of selected term names with links.
485 + *
486 + * @param int $post_id The builder post ID.
487 + * @param string $taxonomy The taxonomy slug.
488 + *
489 + * @return string
490 + */
491 + private function get_selected_terms_html( $post_id, $taxonomy ) {
492 + if ( 'product_cat' === $taxonomy ) {
493 + $option_name = BuilderFns::option_name_product_page_selected_cat( $post_id );
494 + } else {
495 + $option_name = BuilderFns::option_name_product_page_selected_tag( $post_id );
496 + }
497 +
498 + $term_ids = TemplateSettings::instance()->get_option( $option_name );
499 +
500 + if ( empty( $term_ids ) || ! is_array( $term_ids ) ) {
501 + return '';
502 + }
503 +
504 + $links = [];
505 +
506 + foreach ( $term_ids as $term_id ) {
507 + $term = get_term( absint( $term_id ), $taxonomy );
508 +
509 + if ( is_wp_error( $term ) || ! $term ) {
510 + continue;
511 + }
512 +
513 + $term_link = get_term_link( $term );
514 +
515 + if ( is_wp_error( $term_link ) ) {
516 + continue;
517 + }
518 +
519 + $links[] = '<a href="' . esc_url( $term_link ) . '" target="_blank">'
520 + . esc_html( $term->name ) . '</a>';
521 + }
522 +
523 + if ( empty( $links ) ) {
524 + return '';
525 + }
526 +
527 + return '<br><small>' . implode( ', ', $links ) . '</small>';
528 + }
529 +
530 + /**
263 531 * Register sortable columns
264 532 *
265 533 * @param [type] $columns column list.
266 534 *
@@ -331,9 +599,9 @@
331 599 * @return string
332 600 */
333 601 public function builder_template( $template ) {
334 602 $builder_page_id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_page();
335 - if ( $builder_page_id ) {
603 + if ( defined( 'ELEMENTOR_VERSION' ) && $builder_page_id ) { // Elementor Check.
336 604 $page_template = get_post_meta( $builder_page_id, '_wp_page_template', true );
337 605 $new_template = RTSB_ABSPATH . 'page-templates/builder-template.php';
338 606 if ( 'elementor_canvas' === $page_template ) {
339 607 $new_template = RTSB_ABSPATH . 'page-templates/elementor-canvas.php';
@@ -379,9 +647,8 @@
379 647 * @return array An updated array of row action links.
380 648 */
381 649 public function filter_post_row_actions( $actions ) {
382 650 if ( $this->is_current_screen() ) {
383 - // unset( $actions['view'] );
384 651 unset( $actions['inline hide-if-no-js'] );
385 652
386 653 if ( isset( $actions['view'] ) ) {
387 654 $actions['view'] = str_replace( __( 'View', 'shopbuilder' ), __( 'Preview', 'shopbuilder' ), $actions['view'] );
@@ -388,17 +655,6 @@
388 655 }
389 656 }
390 657
391 658 return $actions;
392 - }
393 -
394 - /**
395 - * Remove admin notices
396 - */
397 - public function remove_all_notices() {
398 - $screen = get_current_screen();
399 - if ( 'edit-rtsb_builder' === $screen->id ) {
400 - remove_all_actions( 'admin_notices' );
401 - remove_all_actions( 'all_admin_notices' );
402 - }
403 659 }
404 660 }