PluginProbe
Commerce7 for WordPress / trunk
Commerce7 for WordPress vtrunk
1.8.1 1.8.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.3 1.2.0 1.2.1 1.2.2 1.2.3 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.3.4 1.3.5 1.4.0 1.4.1 All 37 releases
wp-commerce7 / includes / class-c7wp.php

class-c7wp.php in Commerce7 for WordPress trunk, at includes/class-c7wp.php

1,540 lines 45.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * C7WP Class
4 *
5 * @package wp-commerce7
6 * @author Michael Bourne
7 * @license GPL3
8 * @link https://ursa6.com
9 * @since 1.0.0
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 return;
14 }
15
16 /**
17 * Main plugin class
18 */
19 class C7WP {
20
21 /**
22 * Core singleton class
23 *
24 * @var self - pattern realization
25 */
26 private static $_instance; // phpcs:ignore
27
28 /**
29 * Prefix for plugin
30 *
31 * @var $prefix
32 */
33 private $prefix;
34
35 /**
36 * C7 widgets version
37 *
38 * @var $widgetsver
39 */
40 private $widgetsver;
41
42 /**
43 * SEO plugin detected
44 *
45 * @var $seoplugin
46 */
47 private $seoplugin;
48
49 /**
50 * Cached plugin settings
51 *
52 * @var array
53 */
54 private $settings;
55
56
57 /**
58 * Constructor.
59 */
60 private function __construct() {
61
62 // add menu item
63 add_action( 'admin_menu', array( $this, 'add_admin_menu' ), 99 );
64
65 // Admin Init
66 add_action( 'admin_init', array( $this, 'admin_init' ) );
67
68 // WP Init
69 add_action( 'init', array( $this, 'public_init' ) );
70
71 // query vars
72 add_filter( 'query_vars', array( $this, 'register_query_vars' ) );
73
74 // Pagebuilder support
75 add_action( 'init', array( $this, 'load_elements' ) );
76 add_action( 'fl_builder_loaded', array( $this, 'load_beaverbuilder_elements' ) );
77 add_action( 'init', array( $this, 'load_beaverbuilder_elements' ), 11 );
78 add_action( 'after_setup_theme', array( $this, 'load_cs_elements' ) );
79 add_action( 'elementor/widgets/register', array( $this, 'c7wp_elementor_registered' ) );
80 add_action( 'elementor/elements/categories_registered', array( $this, 'c7wp_add_elementor_widget_categories' ) );
81 add_filter( 'block_categories_all', array( $this, 'c7wp_block_categories' ), 10, 2 );
82
83 // add admin styles and scripts
84 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
85 add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'elementor_editor_enqueue_scripts' ) );
86 add_action( 'elementor/frontend/after_enqueue_scripts', array( $this, 'elementor_frontend_assets_fallback' ) );
87 add_action( 'after_setup_theme', array( $this, 'load_c7_css' ), 9 );
88 add_filter( 'body_class', array( $this, 'add_body_class' ) );
89
90 // for front end
91 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 10 );
92 add_action( 'wp_enqueue_scripts_clean', array( $this, 'enqueue_scripts' ), 10 );
93 add_filter( 'script_loader_tag', array( $this, 'add_data_to_c7_script' ), 10, 3 );
94 add_action( 'wp_footer', array( $this, 'footer_inject' ) );
95
96 // Backend
97 add_filter( 'display_post_states', array( $this, 'add_display_post_states' ), 10, 2 );
98
99 // main variables
100 $this->prefix = 'c7wp_';
101 $this->seoplugin = false;
102
103 // Cache settings to avoid multiple database calls
104 $this->settings = get_option( 'c7wp_settings', array() );
105 if ( isset( $this->settings['c7wp_widget_version'] ) && ! empty( $this->settings['c7wp_widget_version'] ) ) {
106 $this->widgetsver = esc_attr( $this->settings['c7wp_widget_version'] );
107 } else {
108 $this->widgetsver = 'v2';
109 }
110
111 // load health check integration
112 require_once C7WP_ROOT . '/includes/health-check.php';
113
114 // c7 div output for certain pagebuilders
115 add_shortcode( 'c7wp', array( $this, 'c7wp_shortcode' ) );
116 }
117
118 /**
119 * Get the instance of C7WP Plugins
120 *
121 * @return self
122 */
123 public static function getInstance() { // phpcs:ignore
124 if ( ! ( self::$_instance instanceof self ) ) {
125 self::$_instance = new self();
126 }
127
128 return self::$_instance;
129 }
130
131 /**
132 * Get plugin settings with caching
133 *
134 * @return array Plugin settings
135 */
136 public function get_settings() {
137 return $this->settings;
138 }
139
140 /**
141 * Get active Commerce7 widget version.
142 *
143 * @return string
144 */
145 public function get_widgetsver() {
146 return $this->widgetsver;
147 }
148
149 /**
150 * Refresh cached settings
151 */
152 public function refresh_settings() {
153 $this->settings = get_option( 'c7wp_settings', array() );
154 }
155
156 /**
157 * @param mixed $instance Singleton instance.
158 */
159 public static function setInstance( $instance ) { // phpcs:ignore
160 self::$_instance = $instance;
161 }
162
163 /**
164 * Init main functions (for hook admin_init)
165 */
166 public function admin_init() {
167
168 if ( ! isset( $this->settings['c7wp_frontend_routes'] ) || ! is_array( $this->settings['c7wp_frontend_routes'] ) ) {
169 $this->settings['c7wp_frontend_routes'] = array(
170 'profile' => 'profile',
171 'collection' => 'collection',
172 'product' => 'product',
173 'club' => 'club',
174 'checkout' => 'checkout',
175 'cart' => 'cart',
176 'privacy' => 'privacy',
177 'terms' => 'terms',
178 'reservation' => 'reservation',
179 );
180 update_option( 'c7wp_settings', $this->settings, true );
181 $this->refresh_settings();
182 }
183
184 $this->settings_init();
185 }
186
187 /**
188 * Load Commerce7 CSS in the editor at a low priority so themes can override it.
189 *
190 * @return void
191 */
192 public function load_c7_css() {
193 /**
194 * Filter: `c7wp_enqueue_c7_css_admin`
195 *
196 * Filter for enqueueing the Commerce7 provided CSS file on the backend.
197 * Useful for Gutenberg rendered blocks, but it can override theme styles in the editor.
198 *
199 * @param bool Should the CSS file be enqueued? Default: true
200 */
201 if ( apply_filters( 'c7wp_enqueue_c7_css_admin', true ) ) {
202 switch ( $this->widgetsver ) {
203 case 'v2':
204 add_editor_style( 'https://cdn.commerce7.com/v2/commerce7.css' );
205 break;
206 case 'v2-compat':
207 add_editor_style( 'https://cdn.commerce7.com/v2/commerce7.css' );
208 break;
209 case 'beta':
210 add_editor_style( 'https://cdn.commerce7.com/beta/commerce7.css' );
211 break;
212 }
213 }
214 }
215
216 /**
217 * Public Init main functions
218 */
219 public function public_init() {
220
221 $this->add_c7_rewrites();
222
223 if ( get_option( 'c7wp_activation' ) === true ) {
224 delete_option( 'c7wp_activation' );
225 flush_rewrite_rules();
226 }
227 }
228
229 /**
230 * Register custom query var for later use
231 */
232 public function register_query_vars( $vars ) {
233 $vars[] = 'c7slug';
234 return $vars;
235 }
236
237 /**
238 * Register WPBakery elements on vc_before_init.
239 */
240 public function load_wpbakery_elements() {
241 if ( ! function_exists( 'vc_map' ) ) {
242 return;
243 }
244
245 require_once C7WP_ROOT . '/includes/wpbakery/load.php';
246 }
247
248 /**
249 * Register Beaver Builder modules after FL Builder loads.
250 */
251 public function load_beaverbuilder_elements() {
252 static $loaded = false;
253
254 if ( $loaded || ! defined( 'FL_BUILDER_VERSION' ) || ! class_exists( 'FLBuilder' ) ) {
255 return;
256 }
257
258 $loaded = true;
259 require_once C7WP_ROOT . '/includes/beaverbuilder/load.php';
260 }
261
262 /**
263 * Load elements after plugins loaded
264 */
265 public function load_elements() {
266
267 // WP Bakery Support
268 if ( defined( 'WPB_VC_VERSION' ) ) {
269 add_action( 'vc_before_init', array( $this, 'load_wpbakery_elements' ) );
270 }
271
272 // Gutenberg support
273 $minimum_wp_version = '5.4';
274 if ( version_compare( $GLOBALS['wp_version'], $minimum_wp_version, '>' ) ) {
275 require_once C7WP_ROOT . '/includes/gutenberg/load.php';
276 // Canonical fix
277 require_once C7WP_ROOT . '/includes/wordpress/load.php';
278 }
279
280 // Yoast Support
281 if ( defined( 'WPSEO_VERSION' ) ) {
282 require_once C7WP_ROOT . '/includes/yoast/load.php';
283 $this->seoplugin = true;
284 }
285
286 // RankMath support
287 if ( defined( 'RANK_MATH_VERSION' ) ) {
288 require_once C7WP_ROOT . '/includes/rankmath/load.php';
289 $this->seoplugin = true;
290 }
291
292 // All In One SEO
293 if ( defined( 'AIOSEO_VERSION' ) ) {
294 require_once C7WP_ROOT . '/includes/aioseo/load.php';
295 $this->seoplugin = true;
296 }
297
298 // SEOpress
299 if ( defined( 'SEOPRESS_VERSION' ) ) {
300 require_once C7WP_ROOT . '/includes/seopress/load.php';
301 $this->seoplugin = true;
302 }
303 }
304
305 /**
306 * Add custom Gutenberg Block Category
307 *
308 * @param array $categories Gutenberg categories.
309 * @param object $post WP_Post object.
310 * @return array $categories
311 */
312 public function c7wp_block_categories( $categories, $post ) {
313 return array_merge(
314 $categories,
315 array(
316 array(
317 'slug' => 'commerce7',
318 'title' => __( 'Commerce7', 'wp-commerce7' ),
319 'icon' => null,
320 ),
321 )
322 );
323 }
324
325 /**
326 * Cornerstone element
327 */
328 public function load_cs_elements() {
329 if ( function_exists( 'cs_register_element' ) ) {
330 require_once C7WP_ROOT . '/includes/themeco/pro/load.php';
331 } elseif ( class_exists( 'Cornerstone_Plugin' ) || function_exists( 'cornerstone_boot' ) || function_exists( 'cornerstone_register_element' ) ) {
332 require_once C7WP_ROOT . '/includes/themeco/legacy/load.php';
333 }
334 }
335
336 /**
337 * Elementor Category
338 */
339 public function c7wp_add_elementor_widget_categories( $elements_manager ) {
340 $elements_manager->add_category(
341 'commerce7',
342 array(
343 'title' => __( 'Commerce7', 'wp-commerce7' ),
344 'icon' => 'fa fa-shopping-cart',
345 ),
346 );
347 }
348
349 /**
350 * Elementor element
351 */
352 public function c7wp_elementor_registered() {
353 if ( class_exists( '\Elementor\Plugin' ) ) {
354 require_once C7WP_ROOT . '/includes/elementor/load.php';
355 }
356 }
357
358 /**
359 * Add new pages to admin
360 */
361 public function add_admin_menu() {
362
363 add_menu_page(
364 __( 'Commerce7 for WordPress', 'wp-commerce7' ),
365 'Commerce7',
366 'manage_options',
367 'commerce7',
368 array(
369 $this,
370 'options_page',
371 ),
372 C7WP_URI . 'assets/c7sm.svg',
373 79
374 );
375 }
376
377 /**
378 * Render all options
379 */
380 public function options_page() {
381
382 // check user capabilities
383 if ( ! current_user_can( 'manage_options' ) ) {
384 return;
385 }
386
387 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
388 if ( isset( $_GET['settings-updated'] ) && empty( get_settings_errors( 'c7wp_settings' ) ) ) { // phpcs:ignore
389 add_settings_error( 'c7wp_settings', 'c7wp_settings_saved', __( 'Settings Saved', 'wp-commerce7' ), 'updated' );
390 }
391
392 settings_errors( 'c7wp_settings' );
393
394 include_once C7WP_ROOT . '/admin/template.options.page.php';
395 }
396
397
398 /**
399 * C7WP settings init
400 */
401 protected function settings_init() {
402
403 register_setting(
404 'commerce7',
405 'c7wp_settings',
406 array(
407 'sanitize_callback' => array( $this, 'c7wp_settings_callback' ),
408 'default' => array(),
409 )
410 );
411
412 add_settings_section(
413 'c7wp_commerce7_section',
414 false,
415 false,
416 'commerce7'
417 );
418
419 add_settings_field(
420 'c7wp_tenant',
421 __( 'Tenant ID', 'wp-commerce7' ),
422 array(
423 $this,
424 'c7wp_tenant_render',
425 ),
426 'commerce7',
427 'c7wp_commerce7_section'
428 );
429
430 add_settings_field(
431 'c7wp_display_cart',
432 __( 'Display Cart Box Automatically?', 'wp-commerce7' ),
433 array(
434 $this,
435 'c7wp_display_cart_render',
436 ),
437 'commerce7',
438 'c7wp_commerce7_section'
439 );
440
441 add_settings_field(
442 'c7wp_display_cart_location',
443 __( 'Cart Box Location', 'wp-commerce7' ),
444 array(
445 $this,
446 'c7wp_display_cart_location_render',
447 ),
448 'commerce7',
449 'c7wp_commerce7_section'
450 );
451
452 add_settings_field(
453 'c7wp_display_cart_color',
454 __( 'Cart Box Theme', 'wp-commerce7' ),
455 array(
456 $this,
457 'c7wp_display_cart_color_render',
458 ),
459 'commerce7',
460 'c7wp_commerce7_section'
461 );
462
463 add_settings_field(
464 'c7wp_widget_version',
465 __( 'Front-end Widgets Version', 'wp-commerce7' ),
466 array(
467 $this,
468 'c7wp_widget_version_render',
469 ),
470 'commerce7',
471 'c7wp_commerce7_section'
472 );
473
474 add_settings_field(
475 'c7wp_enable_product_reviews',
476 __( 'Enable Product Reviews App for Commerce7', 'wp-commerce7' ),
477 array(
478 $this,
479 'c7wp_enable_product_reviews_render',
480 ),
481 'commerce7',
482 'c7wp_commerce7_section'
483 );
484
485 add_settings_field(
486 'c7wp_enable_custom_routes',
487 __( 'Override front end routes?', 'wp-commerce7' ),
488 array(
489 $this,
490 'c7wp_enable_custom_routes_render',
491 ),
492 'commerce7',
493 'c7wp_commerce7_section'
494 );
495
496 add_settings_field(
497 'c7wp_frontend_routes',
498 __( 'Custom Front-end Routes', 'wp-commerce7' ),
499 array(
500 $this,
501 'c7wp_frontend_routes_render',
502 ),
503 'commerce7',
504 'c7wp_commerce7_section'
505 );
506 }
507
508 public function c7wp_settings_callback( $input ) {
509
510 $output = array();
511
512 foreach ( $input as $key => $value ) {
513 if ( isset( $input[ $key ] ) && ! empty( $value ) ) {
514 if ( is_array( $value ) ) {
515 foreach ( $value as $subkey => $subvalue ) {
516 $output[ $key ][ $subkey ] = $this->sanitize_setting_field( $key, $subkey, $subvalue );
517 }
518 } else {
519 $output[ $key ] = $this->sanitize_setting_field( $key, null, $value );
520 }
521 }
522 }
523
524 $this->settings = $output;
525
526 return apply_filters( 'c7wp_settings_post_validation', $output, $input );
527 }
528
529 /**
530 * Sanitize individual setting fields based on field type
531 *
532 * @param string $key The setting key.
533 * @param string|null $subkey The subkey for array values.
534 * @param mixed $value The value to sanitize.
535 * @return mixed The sanitized value.
536 */
537 private function sanitize_setting_field( $key, $subkey, $value ) {
538 // Handle tenant ID specifically
539 if ( 'c7wp_tenant' === $key ) {
540 // Tenant ID should be alphanumeric with possible hyphens/underscores
541 return trim( sanitize_text_field( $value ) );
542 }
543
544 // Handle frontend routes (page slugs)
545 if ( 'c7wp_frontend_routes' === $key && ! is_null( $subkey ) ) {
546 // Page slugs should be valid WordPress slugs
547 $sanitized = sanitize_title( $value );
548 // Ensure it's not empty and doesn't conflict with WordPress reserved terms
549 $reserved_terms = array( 'admin', 'api', 'wp-admin', 'wp-content', 'wp-includes' );
550 if ( empty( $sanitized ) || in_array( $sanitized, $reserved_terms, true ) ) {
551 // Return default value if invalid
552 $defaults = array(
553 'profile' => 'profile',
554 'collection' => 'collection',
555 'product' => 'product',
556 'club' => 'club',
557 'checkout' => 'checkout',
558 'cart' => 'cart',
559 'reservation' => 'reservation',
560 );
561 return isset( $defaults[ $subkey ] ) ? $defaults[ $subkey ] : sanitize_title( $value );
562 }
563 return $sanitized;
564 }
565
566 // Handle select fields (these should be from predefined options)
567 $select_fields = array(
568 'c7wp_display_cart' => array( 'yes', 'no' ),
569 'c7wp_display_cart_location' => array( 'tl', 'tr', 'br', 'bl' ),
570 'c7wp_display_cart_color' => array( 'light', 'dark' ),
571 'c7wp_widget_version' => array( 'v2', 'v2-compat', 'beta' ),
572 'c7wp_enable_product_reviews' => array( 'yes', 'no' ),
573 'c7wp_enable_custom_routes' => array( 'yes', 'no' ),
574 );
575
576 if ( isset( $select_fields[ $key ] ) ) {
577 $allowed_values = $select_fields[ $key ];
578 if ( in_array( $value, $allowed_values, true ) ) {
579 return $value;
580 }
581 // Return default value if invalid
582 $defaults = array(
583 'c7wp_display_cart' => 'no',
584 'c7wp_display_cart_location' => 'tr',
585 'c7wp_display_cart_color' => 'light',
586 'c7wp_widget_version' => 'v2',
587 'c7wp_enable_product_reviews' => 'no',
588 'c7wp_enable_custom_routes' => 'no',
589 );
590 return isset( $defaults[ $key ] ) ? $defaults[ $key ] : '';
591 }
592
593 // Default sanitization for other fields
594 return sanitize_text_field( $value );
595 }
596
597 /**
598 * Default Commerce7 front-end route segments.
599 *
600 * @return array<string, string>
601 */
602 private function get_default_frontend_routes() {
603 return array(
604 'profile' => 'profile',
605 'collection' => 'collection',
606 'product' => 'product',
607 'club' => 'club',
608 'checkout' => 'checkout',
609 'cart' => 'cart',
610 'reservation' => 'reservation',
611 );
612 }
613
614 /**
615 * Settings exposed to storefront JavaScript (Product Reviews embed, club selector, etc.).
616 *
617 * @return array<string, mixed>
618 */
619 public function get_public_js_settings() {
620 $options = $this->settings;
621
622 $settings = array(
623 'tenant' => isset( $options['c7wp_tenant'] ) ? $options['c7wp_tenant'] : '',
624 'c7wp_frontend_routes' => ( isset( $options['c7wp_frontend_routes'] ) && is_array( $options['c7wp_frontend_routes'] ) )
625 ? $options['c7wp_frontend_routes']
626 : $this->get_default_frontend_routes(),
627 );
628
629 /**
630 * Filter storefront `window.c7wp_settings` passed to Commerce7 integrations.
631 *
632 * @param array<string, mixed> $settings Public JS settings.
633 */
634 return apply_filters( 'c7wp_public_js_settings', $settings );
635 }
636
637 /**
638 * Whether the Product Reviews embed script should load on the current request.
639 *
640 * @return bool
641 */
642 private function should_enqueue_product_reviews() {
643 $options = $this->settings;
644
645 if ( 'yes' !== ( $options['c7wp_enable_product_reviews'] ?? 'no' ) ) {
646 return false;
647 }
648
649 if ( empty( $options['c7wp_tenant'] ) ) {
650 return false;
651 }
652
653 /**
654 * Filter whether the Product Reviews embed script is enqueued.
655 *
656 * @param bool $enqueue Whether to enqueue the embed script.
657 */
658 return (bool) apply_filters( 'c7wp_enqueue_product_reviews', true );
659 }
660
661 public function c7wp_tenant_render() {
662
663 $options = $this->settings;
664 ?>
665 <input type='text' name='c7wp_settings[c7wp_tenant]' value='<?php echo esc_attr( $options['c7wp_tenant'] ); ?>'>
666 <?php
667 }
668
669 public function c7wp_display_cart_render() {
670
671 $options = $this->settings;
672 ?>
673 <select name='c7wp_settings[c7wp_display_cart]' class='c7displaycart'>
674 <option value='no' <?php selected( $options['c7wp_display_cart'], 'no' ); ?>><?php esc_html_e( 'No', 'wp-commerce7' ); ?></option>
675 <option value='yes' <?php selected( $options['c7wp_display_cart'], 'yes' ); ?>><?php esc_html_e( 'Yes', 'wp-commerce7' ); ?></option>
676 </select>
677 <p><small>
678 <?php
679 echo wp_kses(
680 __(
681 'If set to <strong>yes</strong>, this plugin will add a floating login+cart box to the front end of your website.<br>If set to <strong>no</strong>, you will need to add the <code>[c7wp type=\'login\']</code> and <code>[c7wp type=\'cart\']</code> shortcodes (or Commerce7\'s HTML) to your header manually.<br>We recommend setting this to <strong>no</strong> and placing your cart manually.',
682 'wp-commerce7'
683 ),
684 array(
685 'strong' => array(),
686 'code' => array(),
687 'br' => array(),
688 )
689 );
690 ?>
691 </small></p>
692 <?php
693 }
694
695 public function c7wp_display_cart_location_render() {
696
697 $options = $this->settings;
698 $disabled = ( 'no' === $options['c7wp_display_cart'] ) ? 'disabled' : '';
699 if ( ! isset( $options['c7wp_display_cart_location'] ) ) {
700 $options['c7wp_display_cart_location'] = 'tr';
701 }
702 ?>
703 <select name='c7wp_settings[c7wp_display_cart_location]' class='c7cartloc' <?php echo esc_attr( $disabled ); ?> >
704 <option value='tl' <?php selected( $options['c7wp_display_cart_location'], 'tl' ); ?>><?php esc_html_e( 'Top left', 'wp-commerce7' ); ?></option>
705 <option value='tr' <?php selected( $options['c7wp_display_cart_location'], 'tr' ); ?>><?php esc_html_e( 'Top right', 'wp-commerce7' ); ?></option>
706 <option value='br' <?php selected( $options['c7wp_display_cart_location'], 'br' ); ?>><?php esc_html_e( 'Bottom right', 'wp-commerce7' ); ?></option>
707 <option value='bl' <?php selected( $options['c7wp_display_cart_location'], 'bl' ); ?>><?php esc_html_e( 'Bottom left', 'wp-commerce7' ); ?></option>
708 </select>
709
710 <?php
711 }
712
713 public function c7wp_display_cart_color_render() {
714
715 $options = $this->settings;
716 $disabled = ( 'no' === $options['c7wp_display_cart'] ) ? 'disabled' : '';
717 if ( ! isset( $options['c7wp_display_cart_color'] ) ) {
718 $options['c7wp_display_cart_color'] = 'light';
719 }
720 ?>
721 <select name='c7wp_settings[c7wp_display_cart_color]' class='c7cartcolor' <?php echo esc_attr( $disabled ); ?> >
722 <option value='light' <?php selected( $options['c7wp_display_cart_color'], 'light' ); ?>><?php esc_html_e( 'Light website', 'wp-commerce7' ); ?></option>
723 <option value='dark' <?php selected( $options['c7wp_display_cart_color'], 'dark' ); ?>><?php esc_html_e( 'Dark website', 'wp-commerce7' ); ?></option>
724 </select>
725 <p><small>
726 <?php
727 esc_html_e( 'Please select the color theme for the Cart Box. "Light website" is for light colored or white background site, "Dark website" is for dark colored or black background sites.', 'wp-commerce7' );
728 ?>
729 </small></p>
730 <?php
731 }
732
733 public function c7wp_widget_version_render() {
734
735 $options = $this->settings;
736 ?>
737 <select name='c7wp_settings[c7wp_widget_version]' class='c7widgetversion'>
738 <option value='v2-compat' <?php selected( $options['c7wp_widget_version'], 'v2-compat' ); ?>><?php esc_html_e( 'V2 (Compatibility Mode)', 'wp-commerce7' ); ?></option>
739 <option value='v2' <?php selected( $options['c7wp_widget_version'], 'v2' ); ?>><?php esc_html_e( 'V2', 'wp-commerce7' ); ?></option>
740 <option value='beta' <?php selected( $options['c7wp_widget_version'], 'beta' ); ?>><?php esc_html_e( 'V1', 'wp-commerce7' ); ?></option>
741 </select>
742 <p><small>
743 <?php
744 echo wp_kses(
745 __(
746 '<strong>V2 (Compatibility Mode):</strong> This will load the V2 widgets in a way that is compatible with more themes and plugins. Safe for all websites, and <strong>the recommended option for everyone</strong>.
747 <br>
748 <strong>V2:</strong> The standard front-end widgets version used by most wineries.
749 <br>
750 <strong>V1:</strong> Only a select few legacy sites are still using the old beta/V1 widgets.',
751 'wp-commerce7',
752 ),
753 array(
754 'strong' => array(),
755 'br' => array(),
756 )
757 );
758 ?>
759 </small></p>
760 <?php
761 }
762
763 /**
764 * Render Product Reviews embed setting field.
765 */
766 public function c7wp_enable_product_reviews_render() {
767
768 $options = $this->settings;
769 $value = isset( $options['c7wp_enable_product_reviews'] ) ? $options['c7wp_enable_product_reviews'] : 'no';
770 ?>
771 <select name='c7wp_settings[c7wp_enable_product_reviews]' class='c7productreviews'>
772 <option value='no' <?php selected( $value, 'no' ); ?>><?php esc_html_e( 'No', 'wp-commerce7' ); ?></option>
773 <option value='yes' <?php selected( $value, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wp-commerce7' ); ?></option>
774 </select>
775 <p><small>
776 <?php
777 echo wp_kses(
778 __(
779 'Loads the Commerce7 Product Reviews embed on your storefront when set to <strong>yes</strong>. Install and configure the Product Reviews app in your Commerce7 admin <strong>first</strong> (PDP/PLP selectors and moderation).',
780 'wp-commerce7'
781 ),
782 array(
783 'strong' => array(),
784 'a' => array(
785 'href' => array(),
786 'target' => array(),
787 'rel' => array(),
788 ),
789 )
790 );
791 ?>
792 </small></p>
793 <?php
794 }
795
796 public function c7wp_enable_custom_routes_render() {
797
798 $options = $this->settings;
799 $disabled = ( 'beta' === $options['c7wp_widget_version'] ) ? 'disabled' : '';
800 ?>
801 <select name='c7wp_settings[c7wp_enable_custom_routes]' <?php echo esc_attr( $disabled ); ?> >
802 <option value='no'
803 <?php
804 if ( isset( $options['c7wp_enable_custom_routes'] ) ) {
805 selected(
806 $options['c7wp_enable_custom_routes'],
807 'no'
808 );
809 }
810 ?>
811 ><?php esc_html_e( 'No', 'wp-commerce7' ); ?></option>
812 <option value='yes'
813 <?php
814 if ( isset( $options['c7wp_enable_custom_routes'] ) ) {
815 selected(
816 $options['c7wp_enable_custom_routes'],
817 'yes'
818 );
819 }
820 ?>
821 ><?php esc_html_e( 'Yes', 'wp-commerce7' ); ?></option>
822 </select>
823 <p><small>
824 <?php
825 echo wp_kses( __( 'For V2 frontend only. If set to <strong>yes</strong>, this plugin will allow you to set custom routing options (page slugs) below. Enable this option and save changes to edit routes below.', 'wp-commerce7' ), array( 'strong' => array() ) );
826 ?>
827 </small></p>
828 <?php
829 }
830
831 public function c7wp_frontend_routes_render() {
832
833 $options = $this->settings;
834 $disabled = ( 'yes' === $options['c7wp_enable_custom_routes'] ) ? '' : 'disabled';
835 ?>
836 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][cart]'
837 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['cart'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
838 <span>&#47;</span>
839 <label for="c7wp_settings[c7wp_frontend_routes][cart]"><?php esc_html_e( 'Cart Page', 'wp-commerce7' ); ?></label></div>
840
841 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][checkout]'
842 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['checkout'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
843 <span>&#47;</span>
844 <label for="c7wp_settings[c7wp_frontend_routes][checkout]"><?php esc_html_e( 'Checkout Page', 'wp-commerce7' ); ?></label></div>
845
846 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][club]'
847 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['club'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
848 <span>&#47;</span>
849 <label for="c7wp_settings[c7wp_frontend_routes][club]"><?php esc_html_e( 'Club Pages', 'wp-commerce7' ); ?></label></div>
850
851 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][collection]'
852 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['collection'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
853 <span>&#47;</span>
854 <label for="c7wp_settings[c7wp_frontend_routes][collection]"><?php esc_html_e( 'Collection Pages', 'wp-commerce7' ); ?></label></div>
855
856 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][product]'
857 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['product'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
858 <span>&#47;</span>
859 <label for="c7wp_settings[c7wp_frontend_routes][product]"><?php esc_html_e( 'Product Pages', 'wp-commerce7' ); ?></label></div>
860
861 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][profile]'
862 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['profile'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
863 <span>&#47;</span>
864 <label for="c7wp_settings[c7wp_frontend_routes][profile]"><?php esc_html_e( 'Profile Page', 'wp-commerce7' ); ?></label></div>
865
866 <div class="routing-row"><input type='text' name='c7wp_settings[c7wp_frontend_routes][reservation]'
867 value='<?php echo esc_attr( $options['c7wp_frontend_routes']['reservation'] ); ?>' class="routing-field" <?php echo esc_attr( $disabled ); ?>>
868 <span>&#47;</span>
869 <label for="c7wp_settings[c7wp_frontend_routes][reservation]"><?php esc_html_e( 'Reservation Page', 'wp-commerce7' ); ?></label></div>
870
871 <p><small>
872 <?php
873 // Define the allowed HTML tags and attributes
874 $allowed_html = array(
875 'a' => array(
876 'href' => array(),
877 'target' => array(),
878 ),
879 'strong' => array(),
880 'br' => array(),
881 );
882
883 // Define the URLs
884 $c7_url = 'https://admin.platform.commerce7.com/developer/frontend-v2';
885 $permalinks_url = esc_url( admin_url( 'options-permalink.php', 'https' ) );
886
887 /* translators: %1$s: URL to the routing settings in Commerce7 %2$s: URL to the permalinks settings in WordPress */
888 $string = __(
889 'These routes <strong>must</strong> match your <a href="%1$s" target="_blank">routing settings in Commerce7</a>.
890 <br>
891 Make sure you edit the slugs of any existing pages if you are changing these values.
892 <br>
893 You <strong>must</strong> <a href="%2$s">resave your permalinks in WordPress</a> after changing these settings.',
894 'wp-commerce7',
895 );
896
897 // Replace the placeholders with actual URLs
898 $formatted_string = sprintf( $string, esc_url( $c7_url ), esc_url( $permalinks_url ) );
899
900 // Use wp_kses to allow specific HTML tags in the translated string
901 echo wp_kses( $formatted_string, $allowed_html );
902 ?>
903 </small></p>
904 <?php
905 }
906
907
908 /**
909 * Enqueue admin scripts
910 */
911 public function admin_enqueue_scripts() {
912 wp_enqueue_style( 'wp-commerce7-admin', C7WP_URI . 'assets/admin/css/commerce7-for-wordpress-admin.css', array(), C7WP_VERSION );
913 }
914
915 /**
916 * Enqueue public scripts
917 */
918 public function enqueue_scripts() {
919
920 $options = $this->settings;
921
922 /**
923 * Load the magic cart box CSS if enabled
924 */
925 if ( 'yes' === $options['c7wp_display_cart'] ) {
926 wp_enqueue_style( 'wp-commerce7', C7WP_URI . 'assets/public/css/commerce7-for-wordpress.css', array(), C7WP_VERSION );
927 }
928
929 /**
930 * Load mandatory Commerce7 JS file.
931 */
932 switch ( $this->widgetsver ) {
933 case 'v2':
934 wp_register_script( 'c7js', 'https://cdn.commerce7.com/v2/commerce7.js', array(), null, true ); // phpcs:ignore
935 break;
936 case 'v2-compat':
937 wp_register_script( 'c7js', 'https://cdn.commerce7.com/v2/commerce7-eventload.js', array(), null, true ); // phpcs:ignore
938 break;
939 case 'beta':
940 wp_register_script( 'c7js', 'https://cdn.commerce7.com/beta/commerce7.js', array(), null, true ); // phpcs:ignore
941 break;
942 default:
943 wp_register_script( 'c7js', 'https://cdn.commerce7.com/v2/commerce7.js', array(), null, true ); // phpcs:ignore
944 break;
945 }
946 /**
947 * Enqueues the 'c7js' script if the following conditions are met:
948 * - The current request is not an admin request.
949 * - The 'ct_builder' query parameter is not present in the URL. (Oxygen Builder)
950 * - The 'et_fb' query parameter is not present in the URL (Divi Builder).
951 *
952 * This ensures that the C7 script is only enqueued on the front-end and not when
953 * using certain page builders like Oxygen.
954 */
955 if ( ! is_admin() && empty( $_GET['ct_builder'] ) && empty( $_GET['et_fb'] ) ) {
956 wp_enqueue_script( 'c7js' );
957 wp_localize_script( 'c7js', 'c7wp_settings', $this->get_public_js_settings() );
958
959 if ( $this->should_enqueue_product_reviews() ) {
960 $reviews_src = apply_filters(
961 'c7wp_product_reviews_embed_src',
962 'https://c7-product-reviews.ursa6.com/embed/invoke-wp-plugin.js'
963 );
964 wp_register_script( 'c7wp-product-reviews', $reviews_src, array( 'c7js' ), null, true ); // phpcs:ignore
965 wp_enqueue_script( 'c7wp-product-reviews' );
966 }
967 }
968
969 // Enqueue block-specific assets only when needed
970 $this->enqueue_block_assets();
971
972 /**
973 * Filter: `c7wp_enqueue_c7_css`
974 *
975 * Filter for enqueueing the Commerce7 provided CSS file.
976 *
977 * @param bool Should the CSS file be enqueued? Default: true
978 */
979 if ( apply_filters( 'c7wp_enqueue_c7_css', true ) ) {
980 switch ( $this->widgetsver ) {
981 case 'v2':
982 wp_register_style( 'c7css', 'https://cdn.commerce7.com/v2/commerce7.css', false, null ); // phpcs:ignore
983 break;
984 case 'v2-compat':
985 wp_register_style( 'c7css', 'https://cdn.commerce7.com/v2/commerce7.css', false, null ); // phpcs:ignore
986 break;
987 case 'beta':
988 wp_register_style( 'c7css', 'https://cdn.commerce7.com/beta/commerce7.css', false, null ); // phpcs:ignore
989 break;
990 default:
991 wp_register_style( 'c7css', 'https://cdn.commerce7.com/v2/commerce7.css', false, null ); // phpcs:ignore
992 break;
993 }
994 wp_enqueue_style( 'c7css' );
995 }
996
997 /**
998 * Add editor styles for custom blocks
999 */
1000 if ( is_user_logged_in() && current_user_can( 'edit_pages' ) ) {
1001 wp_enqueue_style( 'wp-commerce7-pagebuilders', C7WP_URI . 'assets/public/css/commerce7-for-wordpress-editors.css', array(), C7WP_VERSION );
1002 }
1003
1004 /**
1005 * Add canonical fix if SEO plugins present
1006 */
1007 if ( $this->seoplugin ) {
1008
1009 if ( ! isset( $options['c7wp_frontend_routes'] ) || ! is_array( $options['c7wp_frontend_routes'] ) ) {
1010 $product_route = 'product';
1011 $collection_route = 'collection';
1012 } else {
1013 $product_route = $options['c7wp_frontend_routes']['product'];
1014 $collection_route = $options['c7wp_frontend_routes']['collection'];
1015 }
1016
1017 if ( is_page( array( $product_route, $collection_route ) ) ) {
1018 wp_register_script( 'c7wp-seo', C7WP_URI . 'assets/public/js/c7wp-seo.js', array(), C7WP_VERSION, true ); // phpcs:ignore
1019 wp_enqueue_script( 'c7wp-seo' );
1020 }
1021 }
1022 }
1023
1024 /**
1025 * Enqueue block-specific assets only when blocks are present
1026 */
1027 private function enqueue_block_assets() {
1028 global $post;
1029
1030 if ( ! $post ) {
1031 return;
1032 }
1033
1034 $post_content = $post->post_content;
1035
1036 // Check for Gutenberg blocks
1037 if ( has_blocks( $post_content ) ) {
1038 $blocks = parse_blocks( $post_content );
1039 $this->enqueue_blocks_assets( $blocks );
1040 }
1041
1042 if ( C7WP_Widgets::content_has_c7_widgets( $post_content ) ) {
1043 $this->enqueue_shortcode_assets();
1044 }
1045
1046 $widget_slugs = C7WP_Widgets::get_slugs_in_content( $post_content );
1047 foreach ( $widget_slugs as $slug ) {
1048 if ( 'clubselector' === $slug ) {
1049 C7WP_Widgets::enqueue_clubselector_assets();
1050 }
1051 if ( 'clubselector-v2' === $slug ) {
1052 C7WP_Widgets::enqueue_clubselector_v2_assets();
1053 }
1054 }
1055 }
1056
1057 /**
1058 * Recursively enqueue assets for blocks
1059 *
1060 * @param array $blocks Array of blocks to check.
1061 */
1062 private function enqueue_blocks_assets( $blocks ) {
1063 foreach ( $blocks as $block ) {
1064 // Check if this is a Commerce7 block
1065 if ( isset( $block['blockName'] ) && strpos( $block['blockName'], 'c7wp/' ) === 0 ) {
1066 $block_type = str_replace( 'c7wp/', '', $block['blockName'] );
1067 $this->enqueue_specific_block_assets( $block_type );
1068 }
1069
1070 // Check inner blocks
1071 if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) ) {
1072 $this->enqueue_blocks_assets( $block['innerBlocks'] );
1073 }
1074 }
1075 }
1076
1077 /**
1078 * Enqueue assets for a specific block type
1079 *
1080 * @param string $block_type The block type to enqueue assets for.
1081 */
1082 private function enqueue_specific_block_assets( $block_type ) {
1083 $options = $this->settings;
1084 $widgetsver = $options['c7wp_widget_version'];
1085
1086 // Determine the correct directory
1087 $dir = in_array( $widgetsver, array( 'v2', 'v2-compat' ), true ) ? 'blocks-v2' : 'blocks';
1088
1089 // Enqueue frontend styles
1090 $frontend_css_path = $dir . '/' . $block_type . '/frontend.css';
1091 if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_css_path ) ) {
1092 wp_enqueue_style(
1093 'c7wp-' . $block_type . '-frontend',
1094 C7WP_URI . 'includes/gutenberg/' . $frontend_css_path,
1095 array(),
1096 C7WP_VERSION
1097 );
1098 }
1099
1100 // Enqueue frontend scripts
1101 $frontend_js_path = $dir . '/' . $block_type . '/frontend.js';
1102 if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_js_path ) ) {
1103 wp_enqueue_script(
1104 'c7wp-' . $block_type . '-frontend',
1105 C7WP_URI . 'includes/gutenberg/' . $frontend_js_path,
1106 array(),
1107 C7WP_VERSION,
1108 true
1109 );
1110
1111 // Localize settings for specific blocks
1112 if ( 'clubselector' === $block_type ) {
1113 wp_localize_script(
1114 'c7wp-' . $block_type . '-frontend',
1115 'c7wp_settings',
1116 $this->get_public_js_settings()
1117 );
1118 }
1119 }
1120 }
1121
1122 /**
1123 * Enqueue assets for shortcode usage
1124 */
1125 private function enqueue_shortcode_assets() {
1126 // Enqueue general Commerce7 styles
1127 wp_enqueue_style(
1128 'c7wp-shortcode',
1129 C7WP_URI . 'assets/public/css/commerce7-for-wordpress.css',
1130 array(),
1131 C7WP_VERSION
1132 );
1133 }
1134
1135 /**
1136 * Elementor specific styles
1137 *
1138 * @return void
1139 */
1140 public function elementor_editor_enqueue_scripts() {
1141 wp_enqueue_style( 'wp-commerce7-icons', C7WP_URI . 'assets/admin/css/c7wpicons.css', array(), C7WP_VERSION );
1142 }
1143
1144 /**
1145 * Ensure clubselector frontend assets are loaded for Elementor when Gutenberg is disabled
1146 *
1147 * @return void
1148 */
1149 public function elementor_frontend_assets_fallback() {
1150 if ( ! function_exists( 'register_block_type' ) && empty( $_GET['ct_builder'] ) && empty( $_GET['et_fb'] ) ) { // phpcs:ignore
1151 C7WP_Widgets::register_clubselector_assets( $this );
1152 C7WP_Widgets::register_clubselector_v2_assets( $this );
1153 C7WP_Widgets::enqueue_clubselector_assets();
1154 }
1155 }
1156
1157
1158
1159 /**
1160 * Add custom attributes to enqueued C7 script. Add RocketLoader false sync flag in case customer uses CloudFlare.
1161 *
1162 * @param string $tag HTML script tage for output in head or footer.
1163 * @param string $handle Name registered to the script.
1164 * @param string $src Script source URL.
1165 *
1166 * @return string $tag
1167 */
1168 public function add_data_to_c7_script( $tag, $handle, $src ) {
1169 if ( 'c7js' === $handle ) {
1170 $options = $this->settings;
1171
1172 if ( isset( $options['c7wp_tenant'] ) ) {
1173 $tag = '<script data-cfasync="false" type="text/javascript" src="' . esc_url( $src ) . '" id="c7-javascript" data-tenant="' . esc_attr( $options['c7wp_tenant'] ) . '"></script>'; // phpcs:ignore
1174 }
1175 }
1176
1177 if ( 'c7wp-product-reviews' === $handle ) {
1178 $tag = '<script data-cfasync="false" type="text/javascript" id="c7-product-reviews" src="' . esc_url( $src ) . '"></script>'; // phpcs:ignore
1179 }
1180
1181 return $tag;
1182 }
1183
1184
1185
1186 /**
1187 * Add rewrite rules to WordPress for Commerce7 static routes.
1188 * This works so well that it's been copied line for line by other C7 partners.
1189 * I don't blame them, but really, just use this plugin for your clients. It works.
1190 * If you're copying my code, please respect the law and give me credit for it.
1191 */
1192 public function add_c7_rewrites() {
1193
1194 if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
1195
1196 $options = $this->settings;
1197
1198 if ( isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
1199 $routes = implode( '|', array_values( $options['c7wp_frontend_routes'] ) );
1200
1201 add_rewrite_rule(
1202 '^(' . $routes . ')/(.+)/?$',
1203 'index.php?pagename=$matches[1]&c7slug=$matches[2]',
1204 'top'
1205 );
1206 } else {
1207 add_rewrite_rule(
1208 '^(profile|collection|product|club|checkout|reservation)/(.+)/?$',
1209 'index.php?pagename=$matches[1]&c7slug=$matches[2]',
1210 'top'
1211 );
1212 }
1213 } else {
1214 add_rewrite_rule(
1215 '^(profile|collection|product|club|checkout|reservation)/(.+)/?$',
1216 'index.php?pagename=$matches[1]&c7slug=$matches[2]',
1217 'top'
1218 );
1219 }
1220 }
1221
1222
1223
1224 /**
1225 * Add Cart Box to body
1226 */
1227 public function footer_inject() {
1228
1229 $options = $this->settings;
1230 if ( 'yes' === $options['c7wp_display_cart'] ) {
1231
1232 $color = ( 'dark' === $options['c7wp_display_cart_color'] ) ? 'c7dark' : 'c7light';
1233
1234 switch ( $options['c7wp_display_cart_location'] ) {
1235 case 'tl':
1236 $class = 'top-left ';
1237 break;
1238
1239 case 'tr':
1240 $class = 'top-right ';
1241 break;
1242
1243 case 'br':
1244 $class = 'bottom-right ';
1245 break;
1246
1247 case 'bl':
1248 $class = 'bottom-left ';
1249 break;
1250
1251 default:
1252 $class = 'top-left ';
1253 break;
1254 }
1255
1256 $login = ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ) ) ) ? 'c7-account' : 'c7-login';
1257
1258 echo '<div id="c7wp-cart-box" class="' . esc_attr( $class ) . esc_attr( $color ) . '"><div id="' . esc_attr( $login ) . '"></div><div id="c7-cart"></div></div>';
1259
1260 }
1261 }
1262
1263
1264
1265 /**
1266 * [c7wp] Shortcode for div output (used by pagebuilder elements, or in the editor)
1267 *
1268 * @param array $atts Attributes used on the shortcode.
1269 *
1270 * @return string
1271 */
1272 public function c7wp_shortcode( $atts ) {
1273
1274 $atts = shortcode_atts(
1275 array(
1276 'type' => 'default',
1277 'data' => '',
1278 'join-text' => __( 'Join Club', 'wp-commerce7' ),
1279 'edit-text' => __( 'Edit Membership', 'wp-commerce7' ),
1280 ),
1281 $atts,
1282 'c7wp'
1283 );
1284
1285 $allowed_types = array(
1286 'default',
1287 'personalization',
1288 'buy',
1289 'buyslug',
1290 'subscribe',
1291 'collection',
1292 'login',
1293 'cart',
1294 'reservation',
1295 'form',
1296 'joinnow',
1297 'quickshop',
1298 'createaccount',
1299 'loginform',
1300 'collectionlist',
1301 );
1302
1303 if ( ! in_array( $atts['type'], $allowed_types, true ) ) {
1304 $atts['type'] = 'default';
1305 }
1306
1307 $output = '<div class="c7wp-wrap" data-c7-type="' . $atts['type'] . '">';
1308
1309 if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
1310 $output .= $this->render_v2_widget( $atts );
1311 } else {
1312 $output .= $this->render_v1_widget( $atts );
1313 }
1314
1315 $output .= '</div>';
1316
1317 return $output;
1318 }
1319
1320 /**
1321 * Render V2 widget output
1322 *
1323 * @param array $atts Shortcode attributes.
1324 * @return string Widget HTML.
1325 */
1326 private function render_v2_widget( $atts ) {
1327 switch ( $atts['type'] ) {
1328 case 'default':
1329 return '<div id="c7-content"></div>';
1330
1331 case 'personalization':
1332 return '<div class="c7-personalization" data-block-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1333
1334 case 'subscribe':
1335 return ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-field="true"></div>' : '<div class="c7-subscribe"></div>';
1336
1337 case 'collection':
1338 return '<div class="c7-product-collection" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1339
1340 case 'login':
1341 return '<div id="c7-account"></div>';
1342
1343 case 'cart':
1344 return '<div id="c7-cart"></div>';
1345
1346 case 'reservation':
1347 return '<div class="c7-reservation-availability" data-reservation-type-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1348
1349 case 'form':
1350 return '<div class="c7-custom-form" data-form-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1351
1352 case 'joinnow':
1353 return '<div class="c7-club-join-button"
1354 data-club-slug="' . trim( esc_attr( $atts['data'] ) ) . '"
1355 data-join-text="' . trim( esc_attr( $atts['join-text'] ) ) . '"
1356 data-edit-text="' . trim( esc_attr( $atts['edit-text'] ) ) . '"
1357 ></div>';
1358
1359 case 'buyslug':
1360 return '<div class="c7-buy-product" data-product-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1361
1362 case 'loginform':
1363 return '<div id="c7-login-form" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1364
1365 case 'collectionlist':
1366 return '<div id="c7-collection-list"></div>';
1367
1368 default:
1369 return '<div id="c7-content"></div>';
1370 }
1371 }
1372
1373 /**
1374 * Render V1 widget output
1375 *
1376 * @param array $atts Shortcode attributes.
1377 * @return string Widget HTML.
1378 */
1379 private function render_v1_widget( $atts ) {
1380 switch ( $atts['type'] ) {
1381 case 'default':
1382 return '<div id="c7-content"></div>';
1383
1384 case 'personalization':
1385 return '<div class="c7-personalization" data-block-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1386
1387 case 'buy':
1388 return '<div class="c7-buy-variant" data-sku="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1389
1390 case 'buyslug':
1391 return '<div class="c7-buy-variant" data-product-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1392
1393 case 'subscribe':
1394 return ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-fields="true"></div>' : '<div class="c7-subscribe"></div>';
1395
1396 case 'collection':
1397 return '<div class="c7-product-collection" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1398
1399 case 'login':
1400 return '<div id="c7-login"></div>';
1401
1402 case 'cart':
1403 return '<div id="c7-cart"></div>';
1404
1405 case 'reservation':
1406 return '<div class="c7-reservation-availability" data-reservation-type-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1407
1408 case 'form':
1409 return '<div class="c7-form-wrapper" data-form-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1410
1411 case 'joinnow':
1412 return '<div class="c7-club-join-button" data-club-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1413
1414 case 'quickshop':
1415 return '<div id="c7-quick-shop" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1416
1417 case 'loginform':
1418 return '<div id="c7-login-form" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1419
1420 case 'createaccount':
1421 return '<div id="c7-create-account" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
1422
1423 default:
1424 return '<div id="c7-content"></div>';
1425 }
1426 }
1427
1428 /**
1429 * Add post state text to clarify C7 pages for admins
1430 *
1431 * @param array $post_states Array of post state messages.
1432 * @param object $post WP Post object.
1433 * @return array $post_states
1434 */
1435 public function add_display_post_states( $post_states, $post ) {
1436
1437 $options = $this->settings;
1438 if ( isset( $options['c7wp_widget_version'] ) && 'v2' === $options['c7wp_widget_version']
1439 && isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
1440 $pages = array_values( $options['c7wp_frontend_routes'] );
1441 } else {
1442 $pages = array(
1443 'profile',
1444 'collection',
1445 'product',
1446 'club',
1447 'checkout',
1448 'cart',
1449 'reservation',
1450 );
1451 }
1452
1453 if ( in_array( $post->post_name, $pages, true ) ) {
1454 $post_states['c7wp'] = esc_html__( 'Commerce7 Dynamic Route', 'wp-commerce7' );
1455 }
1456
1457 $static_pages = array(
1458 'privacy',
1459 'terms',
1460 );
1461
1462 if ( in_array( $post->post_name, $static_pages, true ) ) {
1463 $post_states['c7wp-static'] = esc_html__( 'Commerce7 Required Page', 'wp-commerce7' );
1464 }
1465
1466 return $post_states;
1467 }
1468
1469
1470 /**
1471 * Add body class for styles specificity and scope
1472 */
1473 public function add_body_class( $classes ) {
1474 $classes[] = 'c7wp';
1475 return $classes;
1476 }
1477
1478 /**
1479 * Check if a page has proper Commerce7 content
1480 *
1481 * @param int|WP_Post $page Page ID or WP_Post object.
1482 * @return bool True if page has proper C7 content.
1483 */
1484 public function page_has_c7_content( $page ) {
1485 if ( is_numeric( $page ) ) {
1486 $page = get_post( $page );
1487 }
1488
1489 if ( ! $page || 'page' !== $page->post_type ) {
1490 return false;
1491 }
1492
1493 $content = $page->post_content;
1494
1495 // Check for various C7 content patterns
1496 $patterns = array(
1497 '/c7-content/', // Basic C7 div
1498 '/wp:c7wp/', // Gutenberg blocks
1499 '/<!-- wp:c7wp/', // Gutenberg block comments
1500 '/\[c7wp/', // Shortcodes
1501 '/class="c7wp-/', // C7WP wrapper classes
1502 );
1503
1504 foreach ( $patterns as $pattern ) {
1505 if ( preg_match( $pattern, $content ) ) {
1506 return true;
1507 }
1508 }
1509
1510 return false;
1511 }
1512
1513 /**
1514 * Get pages that need Commerce7 content
1515 *
1516 * @return array Array of page objects that need C7 content.
1517 */
1518 public function get_pages_needing_c7_content() {
1519 $options = $this->settings;
1520
1521 if ( isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
1522 $required_pages = array_values( $options['c7wp_frontend_routes'] );
1523 } else {
1524 $required_pages = array( 'profile', 'collection', 'product', 'club', 'checkout', 'cart', 'reservation' );
1525 }
1526
1527 $pages_needing_content = array();
1528
1529 foreach ( $required_pages as $page_slug ) {
1530 $page = get_page_by_path( $page_slug, 'ARRAY_N', 'page' );
1531 if ( $page && ! $this->page_has_c7_content( $page[0] ) ) {
1532 $pages_needing_content[] = get_post( $page[0] );
1533 }
1534 }
1535
1536 return $pages_needing_content;
1537 }
1538
1539 }
1540