PluginProbe ʕ •ᴥ•ʔ
ShopEngine Elementor WooCommerce Builder Addon – All in One WooCommerce Solution with eCommerce Templates & Woo Widgets / 4.9.4
ShopEngine Elementor WooCommerce Builder Addon – All in One WooCommerce Solution with eCommerce Templates & Woo Widgets v4.9.4
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 2.0.0 2.1.0 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.1 3.0.0 3.1.0 3.1.1 4.0.0 4.0.1 4.1.0 4.1.1 4.2.0 4.2.1 4.3.0 4.3.1 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.6.6 4.6.7 4.6.8 4.6.9 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 4.7.9 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.8.9 trunk 0.1.2-beta 0.1.3-beta 0.1.4-beta 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.5.0 1.5.1 1.6.0 1.6.1 1.7.0 1.8.0 1.8.1 1.9.0
shopengine / core / page-templates / hooks / single.php
shopengine / core / page-templates / hooks Last commit date
archive.php 2 weeks ago base-content.php 2 years ago base.php 2 weeks ago cart.php 2 weeks ago checkout.php 2 weeks ago shop.php 4 years ago single.php 2 weeks ago
single.php
175 lines
1 <?php
2
3 namespace ShopEngine\Core\Page_Templates\Hooks;
4
5 use ShopEngine\Core\Builders\Templates;
6
7 defined('ABSPATH') || exit;
8
9 class Single extends Base {
10
11 protected $page_type = 'single';
12 protected $template_part = 'content-single-product.php';
13
14 public function init() : void {
15 // nothing is going on here
16 add_action('wp_enqueue_scripts', [$this,'single_page_css_conflict_remove'], 20);
17
18 $this->delayed_hook_conflicts();
19 }
20
21 protected function get_page_type_option_slug(): string {
22 if(!empty($_REQUEST['nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['nonce'])), 'wp_rest')) {
23 return !empty($_REQUEST['shopengine_quickview']) && $_REQUEST['shopengine_quickview'] === 'modal-content' ? 'quick_view' : $this->page_type;
24 }
25 return $this->page_type;
26 }
27
28 protected function template_include_pre_condition() : bool {
29
30 return is_product();
31 }
32
33 public function before_hooks() {
34 do_action( 'woocommerce_before_single_product' );
35
36 $this->generate_product_structured_data();
37 }
38
39 /**
40 * Emit WooCommerce Product JSON-LD (schema.org) structured data.
41 *
42 * ShopEngine renders the single product from builder widgets and never fires
43 * the `woocommerce_single_product_summary` action, which is where WooCommerce
44 * normally hooks WC_Structured_Data::generate_product_data() (priority 60).
45 * Without this call the collected structured-data array stays empty and
46 * WooCommerce's wp_footer output prints nothing, so the Product schema
47 * disappears whenever a ShopEngine single template is active. Generating it
48 * here lets WooCommerce's own output_structured_data() render it as usual.
49 *
50 * @return void
51 */
52 protected function generate_product_structured_data() {
53
54 // Skip Quick View: its markup is loaded into a modal and would inject
55 // duplicate/misplaced product schema into the host page's footer.
56 if ( $this->get_page_type_option_slug() === 'quick_view' ) {
57 return;
58 }
59
60 if ( ! function_exists( 'WC' ) || empty( WC()->structured_data ) ) {
61 return;
62 }
63
64 global $product;
65
66 if ( ! $product instanceof \WC_Product ) {
67 $product = wc_get_product( get_queried_object_id() );
68 }
69
70 if ( $product instanceof \WC_Product ) {
71 WC()->structured_data->generate_product_data( $product );
72 }
73 }
74
75 public function after_hooks()
76 {
77 do_action( 'woocommerce_after_single_product' );
78 $themeName = get_template();
79 if ( $themeName == 'eduma' ) {
80 wp_dequeue_script('thim-main');
81 wp_dequeue_script('thim-custom-script');
82 }
83 }
84
85 public function single_page_css_conflict_remove() {
86
87 // Remove style and script for astra addon single product layout
88 if(is_plugin_active('astra-addon/astra-addon.php')) {
89
90 wp_dequeue_style('astra-addon-css');
91 }
92
93 if (function_exists('wp_get_theme')) {
94 $theme = wp_get_theme();
95 $active_theme = $theme->get('Name');
96
97 if ($active_theme === 'PHOX' || $active_theme === 'PHOX Child') {
98
99 wp_dequeue_style('wdes-woocommerce');
100 wp_dequeue_script('bootstrap');
101 }
102 }
103
104 $themeName = get_template();
105 // Remove Auxin Shop CSS if Auxin Shop is active
106 if(is_plugin_active('auxin-shop/auxin-shop.php')) {
107
108 wp_dequeue_style('auxin-shop');
109
110 } if ($themeName == 'phlox-pro') {
111
112 wp_dequeue_style('auxin-elementor-base');
113 }
114
115 /**
116 * Remove Envo Shop JS if Envo Shop is active
117 * This is to prevent conflicts with ShopEngine's single product template
118 */
119
120 if($themeName == 'envo-shop') {
121
122 // Stop Envo Shop from rendering its own duplicate plus/minus buttons
123 remove_action('woocommerce_before_add_to_cart_quantity', 'envo_shop_display_quantity_minus');
124 remove_action('woocommerce_after_add_to_cart_quantity', 'envo_shop_display_quantity_plus');
125
126 // Envo Shop's click handler is bound with the generic 'button.plus, button.minus'
127 // selector, which also matches ShopEngine's own quantity buttons (same markup),
128 // so it still double-fires alongside ShopEngine's handler even without its own
129 // buttons rendered. Unbind it specifically, leaving ShopEngine's handler intact.
130 wp_add_inline_script(
131 'envo-shop-theme-js',
132 "jQuery(function($){ $('form.cart').off('click', 'button.plus, button.minus'); });",
133 'after'
134 );
135 }
136
137 }
138
139 public function delayed_hook_conflicts() {
140
141 if (is_plugin_active('auxin-shop/auxin-shop.php')) {
142
143 remove_filter('wc_get_template', 'auxshp_get_wc_template', 11, 2);
144
145 // Remove Auxin Shop template loader filter
146 global $wp_filter;
147 if (isset($wp_filter['woocommerce_locate_template'])) {
148 foreach ($wp_filter['woocommerce_locate_template']->callbacks as $priority => $callbacks) {
149 foreach ($callbacks as $key => $callback) {
150 if (is_array($callback['function']) &&
151 is_object($callback['function'][0]) &&
152 get_class($callback['function'][0]) === 'AUXSHP_Template_Loader' &&
153 $callback['function'][1] === 'load_templates') {
154 unset($wp_filter['woocommerce_locate_template']->callbacks[$priority][$key]);
155 }
156 }
157 }
158 }
159
160 if (isset($wp_filter['woocommerce_after_single_product']) &&
161 isset($wp_filter['woocommerce_after_single_product']->callbacks[20]) &&
162 is_array($wp_filter['woocommerce_after_single_product']->callbacks[20])) {
163 foreach ($wp_filter['woocommerce_after_single_product']->callbacks[20] as $key => $callback) {
164 if (is_array($callback['function']) &&
165 is_object($callback['function'][0]) &&
166 get_class($callback['function'][0]) === 'AUXSHP_Template_Loader' &&
167 $callback['function'][1] === 'auxshp_related_products') {
168 unset($wp_filter['woocommerce_after_single_product']->callbacks[20][$key]);
169 }
170 }
171 }
172 }
173 }
174 }
175