PluginProbe
Ultimate Store Kit / 3.1.0
Ultimate Store Kit v3.1.0
3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 92 releases
ultimate-store-kit / includes / builder / builder-integration.php

builder-integration.php in Ultimate Store Kit 3.1.0, at includes/builder/builder-integration.php

551 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UltimateStoreKit\Builder;
4
5 if (! defined('ABSPATH')) {
6 exit; // Exit if accessed directly
7 }
8
9 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- usk_ / BDTUSK_ / ultimate-store-kit- are this plugin's established public prefixes. Ultimate Store Kit Pro calls into these names, as does third-party integration code, so renaming them is a breaking change. Plugin Check only recognises prefixes derived verbatim from the slug and so reports them as unprefixed.
10
11 if (! defined('WPINC')) {
12 die;
13 }
14
15 use Elementor\Controls_Manager;
16 use Elementor\Plugin;
17 use UltimateStoreKit\Includes\Builder\Builder_Template_Helper;
18 use UltimateStoreKit\Base\Singleton;
19 use UltimateStoreKit\Includes\Builder\Meta;
20 use UltimateStoreKit\Includes\Controls\SelectInput\Dynamic_Select;
21
22
23 class Builder_Integration {
24
25 use Singleton;
26
27 private $current_template = null;
28 public $current_template_id = null;
29
30 function __construct() {
31 add_filter('template_include', [$this, 'set_builder_template'], 9999);
32 add_action('elementor/editor/init', [$this, 'set_sample_post'], 999);
33
34 add_action('print_default_editor_scripts', array($this, 'my_custom_fonts'));
35 add_filter("elementor/document/urls/wp_preview", [$this, 'change_preview_editor_url'], 999, 2);
36
37 add_action('elementor/documents/register_controls', [$this, 'register_document_controls']);
38
39 // Add demo bypass filter for template preview
40 add_filter('ultimate_store_kit/preview/verified_bypass', function ($verify) {
41 // Check if we're in a demo environment or development site
42 // For demo sites, you might want to check domain names or other indicators
43 $demo_hosts = apply_filters('ultimate_store_kit/demo_hosts', [
44 'storekit.pro',
45 'demo.storekit.pro',
46 'localhost',
47 '127.0.0.1'
48 ]);
49
50 $current_host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
51
52 foreach ($demo_hosts as $host) {
53 if (strpos($current_host, $host) !== false) {
54 return true;
55 }
56 }
57
58 return $verify;
59 });
60
61 // Add filter to enable demo mode for preview URLs
62 add_filter('ultimate_store_kit/preview/use_demo_bypass', function ($use_demo) {
63 // Enable demo bypass in Elementor editor
64 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check on whether the Elementor editor is loading.
65 if (isset($_GET['action']) && $_GET['action'] === 'elementor') {
66 return true;
67 }
68 return $use_demo;
69 });
70 }
71
72 public function change_preview_editor_url($url, $document) {
73 $post_id = $document->get_main_id();
74 $template_type = get_post_meta($post_id, Meta::TEMPLATE_TYPE, true);
75
76 if (empty($template_type) || get_post_type($post_id) !== Meta::POST_TYPE) {
77 return $url;
78 }
79
80 $template_data = explode(Builder_Template_Helper::separator(), $template_type);
81 if (count($template_data) < 2) {
82 return $url;
83 }
84
85 $post_type = $template_data[0];
86 $template_slug = $template_data[1];
87
88 // Get template URL based on template type
89 $template_url = '';
90
91 // Check for product category first
92 if ($template_slug === 'product-category') {
93 $product_cats = get_terms([
94 'taxonomy' => 'product_cat',
95 'hide_empty' => true,
96 'number' => 1,
97 ]);
98
99 if (!empty($product_cats) && !is_wp_error($product_cats)) {
100 $template_url = get_term_link($product_cats[0]);
101 }
102 }
103
104 // Check for product tag
105 elseif ($template_slug === 'product-tag') {
106 $product_tags = get_terms([
107 'taxonomy' => 'product_tag',
108 'hide_empty' => true,
109 'number' => 1,
110 ]);
111
112 if (!empty($product_tags) && !is_wp_error($product_tags)) {
113 $template_url = get_term_link($product_tags[0]);
114 }
115 } elseif ($template_slug === 'shop' || $template_slug === 'archive') {
116 $template_url = get_permalink(wc_get_page_id('shop'));
117 } elseif ($template_slug === 'single' && $post_type === 'product') {
118 $page_settings_manager = \Elementor\Core\Settings\Manager::get_settings_managers('page');
119 $page_settings_model = $page_settings_manager->get_model($post_id);
120 $sample_product = $page_settings_model->get_settings('usk_builder_sample_post_id');
121
122 $template_url = !empty($sample_product) ?
123 get_permalink($sample_product) :
124 $this->get_default_product_url();
125 } elseif ($template_slug === 'cart') {
126 $template_url = wc_get_cart_url();
127 } elseif ($template_slug === 'checkout') {
128 $template_url = wc_get_checkout_url();
129 } elseif (
130 $template_slug === 'myaccount'
131 || $template_slug === 'login'
132 || strpos($template_slug, 'myaccount-') === 0
133 ) {
134 $template_url = get_permalink(wc_get_page_id('myaccount'));
135 } elseif ($template_slug === 'order-received') {
136 $orders = wc_get_orders(['limit' => 1]);
137 if (!empty($orders)) {
138 $order = $orders[0];
139 $order_id = $order->get_id();
140 $template_url = add_query_arg('order-received', $order_id, wc_get_checkout_url());
141 }
142 }
143
144 if (empty($template_url)) {
145 return $url;
146 }
147
148 $is_demo = apply_filters('ultimate_store_kit/preview/use_demo_bypass', false);
149 $nonce_value = $is_demo ? 'verified' : wp_create_nonce('template_preview_' . $post_id);
150
151 $param = [
152 'usk_template_id' => $post_id,
153 'preview_nonce' => $nonce_value,
154 'preview' => true
155 ];
156
157 // Add parameters two URL
158 $url = add_query_arg($param, $template_url);
159
160 return $url;
161 }
162
163 public function my_custom_fonts() {
164 if (is_admin() && Plugin::instance()->editor->is_edit_mode()) {
165 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check on whether a builder template is open; only enqueues a style.
166 if (isset($_REQUEST['usk-template'])) {
167 wp_register_style('usk-template-builder-hide-preview-btn-inline', false); // phpcs:ignore
168 wp_enqueue_style('usk-template-builder-hide-preview-btn-inline');
169 wp_add_inline_style(
170 'usk-template-builder-hide-preview-btn-inline',
171 '#elementor-panel-footer-saver-preview {display:none!important}'
172 );
173 }
174 }
175 }
176 function set_sample_post() {
177 if (Builder_Template_Helper::isTemplateEditMode()) {
178 $object = \UltimateStoreKit\Includes\Builder\Builder_Post_Singleton::instance();
179 $object::set_sample_post();
180 }
181 }
182
183 function register_document_controls($document) {
184 if (
185 ! $document instanceof \Elementor\Core\DocumentTypes\PageBase
186 || ! $document::get_property('has_elements')
187 ) {
188 return;
189 }
190
191 if (Plugin::instance()->preview->is_preview_mode())
192 return;
193
194 if (! Builder_Template_Helper::isTemplateEditMode()) {
195 return;
196 }
197
198 global $post;
199
200 if (! isset($post->ID)) {
201 return;
202 }
203 $meta = get_post_meta($post->ID);
204
205 $templateMeta = usk_optional($meta)[Meta::TEMPLATE_TYPE];
206 if (! isset($templateMeta[0])) {
207 return;
208 }
209 $postMeta = $templateMeta[0];
210 $postMeta = explode('|', $postMeta);
211 $postType = $postMeta[0];
212
213 if ($postMeta[1] != 'single') {
214 return;
215 }
216
217 $document->start_controls_section(
218 'usk_page_setting_preview',
219 [
220 'label' => esc_html__('Builder Settings', 'ultimate-store-kit'),
221 'tab' => Controls_Manager::TAB_SETTINGS,
222 ]
223 );
224
225 $document->add_control(
226 'usk_builder_sample_post_id',
227 [
228 'label' => __('Builder Post', 'ultimate-store-kit'),
229 'type' => Dynamic_Select::TYPE,
230 'multiple' => false,
231 'label_block' => true,
232 'query_args' => [
233 'post_type' => $postType
234 ],
235 ]
236 );
237
238 $document->add_control(
239 'usk_builder_sample_apply_preview',
240 [
241 'type' => Controls_Manager::BUTTON,
242 'label' => esc_html__('Apply & Preview', 'ultimate-store-kit'),
243 'label_block' => true,
244 'show_label' => false,
245 'text' => esc_html__('Apply & Preview', 'ultimate-store-kit'),
246 'separator' => 'none',
247 'event' => 'ultimateStoreKitBuilderSetting:applySinglePagePostOnPreview',
248 ]
249 );
250
251 $document->end_controls_section();
252 }
253
254 /**
255 * Rewrite default template
256 *
257 */
258 function set_builder_template($template) {
259 if (Builder_Template_Helper::isTemplateEditMode()) {
260 return $this->setBackendTemplate($template);
261 } else {
262 return $this->setFrontendTemplate($template);
263 }
264 }
265
266
267 protected function setBackendTemplate($template) {
268 return $template;
269 }
270
271
272 protected function setFrontendTemplate($template) {
273
274 if (get_post_type() == 'product') {
275 global $product;
276 $product = wc_get_product();
277 }
278
279 if (defined('ELEMENTOR_PATH')) {
280 $elementorTem = ELEMENTOR_PATH . "modules/page-templates/templates/";
281 $elementorTem = explode($elementorTem, $template);
282 if (count($elementorTem) == 2) {
283 return $template;
284 }
285 }
286
287
288 if (is_post_type_archive('product') || is_page(wc_get_page_id('shop')) || is_product_taxonomy()) {
289 $template_type = 'shop';
290
291 if (is_tax('product_cat')) {
292 $template_type = 'category';
293 } elseif (is_tax('product_tag')) {
294 $template_type = 'tag';
295 }
296
297 if ($custom_template = $this->get_template_id($template_type)) {
298 $this->current_template_id = $custom_template;
299 return $this->getTemplatePath('woocommerce/archive-product', $template);
300 }
301 }
302
303
304 if ( is_cart() ) {
305 $cart_template = $this->resolve_cart_template( $template );
306
307 if ( $cart_template ) {
308 return $cart_template;
309 }
310 }
311
312 if (is_order_received_page()) {
313 if ($custom_template = $this->get_template_id('order-received', 'product')) {
314 $this->current_template_id = $custom_template;
315 return $this->getTemplatePath('woocommerce/order-received', $template);
316 }
317 }
318
319 if (is_checkout()) {
320 if ($custom_template = $this->get_template_id('checkout', 'product')) {
321 $this->current_template_id = $custom_template;
322 return $this->getTemplatePath('woocommerce/checkout', $template);
323 }
324 }
325
326 if (is_single() && get_post_type() == 'product') {
327 if ($custom_template = $this->get_template_id('single', 'product')) {
328 $this->current_template_id = $custom_template;
329 return $this->getTemplatePath('woocommerce/single-product', $template);
330 }
331 }
332
333 if (is_account_page()) {
334 global $wp;
335 $query_vars = WC()->query->get_query_vars();
336
337 /**
338 * Add wishlist endpoint
339 */
340 $query_vars['wishlist'] = 'wishlist';
341
342 $endpoint_match = array_intersect_key($wp->query_vars, $query_vars);
343
344 // Logged-out visitors landing on /my-account/ (no endpoint) see the
345 // single Login & Register template, which is responsible for both
346 // authentication flows. WooCommerce's own form-login.php renders
347 // the login and registration forms on the same URL, so one
348 // template covers both intents.
349 if (! is_user_logged_in() && empty($endpoint_match)) {
350 if ($custom_template = $this->get_template_id('login', 'account')) {
351 $this->current_template_id = $custom_template;
352
353 if ($newTemplate = $this->getTemplatePath('woocommerce/login')) {
354 return $newTemplate;
355 }
356
357 return $this->getTemplatePath('woocommerce/my-account', $template);
358 }
359 }
360
361 if ($endpoint = $endpoint_match) {
362 $endpoint = array_key_first($endpoint);
363
364 if ($endpoint && $custom_template = $this->get_template_id($endpoint, 'account')) {
365 $this->current_template_id = $custom_template;
366
367 if ($newTemplate = $this->getTemplatePath("woocommerce/{$endpoint}")) {
368 return $newTemplate;
369 }
370
371 return $this->getTemplatePath("woocommerce/my-account", '');
372 }
373
374 if ($custom_template = $this->get_template_id('myaccount-orders', 'account')) {
375 $this->current_template_id = $custom_template;
376 return $this->getTemplatePath('woocommerce/my-account', $template);
377 }
378 } else {
379 if ($custom_template = $this->get_template_id('myaccount', 'account')) {
380 $this->current_template_id = $custom_template;
381 return $this->getTemplatePath('woocommerce/my-account', $template);
382 }
383 }
384 }
385
386
387 if (is_single()) {
388 if ($custom_template = $this->get_template_id('single', 'post')) {
389 $this->current_template_id = $custom_template;
390 return $this->getTemplatePath('single-post', $template);
391 }
392 }
393
394 if (is_archive()) {
395 if ($custom_template = $this->get_template_id('archive', 'post')) {
396 $this->current_template_id = $custom_template;
397 return $this->getTemplatePath('archive-post', $template);
398 }
399 }
400
401 if (is_home()) {
402 if ($custom_template = $this->get_template_id('home', 'post')) {
403 $this->current_template_id = $custom_template;
404 return $this->getTemplatePath('home', $template);
405 }
406 }
407
408 if ($page_Id = intval(get_option('bdt_usk_compare_products_page_id'))) {
409 if (is_page($page_Id)) {
410 if ($custom_template = $this->get_template_id('compare-products', 'product')) {
411 $this->current_template_id = $custom_template;
412 return $this->getTemplatePath('home', $template);
413 }
414 }
415 }
416
417 return $template;
418 }
419
420
421 public function getThemeTemplatePath($slug) {
422
423 $fullPath = get_template_directory() . "/ultimate-store-kit/$slug";
424 if (file_exists($fullPath)) {
425 return $fullPath;
426 }
427 }
428
429 public function getPluginTemplatePath($slug) {
430
431 $fullPath = BDTUSK_PATH . "includes/builder/templates/$slug";
432 if (file_exists($fullPath)) {
433 return $fullPath;
434 }
435 }
436
437
438 /**
439 * Get Template Path ID
440 *
441 * @param $slug
442 * @param $postType
443 *
444 * @return mixed|void|null
445 */
446 public function get_template_id($slug, $postType = false) {
447 // If we already have a template ID for this request, return it
448 if (null !== $this->current_template_id) {
449 return $this->current_template_id;
450 }
451
452 // Handle template preview from URL parameters
453 if (!empty($_GET['preview']) && !empty($_GET['usk_template_id']) && !empty($_GET['preview_nonce'])) {
454 $usk_template_id = sanitize_text_field(wp_unslash($_GET['usk_template_id']));
455 $nonce = sanitize_text_field(wp_unslash($_GET['preview_nonce']));
456
457 // Special handling for demo bypass
458 $is_demo_bypass = ($nonce === 'verified');
459 $nonce_verified = $is_demo_bypass ?
460 apply_filters('ultimate_store_kit/preview/verified_bypass', false) :
461 wp_verify_nonce($nonce, 'template_preview_' . $usk_template_id);
462
463 if ($nonce_verified) {
464 // For demo bypass mode, we don't need to check template type
465 if ($is_demo_bypass) {
466 $this->current_template_id = (int)$usk_template_id;
467 return $this->current_template_id;
468 }
469
470 // For normal preview, check template type
471 $template_type = get_post_meta($usk_template_id, Meta::TEMPLATE_TYPE, true);
472 if (!empty($template_type)) {
473 $template_data = explode(Builder_Template_Helper::separator(), $template_type);
474 if (count($template_data) >= 2 && $template_data[1] === $slug && ($postType === false || $template_data[0] === $postType)) {
475 $this->current_template_id = (int)$usk_template_id;
476 return $this->current_template_id;
477 }
478 }
479 }
480 }
481
482 // Regular template retrieval
483 $templateId = Builder_Template_Helper::getTemplate($slug, $postType);
484 $this->current_template_id = apply_filters('ultimate-store-kit-builder/custom-shop-template', $templateId);
485
486 return $this->current_template_id;
487 }
488
489
490 /**
491 * Get Template Path
492 *
493 * @param $slug
494 * @param $default
495 *
496 * @return mixed|string|void
497 */
498 protected function getTemplatePath($slug, $default = '') {
499 $phpSlug = "{$slug}.php";
500
501 if ($template = $this->getThemeTemplatePath($phpSlug)) {
502 return $template;
503 }
504
505 if ($template = $this->getPluginTemplatePath($phpSlug)) {
506 return $template;
507 }
508
509 return $default;
510 }
511
512 /**
513 * Resolve the plugin cart template for WooCommerce cart requests.
514 *
515 * @param string $template Default WordPress template path.
516 * @return string|false
517 */
518 protected function resolve_cart_template( $template ) {
519 if ( ! Cart_Render::is_available() ) {
520 return false;
521 }
522
523 $custom_template = $this->get_template_id( 'cart', 'product' );
524 $this->current_template_id = $custom_template ? absint( $custom_template ) : null;
525
526 return $this->getTemplatePath( 'woocommerce/cart', $template );
527 }
528
529 /**
530 * Get default product URL with proper error checking
531 *
532 * @return string
533 */
534 protected function get_default_product_url() {
535 $products = wc_get_products(['status' => 'publish', 'limit' => 1]);
536
537 if (!empty($products) && isset($products[0]) && is_object($products[0])) {
538 $product = $products[0];
539 if (method_exists($product, 'get_id')) {
540 return get_permalink($product->get_id());
541 }
542 }
543
544 // Fallback to shop page if no products found
545 return get_permalink(wc_get_page_id('shop'));
546 }
547 }
548
549
550 Builder_Integration::instance();
551