Plugin Directory

Changeset 3441297


Ignore:
Timestamp:
01/16/2026 10:30:41 PM (4 weeks ago)
Author:
michaelbourne
Message:

Version 1.6.1

Location:
wp-commerce7/trunk
Files:
18 added
35 edited

Legend:

Unmodified
Added
Removed
  • wp-commerce7/trunk/README.txt

    r3410240 r3441297  
    55Requires at least: 6.0
    66Tested up to: 6.9
    7 Stable tag: 1.5.4
     7Stable tag: 1.6.1
    88License: GPLv3 or later
    99License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
     
    6060== Plugin Removal ==
    6161
    62 Removing this plugin will render your widgets inactive, but will not remove them or the pages created.
     62Removing this plugin will render your widgets and/or blocks inactive, but will not remove them or the pages created. Be sure to clean out all pages of Commerce7 content if removing.
    6363
    6464
     
    7373== Changelog ==
    7474
    75 = 1.5.4 =
     75= 1.6.1 - January 16, 2026 =
     76* Added: alignment controls form blocks
     77
     78= 1.6 - December 15, 2025 =
     79* Large plugin changes, please backup site before updating
     80* Improved: Elementor blocks, adding static previews to some block types for inline styles and visual verifications
     81* Improved: Themeco Cornerstone blocks, adding static previews to some block types for inline styles and visual verifications
     82* Improved: Settings sanitization
     83* Improved: C7WP notices
     84* Improved: Page creation on initial plugin activation
     85* Improved: Plugin settings cache speed
     86* Improved: Pages post state text on dynamic routes
     87* Added: WP Health Check screen
     88* Fixed: RankMath canonical tag
     89* Added: Uninstall handler to remove plugin data
     90
     91
     92
     93= 1.5.4 - August 11, 2025 =
    7694* Fix: Gutenberg block settings for old beta frontend
    7795* Fix: Login widget redirect path on V2 frontend
     
    226244== Upgrade Notice ==
    227245
     246= 1.6.1 =
     247Large plugin update, create a backup before proceeding, and test all widgets on all pages afterwards.
     248
    228249= 1.5.1 =
    229250You may need to resave existing Club Selector blocks if you've used them already.
  • wp-commerce7/trunk/admin/template.options.page.php

    r3292177 r3441297  
    1111
    1212if ( ! defined( 'ABSPATH' ) ) {
    13     return;
     13    return;
    1414}
    1515
     
    1717?>
    1818<div class="c7wp-reset c7wp-wrap c7wp-wrap-about">
    19     <div class="c7wp-content">
    20         <div class="c7wp-main">
     19    <div class="c7wp-content">
     20        <div class="c7wp-main">
    2121
    22             <div class="c7wp-row">
    23                 <div class="c7wp-column">
     22            <div class="c7wp-row">
     23                <div class="c7wp-column">
    2424
    25                     <div class="c7wp-box gradient header">
     25                    <div class="c7wp-box gradient header">
    2626
    27                         <div class="c7wp-box-content cols-2">
    28                             <div class="c7wp-box-content-left">
    29                                 <h1 class="c7wp-box-title"><?php esc_html_e( 'Commerce7 for WordPress', 'wp-commerce7' ); ?></h1>
    30                                 <p class="c7wp-box-content-text"><?php esc_html_e( 'Built for wineries that want more control, better performance, and smarter marketing.', 'wp-commerce7' ); ?></p>
    31                             </div>
     27                        <div class="c7wp-box-content cols-2">
     28                            <div class="c7wp-box-content-left">
     29                                <h1 class="c7wp-box-title"><?php esc_html_e( 'Commerce7 for WordPress', 'wp-commerce7' ); ?></h1>
     30                                <p class="c7wp-box-content-text"><?php esc_html_e( 'Built for wineries that want more control, better performance, and smarter marketing.', 'wp-commerce7' ); ?></p>
     31                            </div>
    3232
    33                             <div class="c7wp-box-content-right">
    34                                 <a href="https://c7wp.com/documentation/" target="_blank" class="c7wp-btn c7wp-btn-primary c7wp-btn-large">
    35                                     <svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm23.744 191.488c-52.096 0-92.928 14.784-123.2 44.352-30.976 29.568-45.76 70.4-45.76 122.496h80.256c0-29.568 5.632-52.8 17.6-68.992 13.376-19.712 35.2-28.864 66.176-28.864 23.936 0 42.944 6.336 56.32 19.712 12.672 13.376 19.712 31.68 19.712 54.912 0 17.6-6.336 34.496-19.008 49.984l-8.448 9.856c-45.76 40.832-73.216 70.4-82.368 89.408-9.856 19.008-14.08 42.24-14.08 68.992v9.856h80.96v-9.856c0-16.896 3.52-31.68 10.56-45.76 6.336-12.672 15.488-24.64 28.16-35.2 33.792-29.568 54.208-48.576 60.544-55.616 16.896-22.528 26.048-51.392 26.048-86.592 0-42.944-14.08-76.736-42.24-101.376-28.16-25.344-65.472-37.312-111.232-37.312zm-12.672 406.208a54.272 54.272 0 0 0-38.72 14.784 49.408 49.408 0 0 0-15.488 38.016c0 15.488 4.928 28.16 15.488 38.016A54.848 54.848 0 0 0 523.072 768c15.488 0 28.16-4.928 38.72-14.784a51.52 51.52 0 0 0 16.192-38.72 51.968 51.968 0 0 0-15.488-38.016 55.936 55.936 0 0 0-39.424-14.784z"/></svg>
    36                                     <?php esc_html_e( 'Documentation', 'wp-commerce7' ); ?>
    37                                 </a>
    38                             </div>
     33                            <div class="c7wp-box-content-right">
     34                                <a href="https://c7wp.com/documentation/" target="_blank" class="c7wp-btn c7wp-btn-primary c7wp-btn-large">
     35                                    <svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm23.744 191.488c-52.096 0-92.928 14.784-123.2 44.352-30.976 29.568-45.76 70.4-45.76 122.496h80.256c0-29.568 5.632-52.8 17.6-68.992 13.376-19.712 35.2-28.864 66.176-28.864 23.936 0 42.944 6.336 56.32 19.712 12.672 13.376 19.712 31.68 19.712 54.912 0 17.6-6.336 34.496-19.008 49.984l-8.448 9.856c-45.76 40.832-73.216 70.4-82.368 89.408-9.856 19.008-14.08 42.24-14.08 68.992v9.856h80.96v-9.856c0-16.896 3.52-31.68 10.56-45.76 6.336-12.672 15.488-24.64 28.16-35.2 33.792-29.568 54.208-48.576 60.544-55.616 16.896-22.528 26.048-51.392 26.048-86.592 0-42.944-14.08-76.736-42.24-101.376-28.16-25.344-65.472-37.312-111.232-37.312zm-12.672 406.208a54.272 54.272 0 0 0-38.72 14.784 49.408 49.408 0 0 0-15.488 38.016c0 15.488 4.928 28.16 15.488 38.016A54.848 54.848 0 0 0 523.072 768c15.488 0 28.16-4.928 38.72-14.784a51.52 51.52 0 0 0 16.192-38.72 51.968 51.968 0 0 0-15.488-38.016 55.936 55.936 0 0 0-39.424-14.784z"/></svg>
     36                                    <?php esc_html_e( 'Documentation', 'wp-commerce7' ); ?>
     37                                </a>
     38                            </div>
    3939
    40                         </div>
     40                        </div>
    4141
    42                     </div>
     42                    </div>
    4343
    44                     <div class="c7wp-box">
     44                    <div class="c7wp-box">
    4545
    46                         <div class="c7wp-box-content">
    47                             <form method="post" action="options.php">
    48                                 <?php
    49                                 settings_fields( 'commerce7' );
    50                                 do_settings_sections( 'commerce7' );
    51                                 submit_button();
    52                                 ?>
    53                             </form>
    54                         </div>
     46                        <div class="c7wp-box-content">
     47                            <form method="post" action="options.php">
     48                                <?php
     49                                settings_fields( 'commerce7' );
     50                                do_settings_sections( 'commerce7' );
     51                                submit_button();
     52                                ?>
     53                            </form>
     54                        </div>
     55
     56                    </div>
     57
     58                </div>
     59            </div>
     60
     61        </div>
    5562
    5663
    57                     </div>
    58                 </div>
    59             </div>
     64        <div class="c7wp-sidebar">
    6065
    61         </div>
     66            <?php
    6267
     68                /**
     69                 * Filter: `c7wp_show_sidebar`
     70                 *
     71                 * Filter for hiding the sidebar in our plugin settings, which might include promotions for
     72                 * our services. This is a whitelabel as you're going to get.
     73                 *
     74                 * @param bool Should the sidebar be shown? Default: true
     75                 */
     76            if ( apply_filters( 'c7wp_show_sidebar', true ) ) :
    6377
    64         <div class="c7wp-sidebar">
     78                $gists = array(
     79                    '95be9529b9ba6f9cc96336e085a4b122',
     80                    '902913bb4b5623f945c9ebe30dfbc0b6',
     81                    'fb980c8d769a96f169250a79ca278f74',
     82                );
    6583
    66             <?php
     84                $allowed_html      = wp_kses_allowed_html( 'post' );
     85                $allowed_protocols = array(
     86                    'https',
     87                    'data',
     88                );
    6789
    68                 /**
    69                  * Filter: `c7wp_show_sidebar`
    70                  *
    71                  * Filter for hiding the sidebar in our plugin settings, which might include promotions for
    72                  * our services. This is a whitelabel as you're going to get.
    73                  *
    74                  * @param bool Should the sidebar be shown? Default: true
    75                  */
    76                 if ( apply_filters( 'c7wp_show_sidebar', true ) ) :
     90                $args = array(
     91                    'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
     92                );
    7793
    78                 $gists = [
    79                     '95be9529b9ba6f9cc96336e085a4b122',
    80                     '902913bb4b5623f945c9ebe30dfbc0b6',
    81                     'fb980c8d769a96f169250a79ca278f74',
    82                 ];
     94                foreach ( $gists as $gist ) {
     95                    $callout = get_transient( 'c7wp_' . $gist );
    8396
    84                 $allowed_html = wp_kses_allowed_html( 'post' );
    85                 $allowed_protocols = [
    86                     'https',
    87                     'data',
    88                 ];
     97                    if ( empty( $callout ) ) {
     98                        $response = wp_remote_get( 'https://api.github.com/gists/' . $gist, $args );
    8999
    90                 $args = [
    91                     'headers' => [ 'Content-Type' => 'application/json; charset=utf-8' ],
    92                 ];
     100                        if ( is_array( $response ) && ! is_wp_error( $response ) && '200' === wp_remote_retrieve_response_code( $response ) ) {
     101                            $headers = $response['headers']; // array of http header lines
     102                            $body    = json_decode( $response['body'], true ); // use the content
    93103
    94                 foreach ( $gists as $gist ) {
    95                     $callout = get_transient( 'c7wp_' . $gist );
     104                            if ( isset( $body['files']['index.html']['content'] ) ) {
     105                                $callout = stripslashes( $body['files']['index.html']['content'] );
     106                                set_transient( 'c7wp_' . $gist, $callout, WEEK_IN_SECONDS );
     107                            } else {
     108                                // Log error if WP_DEBUG is enabled
     109                                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     110                                    error_log( 'Commerce7: Invalid response structure from GitHub API for gist ' . $gist );
     111                                }
     112                                continue;
     113                            }
     114                        } else {
     115                            // Log error if WP_DEBUG is enabled
     116                            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     117                                $error_message = is_wp_error( $response ) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code( $response );
     118                                error_log( 'Commerce7: Failed to fetch gist ' . $gist . ': ' . $error_message );
     119                            }
     120                            continue;
     121                        }
     122                    }
    96123
    97                     if ( empty( $callout ) ) {
    98                         $response = wp_remote_get( 'https://api.github.com/gists/' . $gist, $args );
     124                    if ( ! empty( $callout ) ) {
     125                        echo wp_kses( $callout, $allowed_html, $allowed_protocols );
     126                    }
     127                }
    99128
    100                         if ( is_array( $response ) && ! is_wp_error( $response ) && '200' == wp_remote_retrieve_response_code( $response ) ) {
    101                             $headers = $response['headers']; // array of http header lines
    102                             $body    = json_decode( $response['body'], true ); // use the content
     129                endif;
     130            ?>
    103131
    104                             $callout = stripslashes( $body['files']['index.html']['content'] );
    105 
    106                             $trans = set_transient( 'c7wp_' . $gist, $callout, WEEK_IN_SECONDS );
    107                         } else {
    108                             continue;
    109                         }
    110                     }
    111 
    112                     echo wp_kses( $callout, $allowed_html, $allowed_protocols );
    113                 }
    114 
    115                 endif;
    116             ?>
    117 
    118             <div class="c7wp-cta">
    119                 <p class="c7wp-cta-note">Plugin created by URSA6 & 5forests. Provided free to Commerce7 customers and agencies around the world.</p>
    120                 <hr class="c7wp-cta-spacing">
    121                 <p class="c7wp-cta-note">We offer unbeatable <a href="https://5forests.com/services/technology/website-care-plans/" target="_blank">sustainble hosting and care plans</a> to our WordPress clients.</p>
    122             </div>
    123         </div>
     132            <div class="c7wp-cta">
     133                <p class="c7wp-cta-note">Plugin created by URSA6 & 5forests. Provided free to Commerce7 customers and agencies around the world.</p>
     134                <hr class="c7wp-cta-spacing">
     135                <p class="c7wp-cta-note">We offer unbeatable <a href="https://5forests.com/services/technology/website-care-plans/" target="_blank">sustainable hosting and care plans</a> to our WordPress clients.</p>
     136            </div>
     137        </div>
    124138
    125139
    126140
    127     </div>
     141    </div>
    128142</div>
  • wp-commerce7/trunk/assets/public/css/commerce7-for-wordpress.css

    r2768084 r3441297  
    1010    flex-direction: row;
    1111    align-items: center;
    12 }
    13 #c7wp-cart-box.top-left {
    14     top: 0;
    15     right: auto;
    16     left: 0;
    17     bottom: auto;
    18 }
    19 #c7wp-cart-box.bottom-left {
    20     top: auto;
    21     right: auto;
    22     left: 0;
    23     bottom: 0;
    24     position: fixed;
    25 }
    26 #c7wp-cart-box.bottom-right {
    27     top: auto;
    28     right: 0;
    29     left: auto;
    30     bottom: 0;
    31     position: fixed;
    32 }
    33 #c7wp-cart-box.c7dark > div > a {
    34     color: #fff;
    35 }
    36 #c7wp-cart-box.c7light > div > a {
    37     color: #333;
     12
     13    &.top-left {
     14        top: 0;
     15        right: auto;
     16        left: 0;
     17        bottom: auto;
     18    }
     19
     20    &.bottom-left {
     21        top: auto;
     22        right: auto;
     23        left: 0;
     24        bottom: 0;
     25        position: fixed;
     26    }
     27
     28    &.bottom-right {
     29        top: auto;
     30        right: 0;
     31        left: auto;
     32        bottom: 0;
     33        position: fixed;
     34    }
     35
     36    &.c7dark > div > a {
     37        color: #fff;
     38    }
     39
     40    &.c7light > div > a {
     41        color: #333;
     42    }
    3843}
    3944.c7-dark {
     
    4449    padding: 0 1rem 0 0;
    4550}
    46 .c7-checkout__content__title a {
    47     font-size: 0.95rem;
     51.c7-checkout__content__title {
     52    a {
     53        font-size: 0.95rem;
     54    }
    4855}
    4956.c7-cart__count {
     
    5158}
    5259@media screen and (min-width: 46rem) {
    53         .c7-form__group div div.date-picker, .c7-form__group div div.rdtPicker, .c7-form__group div div.rdtDays {
    54         width: auto;
    55         margin: 0;
     60    .c7-form__group {
     61        div div {
     62            &.date-picker,
     63            &.rdtPicker,
     64            &.rdtDays {
     65                width: auto;
     66                margin: 0;
     67            }
     68        }
    5669    }
    5770}
  • wp-commerce7/trunk/commerce7-for-wordpress.php

    r3343231 r3441297  
    1212 * Plugin Name: Commerce7 for WordPress
    1313 * Description: Integrate Commerce7 functionality into your WordPress site easily
    14  * Version: 1.5.4
     14 * Version: 1.6.1
    1515 * Author: URSA6 & 5forests
    1616 * Author URI: https://5forests.com
    1717 * Plugin URI: https://c7wp.com
    1818 * Requires at least: 6.0
    19  * Tested up to: 6.8
    20  * Stable tag: 1.5.4
     19 * Tested up to: 6.9
     20 * Stable tag: 1.6.1
    2121 * Requires PHP: 7.4
    2222 * License: GPL3
     
    2828 * Author: Michael Bourne
    2929 * -----
    30  * Last Modified: Tuesday, August 5th 2025, 9:23:42 pm
     30 * Last Modified: Wednesday, January 7th 2026, 3:14:12 pm
    3131 * Modified By: Michael Bourne
    3232 * -----
     
    4848defined( 'C7WP_ROOT' ) || define( 'C7WP_ROOT', __DIR__ );
    4949defined( 'C7WP_URI' ) || define( 'C7WP_URI', plugin_dir_url( __FILE__ ) );
    50 defined( 'C7WP_VERSION' ) || define( 'C7WP_VERSION', '1.5.4' );
     50defined( 'C7WP_VERSION' ) || define( 'C7WP_VERSION', '1.6.1' );
    5151if ( ! defined( 'C7WP_NOTICES_URL' ) || C7WP_NOTICES_URL !== 'https://c7wp.com/notices.json' ) {
    52     define( 'C7WP_NOTICES_URL', 'https://c7wp.com/notices.json' );
     52    define( 'C7WP_NOTICES_URL', 'https://c7wp.com/notices.json' );
    5353}
    5454
     
    6161 */
    6262function c7wp_activate_plugin() {
    63     add_option( 'c7wp_activation', true );
    64 
    65     $pages = array( 'profile', 'collection', 'product', 'club', 'checkout', 'cart', 'reservation' );
    66     $fail  = array();
    67 
    68     foreach ( $pages as $page ) {
    69         if ( get_page_by_path( $page, 'ARRAY_N', 'page' ) ) {
    70             $fail[] = $page;
    71             continue;
    72         }
    73 
    74         $c7_post = array(
    75         'post_title'   => wp_strip_all_tags( ucfirst( $page ) ),
    76         'post_content' => '<!-- wp:c7wp/default --><div class="wp-block-c7wp-default"><div id="c7-content"></div></div><!-- /wp:c7wp/default -->',
    77         'post_status'  => 'publish',
    78         'post_author'  => 1,
    79         'post_type'    => 'page',
    80         );
    81 
    82         $pageid = wp_insert_post( $c7_post );
    83     }
    84 
    85     if ( ! empty( $fail ) ) {
    86         set_transient( 'c7wp-admin-notice-pages', $fail, 5 );
    87     }
    88 
    89     $c7options = array(
    90                     'c7wp_tenant'                => '',
    91                     'c7wp_display_cart'          => 'no',
    92                     'c7wp_display_cart_location' => 'tr',
    93                     'c7wp_display_cart_color'    => 'light',
    94                     'c7wp_widget_version'        => 'v2',
    95                     'c7wp_enable_custom_routes'  => 'no',
    96                     'c7wp_frontend_routes'       => array(
    97                         'profile'     => 'profile',
    98                         'collection'  => 'collection',
    99                         'product'     => 'product',
    100                         'club'        => 'club',
    101                         'checkout'    => 'checkout',
    102                         'cart'        => 'cart',
    103                         'reservation' => 'reservation',
    104                     ),
    105                 );
    106 
    107     update_option( 'c7wp_settings', $c7options, true );
    108 
    109     // set a default permalink structure if the installation has not yet done this
    110     require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    111     if ( function_exists( 'wp_install_maybe_enable_pretty_permalinks' ) ) {
    112         wp_install_maybe_enable_pretty_permalinks();
    113     }
     63    add_option( 'c7wp_activation', true );
     64
     65    // Schedule remote notices cron job
     66    c7wp_schedule_notices_cron();
     67
     68    $pages = array( 'profile', 'collection', 'product', 'club', 'checkout', 'cart', 'reservation' );
     69    $fail  = array();
     70
     71    foreach ( $pages as $page ) {
     72        if ( get_page_by_path( $page, 'ARRAY_N', 'page' ) ) {
     73            $fail[] = $page;
     74            continue;
     75        }
     76
     77        $c7_post = array(
     78            'post_title'   => wp_strip_all_tags( ucfirst( $page ) ),
     79            'post_content' => '<!-- wp:c7wp/default --><div class="wp-block-c7wp-default"><div id="c7-content"></div></div><!-- /wp:c7wp/default -->',
     80            'post_status'  => 'publish',
     81            'post_author'  => 1,
     82            'post_type'    => 'page',
     83        );
     84
     85        $pageid = wp_insert_post( $c7_post );
     86
     87        // Check if page creation was successful
     88        if ( is_wp_error( $pageid ) ) {
     89            // Log error if WP_DEBUG is enabled
     90            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     91                error_log( 'Commerce7: Failed to create page "' . $page . '": ' . $pageid->get_error_message() );
     92            }
     93            $fail[] = $page;
     94        } elseif ( ! $pageid ) {
     95            // Log error if WP_DEBUG is enabled
     96            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     97                error_log( 'Commerce7: Failed to create page "' . $page . '": Unknown error' );
     98            }
     99            $fail[] = $page;
     100        }
     101    }
     102
     103    if ( ! empty( $fail ) ) {
     104        set_transient( 'c7wp-admin-notice-pages', $fail, 5 );
     105    }
     106
     107    $c7options = array(
     108        'c7wp_tenant'                => '',
     109        'c7wp_display_cart'          => 'no',
     110        'c7wp_display_cart_location' => 'tr',
     111        'c7wp_display_cart_color'    => 'light',
     112        'c7wp_widget_version'        => 'v2',
     113        'c7wp_enable_custom_routes'  => 'no',
     114        'c7wp_frontend_routes'       => array(
     115            'profile'     => 'profile',
     116            'collection'  => 'collection',
     117            'product'     => 'product',
     118            'club'        => 'club',
     119            'checkout'    => 'checkout',
     120            'cart'        => 'cart',
     121            'reservation' => 'reservation',
     122        ),
     123    );
     124
     125    update_option( 'c7wp_settings', $c7options, true );
     126
     127    // set a default permalink structure if the installation has not yet done this
     128    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
     129    if ( function_exists( 'wp_install_maybe_enable_pretty_permalinks' ) ) {
     130        wp_install_maybe_enable_pretty_permalinks();
     131    }
    114132}
    115133register_activation_hook( __FILE__, 'c7wp_activate_plugin' );
     
    130148 */
    131149function c7wp_upgrade_function( $upgrader_object, $options ) {
    132     $current_plugin_path_name = plugin_basename( __FILE__ );
    133 
    134     // If a plugin is being updated.
    135     if ( 'update' === $options['action'] && 'plugin' === $options['type'] && isset( $options['plugins'] ) ) {
    136         foreach ( $options['plugins'] as $each_plugin ) {
    137             // If the plugin being updated is this plugin.
     150    $current_plugin_path_name = plugin_basename( __FILE__ );
     151
     152    // If a plugin is being updated.
     153    if ( 'update' === $options['action'] && 'plugin' === $options['type'] && isset( $options['plugins'] ) ) {
     154        foreach ( $options['plugins'] as $each_plugin ) {
     155            // If the plugin being updated is this plugin.
    138156            if ( $each_plugin == $current_plugin_path_name ) { // phpcs:ignore
    139157
    140                 $options = get_option( 'c7wp_settings' );
    141 
    142                 if ( isset( $options['c7wp_widget_version'] ) && 'v2' === $options['c7wp_widget_version']
    143                   && isset( $options['c7wp_enable_custom_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes']
    144                   && isset( $options['c7wp_frontend_routes'] ) && is_array( $options['c7wp_frontend_routes'] ) ) {
    145                     $pages = $options['c7wp_frontend_routes'];
    146                 } else {
    147                     $pages = array(
    148                         'profile'     => 'profile',
    149                         'collection'  => 'collection',
    150                         'product'     => 'product',
    151                         'club'        => 'club',
    152                         'checkout'    => 'checkout',
    153                         'cart'        => 'cart',
    154                         'reservation' => 'reservation',
    155                     );
    156                 }
    157 
    158                 $fail = array();
    159                 // Loop through required paged for C7.
    160                 foreach ( $pages as $page => $slug ) {
    161                     // if the page is missing, add it to the notice
    162                     if ( ! get_page_by_path( $slug, 'ARRAY_N', 'page' ) ) {
    163                         $fail[] = wp_strip_all_tags( ucfirst( $page ) );
    164                         continue;
    165                     }
    166                 }
    167 
    168                 // If we have missing pages, let's set a transient to display a notice.
    169                 if ( ! empty( $fail ) ) {
    170                     set_transient( 'c7wp-admin-notice-pages-missing', $fail, 0 );
    171                 }
    172 
    173                 $c7options = array(
    174                     'c7wp_tenant'                => '',
    175                     'c7wp_display_cart'          => 'no',
    176                     'c7wp_display_cart_location' => 'tr',
    177                     'c7wp_display_cart_color'    => 'light',
    178                     'c7wp_widget_version'        => 'v2',
    179                     'c7wp_enable_custom_routes'  => 'no',
    180                     'c7wp_frontend_routes'       => array(
    181                         'profile'     => 'profile',
    182                         'collection'  => 'collection',
    183                         'product'     => 'product',
    184                         'club'        => 'club',
    185                         'checkout'    => 'checkout',
    186                         'cart'        => 'cart',
    187                         'reservation' => 'reservation',
    188                     ),
    189                 );
    190 
    191                 // Merge client set options with default options to fix any unset array keys.
    192                 $normalized_options = array_merge( $c7options, $options );
    193                 update_option( 'c7wp_settings', $normalized_options, true );
    194 
    195                 // flush rewrite rules.
    196                 if ( function_exists( 'flush_rewrite_rules' ) ) {
    197                     flush_rewrite_rules();
    198                 }
    199             }
    200         }
    201     }
     158                $options = get_option( 'c7wp_settings' );
     159
     160                if ( isset( $options['c7wp_widget_version'] ) && 'v2' === $options['c7wp_widget_version']
     161                  && isset( $options['c7wp_enable_custom_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes']
     162                  && isset( $options['c7wp_frontend_routes'] ) && is_array( $options['c7wp_frontend_routes'] ) ) {
     163                    $pages = $options['c7wp_frontend_routes'];
     164                } else {
     165                    $pages = array(
     166                        'profile'     => 'profile',
     167                        'collection'  => 'collection',
     168                        'product'     => 'product',
     169                        'club'        => 'club',
     170                        'checkout'    => 'checkout',
     171                        'cart'        => 'cart',
     172                        'reservation' => 'reservation',
     173                    );
     174                }
     175
     176                $fail = array();
     177                // Loop through required paged for C7.
     178                foreach ( $pages as $page => $slug ) {
     179                    // if the page is missing, add it to the notice
     180                    if ( ! get_page_by_path( $slug, 'ARRAY_N', 'page' ) ) {
     181                        $fail[] = wp_strip_all_tags( ucfirst( $page ) );
     182                        continue;
     183                    }
     184                }
     185
     186                // If we have missing pages, let's set a transient to display a notice.
     187                if ( ! empty( $fail ) ) {
     188                    set_transient( 'c7wp-admin-notice-pages-missing', $fail, 0 );
     189                }
     190
     191                $c7options = array(
     192                    'c7wp_tenant'                => '',
     193                    'c7wp_display_cart'          => 'no',
     194                    'c7wp_display_cart_location' => 'tr',
     195                    'c7wp_display_cart_color'    => 'light',
     196                    'c7wp_widget_version'        => 'v2',
     197                    'c7wp_enable_custom_routes'  => 'no',
     198                    'c7wp_frontend_routes'       => array(
     199                        'profile'     => 'profile',
     200                        'collection'  => 'collection',
     201                        'product'     => 'product',
     202                        'club'        => 'club',
     203                        'checkout'    => 'checkout',
     204                        'cart'        => 'cart',
     205                        'reservation' => 'reservation',
     206                    ),
     207                );
     208
     209                // Merge client set options with default options to fix any unset array keys.
     210                $normalized_options = array_merge( $c7options, $options );
     211                update_option( 'c7wp_settings', $normalized_options, true );
     212
     213                // Only flush rewrite rules if route settings have changed
     214                $old_routes = isset( $options['c7wp_frontend_routes'] ) ? $options['c7wp_frontend_routes'] : array();
     215                $new_routes = $normalized_options['c7wp_frontend_routes'];
     216
     217                if ( $old_routes !== $new_routes ) {
     218                    if ( function_exists( 'flush_rewrite_rules' ) ) {
     219                        flush_rewrite_rules();
     220                    }
     221                }
     222            }
     223        }
     224    }
    202225}
    203226
     
    209232
    210233    if ( $data = get_transient( 'c7wp-admin-notice-pages' ) ) { // phpcs:ignore
    211         $pages = implode( ', ', $data );
    212         echo '<div class="notice notice-warning is-dismissible"><p>';
    213         printf(
    214             /* translators: 1: List of missing pages 2: List of missing pages */
    215             esc_html__( 'Commerce7 requires specific pages exist in order for integration to work properly. The following pages are needed but were not created, as they already exist: %1$s.
    216             Please ensure these pages contain the proper content.', 'wp-commerce7' ),
    217             '<strong>' . esc_html( $pages ) . '</strong>'
    218         );
    219         echo '</p></div>';
    220 
    221         /* Delete transient, only display this notice once. */
    222         delete_transient( 'c7wp-admin-notice-pages' );
    223     }
     234        $pages = implode( ', ', $data );
     235        echo '<div class="notice notice-warning is-dismissible"><p>';
     236        printf(
     237            /* translators: 1: List of missing pages 2: List of missing pages */
     238            esc_html__(
     239                'Commerce7 requires specific pages exist in order for integration to work properly. The following pages are needed but were not created, as they already exist: %1$s.
     240            Please ensure these pages contain the proper content.',
     241                'wp-commerce7'
     242            ),
     243            '<strong>' . esc_html( $pages ) . '</strong>'
     244        );
     245        echo '</p></div>';
     246
     247        /* Delete transient, only display this notice once. */
     248        delete_transient( 'c7wp-admin-notice-pages' );
     249    }
    224250
    225251    if ( $data = get_transient( 'c7wp-admin-notice-pages-missing' ) ) { // phpcs:ignore
    226         $pages = implode( ', ', $data );
    227         echo '<div class="notice notice-warning is-dismissible"><p>';
    228         printf(
    229             /* translators: %s: List of missing pages */
    230             esc_html__( 'Commerce7 requires specific pages exist in order for integration to work properly. The following pages were missing: %s. 
    231             These pages were not recreated in case you have removed them on purpose. Please review and crate these pages if needed.', 'wp-commerce7' ),
    232             '<strong>' . esc_html( $pages ) . '</strong>'
    233         );
    234         echo '</p></div>';
    235 
    236         /* Delete transient, only display this notice once. */
    237         delete_transient( 'c7wp-admin-notice-pages-missing' );
    238     }
     252        $pages = implode( ', ', $data );
     253        echo '<div class="notice notice-warning is-dismissible"><p>';
     254        printf(
     255            /* translators: %s: List of missing pages */
     256            esc_html__(
     257                'Commerce7 requires specific pages exist in order for integration to work properly. The following pages were missing: %s. 
     258            These pages were not recreated in case you have removed them on purpose. Please review and crate these pages if needed.',
     259                'wp-commerce7'
     260            ),
     261            '<strong>' . esc_html( $pages ) . '</strong>'
     262        );
     263        echo '</p></div>';
     264
     265        /* Delete transient, only display this notice once. */
     266        delete_transient( 'c7wp-admin-notice-pages-missing' );
     267    }
    239268}
    240269add_action( 'admin_notices', 'c7wp_admin_notice_pages' );
     
    246275 */
    247276function c7wp_deactivate_plugin() {
    248     flush_rewrite_rules();
     277    flush_rewrite_rules();
    249278}
    250279
     
    262291
    263292/**
    264  * Fetch and display remote admin notices
    265  */
    266 function c7wp_remote_notices() {
    267     $transient_key = 'c7wp_remote_notices';
    268     $notices = get_transient( $transient_key );
    269 
    270     if ( false === $notices ) {
    271         $response = wp_remote_get( C7WP_NOTICES_URL );
    272 
    273         if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) {
    274             $notices = json_decode( wp_remote_retrieve_body( $response ), true );
    275             set_transient( $transient_key, $notices, 12 * HOUR_IN_SECONDS );
    276         }
    277     }
    278 
    279     if ( ! empty( $notices ) && is_array( $notices ) ) {
    280         foreach ( $notices as $notice ) {
    281             if ( ! empty( $notice['message'] ) && ! get_user_meta( get_current_user_id(), 'c7wp_notice_dismissed_' . md5( $notice['message'] ), true ) ) {
    282                 add_action('admin_notices', function() use ( $notice ) {
    283                     $dismiss_url = add_query_arg(array(
    284                         'c7wp_dismiss_notice' => md5( $notice['message'] ),
    285                         '_wpnonce' => wp_create_nonce( 'c7wp_dismiss_notice' ),
    286                     ));
    287                     ?>
    288                     <div class="notice notice-<?php echo esc_attr( $notice['type'] ?? 'info' ); ?> is-dismissible">
    289                         <?php if ( ! empty( $notice['format'] ) && 'html' === $notice['format'] ) : ?>
    290                             <?php echo wp_kses_post( $notice['message'] ); ?>
    291                         <?php else : ?>
    292                             <p><?php echo wp_kses_post( $notice['message'] ); ?></p>
    293                         <?php endif; ?>
    294                         <a href="<?php echo esc_url( $dismiss_url ); ?>" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-commerce7' ); ?></span></a>
    295                     </div>
    296                     <?php
    297                 });
    298             }
    299         }
    300     }
    301 }
    302 add_action( 'admin_init', 'c7wp_remote_notices' );
     293 * Fetch remote admin notices via cron job
     294 */
     295function c7wp_fetch_remote_notices() {
     296    $response = wp_remote_get(
     297        C7WP_NOTICES_URL,
     298        array(
     299            'timeout' => 10,
     300            'headers' => array(
     301                'User-Agent' => 'Commerce7-WordPress-Plugin/' . C7WP_VERSION,
     302            ),
     303        )
     304    );
     305
     306    if ( is_wp_error( $response ) ) {
     307        // Log error if WP_DEBUG is enabled
     308        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     309            error_log( 'Commerce7: Failed to fetch remote notices: ' . $response->get_error_message() );
     310        }
     311        return;
     312    }
     313
     314    $response_code = wp_remote_retrieve_response_code( $response );
     315    if ( 200 !== $response_code ) {
     316        // Log error if WP_DEBUG is enabled
     317        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     318            error_log( 'Commerce7: Remote notices API returned HTTP ' . $response_code );
     319        }
     320        return;
     321    }
     322
     323    $notices = json_decode( wp_remote_retrieve_body( $response ), true );
     324    if ( json_last_error() !== JSON_ERROR_NONE ) {
     325        // Log error if WP_DEBUG is enabled
     326        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     327            error_log( 'Commerce7: Failed to parse remote notices JSON: ' . json_last_error_msg() );
     328        }
     329        return;
     330    }
     331
     332    if ( is_array( $notices ) ) {
     333        set_transient( 'c7wp_remote_notices', $notices, 12 * HOUR_IN_SECONDS );
     334    }
     335}
     336
     337/**
     338 * Schedule remote notices cron job
     339 */
     340function c7wp_schedule_notices_cron() {
     341    if ( ! wp_next_scheduled( 'c7wp_fetch_remote_notices' ) ) {
     342        wp_schedule_event( time(), 'twicedaily', 'c7wp_fetch_remote_notices' );
     343    }
     344}
     345
     346/**
     347 * Display remote admin notices
     348 */
     349function c7wp_display_remote_notices() {
     350    $notices = get_transient( 'c7wp_remote_notices' );
     351
     352    if ( ! empty( $notices ) && is_array( $notices ) ) {
     353        foreach ( $notices as $notice ) {
     354            if ( ! empty( $notice['message'] ) && ! get_user_meta( get_current_user_id(), 'c7wp_notice_dismissed_' . md5( $notice['message'] ), true ) ) {
     355                add_action(
     356                    'admin_notices',
     357                    function() use ( $notice ) {
     358                        $dismiss_url = add_query_arg(
     359                            array(
     360                                'c7wp_dismiss_notice' => md5( $notice['message'] ),
     361                                '_wpnonce'            => wp_create_nonce( 'c7wp_dismiss_notice' ),
     362                            )
     363                        );
     364                        ?>
     365                    <div class="notice notice-<?php echo esc_attr( $notice['type'] ?? 'info' ); ?> is-dismissible">
     366                        <?php if ( ! empty( $notice['format'] ) && 'html' === $notice['format'] ) : ?>
     367                            <?php echo wp_kses_post( $notice['message'] ); ?>
     368                        <?php else : ?>
     369                            <p><?php echo wp_kses_post( $notice['message'] ); ?></p>
     370                        <?php endif; ?>
     371                        <a href="<?php echo esc_url( $dismiss_url ); ?>" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-commerce7' ); ?></span></a>
     372                    </div>
     373                        <?php
     374                    }
     375                );
     376            }
     377        }
     378    }
     379}
     380
     381
     382// Clean up cron job on deactivation
     383register_deactivation_hook(
     384    __FILE__,
     385    function() {
     386        wp_clear_scheduled_hook( 'c7wp_fetch_remote_notices' );
     387    }
     388);
     389
     390// Hook the cron job
     391add_action( 'c7wp_fetch_remote_notices', 'c7wp_fetch_remote_notices' );
     392
     393// Display notices on admin pages
     394add_action( 'admin_init', 'c7wp_display_remote_notices' );
    303395
    304396/**
     
    306398 */
    307399function c7wp_handle_notice_dismissal() {
    308     if ( isset( $_GET['c7wp_dismiss_notice'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'c7wp_dismiss_notice' ) ) {
    309         $notice_id = sanitize_text_field( wp_unslash( $_GET['c7wp_dismiss_notice'] ) );
    310         update_user_meta( get_current_user_id(), 'c7wp_notice_dismissed_' . $notice_id, true );
    311         wp_safe_redirect( remove_query_arg( array( 'c7wp_dismiss_notice', '_wpnonce' ) ) );
    312         exit;
    313     }
     400    // Check user capabilities first
     401    if ( ! current_user_can( 'manage_options' ) ) {
     402        return;
     403    }
     404
     405    if ( isset( $_GET['c7wp_dismiss_notice'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'c7wp_dismiss_notice' ) ) {
     406        $notice_id = sanitize_text_field( wp_unslash( $_GET['c7wp_dismiss_notice'] ) );
     407        update_user_meta( get_current_user_id(), 'c7wp_notice_dismissed_' . $notice_id, true );
     408        wp_safe_redirect( remove_query_arg( array( 'c7wp_dismiss_notice', '_wpnonce' ) ) );
     409        exit;
     410    }
    314411}
    315412add_action( 'admin_init', 'c7wp_handle_notice_dismissal' );
  • wp-commerce7/trunk/includes/class-c7wp.php

    r3313519 r3441297  
    4747    private $seoplugin;
    4848
     49    /**
     50     * Cached plugin settings
     51     *
     52     * @var array
     53     */
     54    private $settings;
     55
    4956
    5057    /**
     
    7582        add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
    7683        add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'elementor_editor_enqueue_scripts' ) );
     84        add_action( 'elementor/frontend/after_enqueue_scripts', array( $this, 'elementor_frontend_assets_fallback' ) );
    7785        add_action( 'after_setup_theme', array( $this, 'load_c7_css' ), 9 );
    7886        add_filter( 'body_class', array( $this, 'add_body_class' ) );
     
    9199        $this->seoplugin = false;
    92100
    93         $options = get_option( 'c7wp_settings' );
    94         if ( isset( $options['c7wp_widget_version'] ) && ! empty( $options['c7wp_widget_version'] ) ) {
    95             $this->widgetsver = esc_attr( $options['c7wp_widget_version'] );
     101        // Cache settings to avoid multiple database calls
     102        $this->settings = get_option( 'c7wp_settings', array() );
     103        if ( isset( $this->settings['c7wp_widget_version'] ) && ! empty( $this->settings['c7wp_widget_version'] ) ) {
     104            $this->widgetsver = esc_attr( $this->settings['c7wp_widget_version'] );
    96105        } else {
    97106            $this->widgetsver = 'v2';
    98107        }
    99108
    100         // load translations
    101         add_action( 'plugins_loaded', array( $this, 'c7wp_load_textdomain' ) );
     109        // load health check integration
     110        require_once C7WP_ROOT . '/includes/health-check.php';
    102111
    103112        // c7 div output for certain pagebuilders
     
    119128
    120129    /**
     130     * Get plugin settings with caching
     131     *
     132     * @return array Plugin settings
     133     */
     134    public function get_settings() {
     135        return $this->settings;
     136    }
     137
     138    /**
     139     * Refresh cached settings
     140     */
     141    public function refresh_settings() {
     142        $this->settings = get_option( 'c7wp_settings', array() );
     143    }
     144
     145    /**
    121146     * @param mixed $instance Singleton instance.
    122147     */
     
    130155    public function admin_init() {
    131156
    132         $options = get_option( 'c7wp_settings' );
    133         if ( ! isset( $options['c7wp_frontend_routes'] ) || ! is_array( $options['c7wp_frontend_routes'] ) ) {
    134             $options['c7wp_frontend_routes'] = array(
     157        if ( ! isset( $this->settings['c7wp_frontend_routes'] ) || ! is_array( $this->settings['c7wp_frontend_routes'] ) ) {
     158            $this->settings['c7wp_frontend_routes'] = array(
    135159                'profile'     => 'profile',
    136160                'collection'  => 'collection',
     
    143167                'reservation' => 'reservation',
    144168            );
    145             update_option( 'c7wp_settings', $options, true );
     169            update_option( 'c7wp_settings', $this->settings, true );
     170            $this->refresh_settings();
    146171        }
    147172
     
    271296     */
    272297    public function load_cs_elements() {
    273         // Cornerstone Support
     298        // Cornerstone Support for older versions
    274299        if ( class_exists( 'Cornerstone_Plugin' ) || function_exists( 'cornerstone_boot' ) ) {
    275             require_once C7WP_ROOT . '/includes/themeco/load.php';
     300            require_once C7WP_ROOT . '/includes/themeco/legacy/load.php';
    276301        }
    277302    }
     
    298323            \Elementor\Plugin::instance()->widgets_manager->register( new \C7WP_Elementor() );
    299324        }
    300     }
    301 
    302     /**
    303      * Internationalization
    304      */
    305     public function c7wp_load_textdomain() {
    306         load_plugin_textdomain( 'wp-commerce7', false, C7WP_ROOT . '/languages/' );
    307325    }
    308326
     
    336354        }
    337355
     356        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    338357        if ( isset( $_GET['settings-updated'] ) && empty( get_settings_errors( 'c7wp_settings' ) ) ) { // phpcs:ignore
    339358            add_settings_error( 'c7wp_settings', 'c7wp_settings_saved', __( 'Settings Saved', 'wp-commerce7' ), 'updated' );
     
    351370    protected function settings_init() {
    352371
    353         register_setting( 'commerce7', 'c7wp_settings', 'c7wp_settings_callback' );
     372        register_setting(
     373            'commerce7',
     374            'c7wp_settings',
     375            array(
     376                'sanitize_callback' => array( $this, 'c7wp_settings_callback' ),
     377                'default'           => array(),
     378            )
     379        );
    354380
    355381        add_settings_section(
     
    446472                if ( is_array( $value ) ) {
    447473                    foreach ( $value as $subkey => $subvalue ) {
    448                         $output[ $key ][ $subkey ] = sanitize_title( $subvalue );
     474                        $output[ $key ][ $subkey ] = $this->sanitize_setting_field( $key, $subkey, $subvalue );
    449475                    }
    450476                } else {
    451                     $output[ $key ] = sanitize_title( $value );
     477                    $output[ $key ] = $this->sanitize_setting_field( $key, null, $value );
    452478                }
    453479            }
    454480        }
    455481
     482        $this->settings = $output;
     483
    456484        return apply_filters( 'c7wp_settings_post_validation', $output, $input );
    457485    }
    458486
     487    /**
     488     * Sanitize individual setting fields based on field type
     489     *
     490     * @param string      $key The setting key.
     491     * @param string|null $subkey The subkey for array values.
     492     * @param mixed       $value The value to sanitize.
     493     * @return mixed The sanitized value.
     494     */
     495    private function sanitize_setting_field( $key, $subkey, $value ) {
     496        // Handle tenant ID specifically
     497        if ( 'c7wp_tenant' === $key ) {
     498            // Tenant ID should be alphanumeric with possible hyphens/underscores
     499            return trim( sanitize_text_field( $value ) );
     500        }
     501
     502        // Handle frontend routes (page slugs)
     503        if ( 'c7wp_frontend_routes' === $key && ! is_null( $subkey ) ) {
     504            // Page slugs should be valid WordPress slugs
     505            $sanitized = sanitize_title( $value );
     506            // Ensure it's not empty and doesn't conflict with WordPress reserved terms
     507            $reserved_terms = array( 'admin', 'api', 'wp-admin', 'wp-content', 'wp-includes' );
     508            if ( empty( $sanitized ) || in_array( $sanitized, $reserved_terms, true ) ) {
     509                // Return default value if invalid
     510                $defaults = array(
     511                    'profile'     => 'profile',
     512                    'collection'  => 'collection',
     513                    'product'     => 'product',
     514                    'club'        => 'club',
     515                    'checkout'    => 'checkout',
     516                    'cart'        => 'cart',
     517                    'reservation' => 'reservation',
     518                );
     519                return isset( $defaults[ $subkey ] ) ? $defaults[ $subkey ] : sanitize_title( $value );
     520            }
     521            return $sanitized;
     522        }
     523
     524        // Handle select fields (these should be from predefined options)
     525        $select_fields = array(
     526            'c7wp_display_cart'          => array( 'yes', 'no' ),
     527            'c7wp_display_cart_location' => array( 'tl', 'tr', 'br', 'bl' ),
     528            'c7wp_display_cart_color'    => array( 'light', 'dark' ),
     529            'c7wp_widget_version'        => array( 'v2', 'v2-compat', 'beta' ),
     530            'c7wp_enable_custom_routes'  => array( 'yes', 'no' ),
     531        );
     532
     533        if ( isset( $select_fields[ $key ] ) ) {
     534            $allowed_values = $select_fields[ $key ];
     535            if ( in_array( $value, $allowed_values, true ) ) {
     536                return $value;
     537            }
     538            // Return default value if invalid
     539            $defaults = array(
     540                'c7wp_display_cart'          => 'no',
     541                'c7wp_display_cart_location' => 'tr',
     542                'c7wp_display_cart_color'    => 'light',
     543                'c7wp_widget_version'        => 'v2',
     544                'c7wp_enable_custom_routes'  => 'no',
     545            );
     546            return isset( $defaults[ $key ] ) ? $defaults[ $key ] : '';
     547        }
     548
     549        // Default sanitization for other fields
     550        return sanitize_text_field( $value );
     551    }
     552
    459553    public function c7wp_tenant_render() {
    460554
    461         $options = get_option( 'c7wp_settings' );
     555        $options = $this->settings;
    462556        ?>
    463557        <input type='text' name='c7wp_settings[c7wp_tenant]' value='<?php echo esc_attr( $options['c7wp_tenant'] ); ?>'>
     
    467561    public function c7wp_display_cart_render() {
    468562
    469         $options = get_option( 'c7wp_settings' );
     563        $options = $this->settings;
    470564        ?>
    471565        <select name='c7wp_settings[c7wp_display_cart]' class='c7displaycart'>
     
    475569        <p><small>
    476570            <?php
    477                 echo wp_kses( __( 'If set to <strong>yes</strong>, this plugin will add a floating login  cart box to the front end of your website. 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. We recommend setting this to no and placing your cart manually.', 'wp-commerce7' ),
    478                 array( 'strong' => array(), 'code' => array() ) );
    479          ?>
    480          </small></p>
     571                echo wp_kses(
     572                    __(
     573                        'If set to <strong>yes</strong>, this plugin will add a floating login  cart box to the front end of your website. 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. We recommend setting this to no and placing your cart manually.',
     574                        'wp-commerce7'
     575                    ),
     576                    array(
     577                        'strong' => array(),
     578                        'code'   => array(),
     579                    )
     580                );
     581            ?>
     582        </small></p>
    481583        <?php
    482584    }
     
    484586    public function c7wp_display_cart_location_render() {
    485587
    486         $options  = get_option( 'c7wp_settings' );
     588        $options  = $this->settings;
    487589        $disabled = ( 'no' === $options['c7wp_display_cart'] ) ? 'disabled' : '';
    488590        if ( ! isset( $options['c7wp_display_cart_location'] ) ) {
     
    502604    public function c7wp_display_cart_color_render() {
    503605
    504         $options  = get_option( 'c7wp_settings' );
     606        $options  = $this->settings;
    505607        $disabled = ( 'no' === $options['c7wp_display_cart'] ) ? 'disabled' : '';
    506608        if ( ! isset( $options['c7wp_display_cart_color'] ) ) {
     
    522624    public function c7wp_widget_version_render() {
    523625
    524         $options = get_option( 'c7wp_settings' );
     626        $options = $this->settings;
    525627        ?>
    526628        <select name='c7wp_settings[c7wp_widget_version]' class='c7widgetversion'>
     
    531633        <p><small>
    532634            <?php
    533                 echo wp_kses( '<strong>V2:</strong> The standard front-end widgets version used by most wineries. Most users should select this option.
    534                 <br>
    535                 <strong>V2 (Compatibility Mode):</strong> Use this if you are having issues with the standard V2 widgets, such as widgets not displaying correctly or displaying multiple times. This will load the V2 widgets in a way that is compatible with more themes.
    536                 <br>
    537                 <strong>V1:</strong> Only a select few legacy sites are still using the old beta/V1 widgets.', array( 'strong' => array(), 'br' => array() ) );
     635                echo wp_kses(
     636                    __(
     637                        '<strong>V2:</strong> The standard front-end widgets version used by most wineries.
     638                        <br>
     639                        <strong>V2 (Compatibility Mode):</strong> This will load the V2 widgets in a way that is compatible with more themes and plugins, with safer javascript invocation. Safe for all websites, and the recommended option for everyone.
     640                        <br>
     641                        <strong>V1:</strong> Only a select few legacy sites are still using the old beta/V1 widgets.',
     642                        'wp-commerce7',
     643                    ),
     644                    array(
     645                        'strong' => array(),
     646                        'br'     => array(),
     647                    )
     648                );
    538649            ?>
    539650        </small></p>
    540         </small></p>
    541651        <?php
    542652    }
     
    544654    public function c7wp_enable_custom_routes_render() {
    545655
    546         $options  = get_option( 'c7wp_settings' );
     656        $options  = $this->settings;
    547657        $disabled = ( 'beta' === $options['c7wp_widget_version'] ) ? 'disabled' : '';
    548658        ?>
    549659        <select name='c7wp_settings[c7wp_enable_custom_routes]' <?php echo esc_attr( $disabled ); ?> >
    550660            <option value='no'
    551                 <?php if ( isset( $options['c7wp_enable_custom_routes'] ) ) { selected( $options['c7wp_enable_custom_routes'], 'no' ); } ?>
     661                <?php
     662                if ( isset( $options['c7wp_enable_custom_routes'] ) ) {
     663                    selected(
     664                        $options['c7wp_enable_custom_routes'],
     665                        'no'
     666                    );
     667                }
     668                ?>
    552669                ><?php esc_html_e( 'No', 'wp-commerce7' ); ?></option>
    553670            <option value='yes'
    554                 <?php if ( isset( $options['c7wp_enable_custom_routes'] ) ) { selected( $options['c7wp_enable_custom_routes'], 'yes' ); } ?>
     671                <?php
     672                if ( isset( $options['c7wp_enable_custom_routes'] ) ) {
     673                    selected(
     674                        $options['c7wp_enable_custom_routes'],
     675                        'yes'
     676                    );
     677                }
     678                ?>
    555679                ><?php esc_html_e( 'Yes', 'wp-commerce7' ); ?></option>
    556680        </select>
     
    565689    public function c7wp_frontend_routes_render() {
    566690
    567         $options  = get_option( 'c7wp_settings' );
     691        $options  = $this->settings;
    568692        $disabled = ( 'yes' === $options['c7wp_enable_custom_routes'] ) ? '' : 'disabled';
    569693        ?>
     
    607731            // Define the allowed HTML tags and attributes
    608732            $allowed_html = array(
    609                     'a' => array(
    610                             'href' => array(),
    611                             'target' => array(),
    612                     ),
    613                     'strong' => array(),
    614                     'br' => array(),
     733                'a' => array(
     734                    'href'  => array(),
     735                    'target' => array(),
     736                ),
     737                'strong' => array(),
     738                'br'    => array(),
    615739            );
    616740
     
    621745            /* translators: %1$s: URL to the routing settings in Commerce7 %2$s: URL to the permalinks settings in WordPress */
    622746            $string = __(
    623                     'These routes <strong>must</strong> match your <a href="%1$s" target="_blank">routing settings in Commerce7</a>.
    624                     <br>
    625                     Make sure you edit the slugs of any existing pages if you are changing these values.
    626                     <br>
    627                     You <strong>must</strong> <a href="%2$s">resave your permalinks in WordPress</a> after changing these settings.',
    628                     'wp-commerce7',
     747                'These routes <strong>must</strong> match your <a href="%1$s" target="_blank">routing settings in Commerce7</a>.
     748                <br>
     749                Make sure you edit the slugs of any existing pages if you are changing these values.
     750                <br>
     751                You <strong>must</strong> <a href="%2$s">resave your permalinks in WordPress</a> after changing these settings.',
     752                'wp-commerce7',
    629753            );
    630754
     
    652776    public function enqueue_scripts() {
    653777
    654         $options = get_option( 'c7wp_settings' );
     778        $options = $this->settings;
    655779
    656780        /**
     
    689813            wp_enqueue_script( 'c7js' );
    690814        }
     815
     816        // Enqueue block-specific assets only when needed
     817        $this->enqueue_block_assets();
    691818
    692819        /**
     
    743870
    744871    /**
     872     * Enqueue block-specific assets only when blocks are present
     873     */
     874    private function enqueue_block_assets() {
     875        global $post;
     876
     877        if ( ! $post ) {
     878            return;
     879        }
     880
     881        $post_content = $post->post_content;
     882
     883        // Check for Gutenberg blocks
     884        if ( has_blocks( $post_content ) ) {
     885            $blocks = parse_blocks( $post_content );
     886            $this->enqueue_blocks_assets( $blocks );
     887        }
     888
     889        // Check for shortcodes
     890        if ( has_shortcode( $post_content, 'c7wp' ) ) {
     891            $this->enqueue_shortcode_assets();
     892        }
     893    }
     894
     895    /**
     896     * Recursively enqueue assets for blocks
     897     *
     898     * @param array $blocks Array of blocks to check.
     899     */
     900    private function enqueue_blocks_assets( $blocks ) {
     901        foreach ( $blocks as $block ) {
     902            // Check if this is a Commerce7 block
     903            if ( isset( $block['blockName'] ) && strpos( $block['blockName'], 'c7wp/' ) === 0 ) {
     904                $block_type = str_replace( 'c7wp/', '', $block['blockName'] );
     905                $this->enqueue_specific_block_assets( $block_type );
     906            }
     907
     908            // Check inner blocks
     909            if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) ) {
     910                $this->enqueue_blocks_assets( $block['innerBlocks'] );
     911            }
     912        }
     913    }
     914
     915    /**
     916     * Enqueue assets for a specific block type
     917     *
     918     * @param string $block_type The block type to enqueue assets for.
     919     */
     920    private function enqueue_specific_block_assets( $block_type ) {
     921        $options    = $this->settings;
     922        $widgetsver = $options['c7wp_widget_version'];
     923
     924        // Determine the correct directory
     925        $dir = in_array( $widgetsver, array( 'v2', 'v2-compat' ), true ) ? 'blocks-v2' : 'blocks';
     926
     927        // Enqueue frontend styles
     928        $frontend_css_path = $dir . '/' . $block_type . '/frontend.css';
     929        if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_css_path ) ) {
     930            wp_enqueue_style(
     931                'c7wp-' . $block_type . '-frontend',
     932                C7WP_URI . 'includes/gutenberg/' . $frontend_css_path,
     933                array(),
     934                C7WP_VERSION
     935            );
     936        }
     937
     938        // Enqueue frontend scripts
     939        $frontend_js_path = $dir . '/' . $block_type . '/frontend.js';
     940        if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_js_path ) ) {
     941            wp_enqueue_script(
     942                'c7wp-' . $block_type . '-frontend',
     943                C7WP_URI . 'includes/gutenberg/' . $frontend_js_path,
     944                array(),
     945                C7WP_VERSION,
     946                true
     947            );
     948
     949            // Localize settings for specific blocks
     950            if ( 'clubselector' === $block_type ) {
     951                $options = $this->settings;
     952                wp_localize_script(
     953                    'c7wp-' . $block_type . '-frontend',
     954                    'c7wp_settings',
     955                    array(
     956                        'c7wp_frontend_routes' => isset( $options['c7wp_frontend_routes'] ) ? $options['c7wp_frontend_routes'] : array( 'club' => 'club' ),
     957                    )
     958                );
     959            }
     960        }
     961    }
     962
     963    /**
     964     * Enqueue assets for shortcode usage
     965     */
     966    private function enqueue_shortcode_assets() {
     967        // Enqueue general Commerce7 styles
     968        wp_enqueue_style(
     969            'c7wp-shortcode',
     970            C7WP_URI . 'assets/public/css/commerce7-for-wordpress.css',
     971            array(),
     972            C7WP_VERSION
     973        );
     974    }
     975
     976    /**
    745977     * Elementor specific styles
    746978     *
     
    750982        wp_enqueue_style( 'wp-commerce7-icons', C7WP_URI . 'assets/admin/css/c7wpicons.css', array(), C7WP_VERSION );
    751983    }
     984
     985    /**
     986     * Ensure clubselector frontend assets are loaded for Elementor when Gutenberg is disabled
     987     *
     988     * @return void
     989     */
     990    public function elementor_frontend_assets_fallback() {
     991        // Only run if Gutenberg is not available (Classic Editor scenario)
     992        if ( ! function_exists( 'register_block_type' ) ) {
     993            // Check if we're on v2 widgets and clubselector frontend assets exist
     994            if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
     995                $frontend_js_path  = 'blocks-v2/clubselector/frontend.js';
     996                $frontend_css_path = 'blocks-v2/clubselector/frontend.css';
     997
     998                // Register and enqueue frontend script if it exists
     999                if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_js_path ) && empty( $_GET['ct_builder'] ) ) {
     1000                    $frontend_script_handle = 'c7wp-clubselector-frontend';
     1001                    wp_register_script(
     1002                        $frontend_script_handle,
     1003                        plugins_url( $frontend_js_path, C7WP_ROOT . '/includes/gutenberg/load.php' ),
     1004                        array(),
     1005                        C7WP_VERSION,
     1006                        true
     1007                    );
     1008
     1009                    // Localize settings
     1010                    $options = get_option( 'c7wp_settings' );
     1011                    wp_localize_script(
     1012                        $frontend_script_handle,
     1013                        'c7wp_settings',
     1014                        array(
     1015                            'c7wp_frontend_routes' => isset( $options['c7wp_frontend_routes'] ) ? $options['c7wp_frontend_routes'] : array( 'club' => 'club' ),
     1016                        )
     1017                    );
     1018
     1019                    wp_enqueue_script( $frontend_script_handle );
     1020                }
     1021
     1022                // Register and enqueue frontend styles if they exist
     1023                if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_css_path ) ) {
     1024                    wp_register_style(
     1025                        'c7wp-clubselector-frontend',
     1026                        plugins_url( $frontend_css_path, C7WP_ROOT . '/includes/gutenberg/load.php' ),
     1027                        array(),
     1028                        C7WP_VERSION
     1029                    );
     1030                    wp_enqueue_style( 'c7wp-clubselector-frontend' );
     1031                }
     1032            }
     1033        }
     1034    }
     1035
    7521036
    7531037
     
    7631047    public function add_data_to_c7_script( $tag, $handle, $src ) {
    7641048        if ( 'c7js' === $handle ) {
    765             $options = get_option( 'c7wp_settings' );
     1049            $options = $this->settings;
    7661050            $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
    7671051        }
     
    7801064    public function add_c7_rewrites() {
    7811065
    782         if ( in_array( $this->widgetsver, [ 'v2', 'v2-compat' ] ) ) {
    783 
    784             $options = get_option( 'c7wp_settings' );
     1066        if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
     1067
     1068            $options = $this->settings;
    7851069
    7861070            if ( isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
     
    8151099    public function footer_inject() {
    8161100
    817         $options = get_option( 'c7wp_settings' );
     1101        $options = $this->settings;
    8181102        if ( 'yes' === $options['c7wp_display_cart'] ) {
    8191103
     
    8421126            }
    8431127
    844             $login = ( in_array( $this->widgetsver, [ 'v2', 'v2-compat' ] ) ) ? 'c7-account' : 'c7-login';
     1128            $login = ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ) ) ) ? 'c7-account' : 'c7-login';
    8451129
    8461130            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>';
     
    8951179        $output = '<div class="c7wp-wrap" data-c7-type="' . $atts['type'] . '">';
    8961180
    897         if ( in_array( $this->widgetsver, [ 'v2', 'v2-compat' ] ) ) {
    898 
    899             switch ( $atts['type'] ) {
    900                 case 'default':
    901                     $output .= '<div id="c7-content"></div>';
    902                     break;
    903 
    904                 case 'personalization':
    905                     $output .= '<div class="c7-personalization" data-block-code="' . esc_attr( $atts['data'] ) . '"></div>';
    906                     break;
    907 
    908                 case 'subscribe':
    909                     $output .= ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-field="true"></div>' : '<div class="c7-subscribe"></div>';
    910                     break;
    911 
    912                 case 'collection':
    913                     $output .= '<div class="c7-product-collection" data-collection-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    914                     break;
    915 
    916                 case 'login':
    917                     $output .= '<div id="c7-account"></div>';
    918                     break;
    919 
    920                 case 'cart':
    921                     $output .= '<div id="c7-cart"></div>';
    922                     break;
    923 
    924                 case 'reservation':
    925                     $output .= '<div class="c7-reservation-availability" data-reservation-type-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    926                     break;
    927 
    928                 case 'form':
    929                     $output .= '<div class="c7-custom-form" data-form-code="' . esc_attr( $atts['data'] ) . '"></div>';
    930                     break;
    931 
    932                 case 'joinnow':
    933                     $output .= '<div class="c7-club-join-button"
    934                                                 data-club-slug="' . esc_attr( $atts['data'] ) . '"
    935                                                 data-join-text="' . esc_attr( $atts['join-text'] ) . '"
    936                                                 data-edit-text="' . esc_attr( $atts['edit-text'] ) . '"
    937                                                 ></div>';
    938                     break;
    939 
    940                 case 'buyslug':
    941                     $output .= '<div class="c7-buy-product" data-product-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    942                     break;
    943 
    944                 case 'loginform':
    945                     $output .= '<div id="c7-login-form" data-redirect-to="' . esc_attr( $atts['data'] ) . '"></div>';
    946                     break;
    947 
    948                 case 'collectionlist':
    949                     $output .= '<div id="c7-collection-list"></div>';
    950                     break;
    951 
    952                 default:
    953                     $output .= '<div id="c7-content"></div>';
    954                     break;
    955             }
     1181        if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
     1182            $output .= $this->render_v2_widget( $atts );
    9561183        } else {
    957 
    958             switch ( $atts['type'] ) {
    959                 case 'default':
    960                     $output .= '<div id="c7-content"></div>';
    961                     break;
    962 
    963                 case 'personalization':
    964                     $output .= '<div class="c7-personalization" data-block-code="' . esc_attr( $atts['data'] ) . '"></div>';
    965                     break;
    966 
    967                 case 'buy':
    968                     $output .= '<div class="c7-buy-variant" data-sku="' . esc_attr( $atts['data'] ) . '"></div>';
    969                     break;
    970 
    971                 case 'buyslug':
    972                     $output .= '<div class="c7-buy-variant" data-product-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    973                     break;
    974 
    975                 case 'subscribe':
    976                     $output .= ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-fields="true"></div>' : '<div class="c7-subscribe"></div>';
    977                     break;
    978 
    979                 case 'collection':
    980                     $output .= '<div class="c7-product-collection" data-collection-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    981                     break;
    982 
    983                 case 'login':
    984                     $output .= '<div id="c7-login"></div>';
    985                     break;
    986 
    987                 case 'cart':
    988                     $output .= '<div id="c7-cart"></div>';
    989                     break;
    990 
    991                 case 'reservation':
    992                     $output .= '<div class="c7-reservation-availability" data-reservation-type-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    993                     break;
    994 
    995                 case 'form':
    996                     $output .= '<div class="c7-form-wrapper" data-form-code="' . esc_attr( $atts['data'] ) . '"></div>';
    997                     break;
    998 
    999                 case 'joinnow':
    1000                     $output .= '<div class="c7-club-join-button" data-club-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    1001                     break;
    1002 
    1003                 case 'quickshop':
    1004                     $output .= '<div id="c7-quick-shop" data-collection-slug="' . esc_attr( $atts['data'] ) . '"></div>';
    1005                     break;
    1006 
    1007                 case 'loginform':
    1008                     $output .= '<div id="c7-login-form" data-redirect-to="' . esc_attr( $atts['data'] ) . '"></div>';
    1009                     break;
    1010 
    1011                 case 'createaccount':
    1012                     $output .= '<div id="c7-create-account" data-redirect-to="' . esc_attr( $atts['data'] ) . '"></div>';
    1013                     break;
    1014 
    1015                 default:
    1016                     $output .= '<div id="c7-content"></div>';
    1017                     break;
    1018             }
     1184            $output .= $this->render_v1_widget( $atts );
    10191185        }
    10201186
     
    10221188
    10231189        return $output;
     1190    }
     1191
     1192    /**
     1193     * Render V2 widget output
     1194     *
     1195     * @param array $atts Shortcode attributes.
     1196     * @return string Widget HTML.
     1197     */
     1198    private function render_v2_widget( $atts ) {
     1199        switch ( $atts['type'] ) {
     1200            case 'default':
     1201                return '<div id="c7-content"></div>';
     1202
     1203            case 'personalization':
     1204                return '<div class="c7-personalization" data-block-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1205
     1206            case 'subscribe':
     1207                return ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-field="true"></div>' : '<div class="c7-subscribe"></div>';
     1208
     1209            case 'collection':
     1210                return '<div class="c7-product-collection" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1211
     1212            case 'login':
     1213                return '<div id="c7-account"></div>';
     1214
     1215            case 'cart':
     1216                return '<div id="c7-cart"></div>';
     1217
     1218            case 'reservation':
     1219                return '<div class="c7-reservation-availability" data-reservation-type-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1220
     1221            case 'form':
     1222                return '<div class="c7-custom-form" data-form-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1223
     1224            case 'joinnow':
     1225                return '<div class="c7-club-join-button"
     1226                    data-club-slug="' . trim( esc_attr( $atts['data'] ) ) . '"
     1227                    data-join-text="' . trim( esc_attr( $atts['join-text'] ) ) . '"
     1228                    data-edit-text="' . trim( esc_attr( $atts['edit-text'] ) ) . '"
     1229                    ></div>';
     1230
     1231            case 'buyslug':
     1232                return '<div class="c7-buy-product" data-product-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1233
     1234            case 'loginform':
     1235                return '<div id="c7-login-form" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1236
     1237            case 'collectionlist':
     1238                return '<div id="c7-collection-list"></div>';
     1239
     1240            default:
     1241                return '<div id="c7-content"></div>';
     1242        }
     1243    }
     1244
     1245    /**
     1246     * Render V1 widget output
     1247     *
     1248     * @param array $atts Shortcode attributes.
     1249     * @return string Widget HTML.
     1250     */
     1251    private function render_v1_widget( $atts ) {
     1252        switch ( $atts['type'] ) {
     1253            case 'default':
     1254                return '<div id="c7-content"></div>';
     1255
     1256            case 'personalization':
     1257                return '<div class="c7-personalization" data-block-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1258
     1259            case 'buy':
     1260                return '<div class="c7-buy-variant" data-sku="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1261
     1262            case 'buyslug':
     1263                return '<div class="c7-buy-variant" data-product-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1264
     1265            case 'subscribe':
     1266                return ( 'true' === $atts['data'] ) ? '<div class="c7-subscribe" data-has-name-fields="true"></div>' : '<div class="c7-subscribe"></div>';
     1267
     1268            case 'collection':
     1269                return '<div class="c7-product-collection" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1270
     1271            case 'login':
     1272                return '<div id="c7-login"></div>';
     1273
     1274            case 'cart':
     1275                return '<div id="c7-cart"></div>';
     1276
     1277            case 'reservation':
     1278                return '<div class="c7-reservation-availability" data-reservation-type-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1279
     1280            case 'form':
     1281                return '<div class="c7-form-wrapper" data-form-code="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1282
     1283            case 'joinnow':
     1284                return '<div class="c7-club-join-button" data-club-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1285
     1286            case 'quickshop':
     1287                return '<div id="c7-quick-shop" data-collection-slug="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1288
     1289            case 'loginform':
     1290                return '<div id="c7-login-form" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1291
     1292            case 'createaccount':
     1293                return '<div id="c7-create-account" data-redirect-to="' . trim( esc_attr( $atts['data'] ) ) . '"></div>';
     1294
     1295            default:
     1296                return '<div id="c7-content"></div>';
     1297        }
    10241298    }
    10251299
     
    10331307    public function add_display_post_states( $post_states, $post ) {
    10341308
    1035         $options = get_option( 'c7wp_settings' );
     1309        $options = $this->settings;
    10361310        if ( isset( $options['c7wp_widget_version'] ) && 'v2' === $options['c7wp_widget_version']
    10371311        && isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
     
    10451319                'checkout',
    10461320                'cart',
    1047                 'privacy',
    1048                 'terms',
    10491321                'reservation',
    10501322            );
     
    10521324
    10531325        if ( in_array( $post->post_name, $pages, true ) ) {
    1054             $post_states['c7wp'] = esc_html__( 'Commerce7 Required Page', 'wp-commerce7' );
     1326            $post_states['c7wp'] = esc_html__( 'Commerce7 Dynamic Route', 'wp-commerce7' );
     1327        }
     1328
     1329        $static_pages = array(
     1330            'privacy',
     1331            'terms',
     1332        );
     1333
     1334        if ( in_array( $post->post_name, $static_pages, true ) ) {
     1335            $post_states['c7wp-static'] = esc_html__( 'Commerce7 Required Page', 'wp-commerce7' );
    10551336        }
    10561337
     
    10661347        return $classes;
    10671348    }
     1349
     1350    /**
     1351     * Check if a page has proper Commerce7 content
     1352     *
     1353     * @param int|WP_Post $page Page ID or WP_Post object.
     1354     * @return bool True if page has proper C7 content.
     1355     */
     1356    public function page_has_c7_content( $page ) {
     1357        if ( is_numeric( $page ) ) {
     1358            $page = get_post( $page );
     1359        }
     1360
     1361        if ( ! $page || 'page' !== $page->post_type ) {
     1362            return false;
     1363        }
     1364
     1365        $content = $page->post_content;
     1366
     1367        // Check for various C7 content patterns
     1368        $patterns = array(
     1369            '/c7-content/',                    // Basic C7 div
     1370            '/wp:c7wp/',                       // Gutenberg blocks
     1371            '/<!-- wp:c7wp/',                  // Gutenberg block comments
     1372            '/\[c7wp/',                        // Shortcodes
     1373            '/class="c7wp-/',                  // C7WP wrapper classes
     1374        );
     1375
     1376        foreach ( $patterns as $pattern ) {
     1377            if ( preg_match( $pattern, $content ) ) {
     1378                return true;
     1379            }
     1380        }
     1381
     1382        return false;
     1383    }
     1384
     1385    /**
     1386     * Get pages that need Commerce7 content
     1387     *
     1388     * @return array Array of page objects that need C7 content.
     1389     */
     1390    public function get_pages_needing_c7_content() {
     1391        $options = $this->settings;
     1392
     1393        if ( isset( $options['c7wp_frontend_routes'] ) && 'yes' === $options['c7wp_enable_custom_routes'] ) {
     1394            $required_pages = array_values( $options['c7wp_frontend_routes'] );
     1395        } else {
     1396            $required_pages = array( 'profile', 'collection', 'product', 'club', 'checkout', 'cart', 'reservation' );
     1397        }
     1398
     1399        $pages_needing_content = array();
     1400
     1401        foreach ( $required_pages as $page_slug ) {
     1402            $page = get_page_by_path( $page_slug, 'ARRAY_N', 'page' );
     1403            if ( $page && ! $this->page_has_c7_content( $page[0] ) ) {
     1404                $pages_needing_content[] = get_post( $page[0] );
     1405            }
     1406        }
     1407
     1408        return $pages_needing_content;
     1409    }
     1410
    10681411}
  • wp-commerce7/trunk/includes/elementor/elementor-buyslug.php

    r3292177 r3441297  
    66 * Author: Michael Bourne
    77 * -----
    8  * Last Modified: Thursday, January 9th 2025, 1:51:21 pm
     8 * Last Modified: Tuesday, September 16th 2025, 10:24:41 pm
    99 * Modified By: Michael Bourne
    1010 * -----
     
    115115
    116116        $this->add_control(
     117            'preview_notice',
     118            [
     119                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     120                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only, it does not reflect the slug you provide nor is it interactive.</div>',
     121                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
     122            ]
     123        );
     124
     125        $this->add_control(
    117126            'data',
    118127            [
     
    141150        $data = esc_attr( $settings['data'] );
    142151
    143         echo do_shortcode( '[c7wp type="buyslug" data="' . $data . '"]' );
    144 
    145     }
    146 
    147     protected function content_template() {} //phpcs:ignore
     152        // Show preview in Elementor editor, shortcode on frontend
     153        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     154            $slugProvided = !empty( $data );
     155            ?>
     156            <div class="c7-buy-product-placeholder" data-product-slug="<?php echo esc_attr( $data ); ?>">
     157                <?php if ( $slugProvided ) : ?>
     158                    <form action="#" class="c7-form c7-product__add-to-cart">
     159                        <div class="c7-product__add-to-cart__price">
     160                            <span class="c7-sr-only">Item Price</span>
     161                            <span class="c7-price--original">$45<span>.00</span></span>
     162                            <span class="c7-price--discounted">$38<span>.00</span></span>
     163                            <span class="c7-product__add-to-cart__price__variant">
     164                                <span class="c7-product__variant__price__title">bottle</span>
     165                            </span>
     166                        </div>
     167                        <div class="c7-product__add-to-cart__form">
     168                            <div class="c7-product__add-to-cart__form__quantity">
     169                                <div class="c7-form__field">
     170                                    <input name="quantity" type="text" inputmode="numeric" value="1" class="c7-product__quantity-input" tabindex="-1" disabled>
     171                                </div>
     172                            </div>
     173                            <button type="submit" class="c7-btn c7-btn--primary" tabindex="-1" disabled>
     174                                <span>Add to Cart</span>
     175                            </button>
     176                        </div>
     177                    </form>
     178                <?php else : ?>
     179                    <div class="c7-message c7-message--alert-error" role="presentation">
     180                        <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     181                            <circle cx="12" cy="12" r="10"></circle>
     182                            <line x1="12" y1="8" x2="12" y2="12"></line>
     183                            <line x1="12" y1="16" x2="12.01" y2="16"></line>
     184                        </svg>
     185                        Please enter a product slug in the widget settings
     186                    </div>
     187                <?php endif; ?>
     188            </div>
     189            <?php
     190        } else {
     191            // Frontend - show actual shortcode
     192            echo do_shortcode( '[c7wp type="buyslug" data="' . $data . '"]' );
     193        }
     194
     195    }
     196
     197    /**
     198     * Render widget output in the editor.
     199     *
     200     * Written as a Backbone JavaScript template and used to generate the live preview.
     201     *
     202     * @since 1.0.0
     203     * @access protected
     204     */
     205    protected function content_template() {
     206        ?>
     207        <#
     208        var slugProvided = settings.data && settings.data.length > 0;
     209        #>
     210        <div class="c7-buy-product-placeholder" data-product-slug="{{{ settings.data }}}">
     211            <# if (slugProvided) { #>
     212                <form action="#" class="c7-form c7-product__add-to-cart">
     213                    <div class="c7-product__add-to-cart__price">
     214                        <span class="c7-sr-only">Item Price</span>
     215                        <span class="c7-price--original">$45<span>.00</span></span>
     216                        <span class="c7-price--discounted">$38<span>.00</span></span>
     217                        <span class="c7-product__add-to-cart__price__variant">
     218                            <span class="c7-product__variant__price__title">bottle</span>
     219                        </span>
     220                    </div>
     221                    <div class="c7-product__add-to-cart__form">
     222                        <div class="c7-product__add-to-cart__form__quantity">
     223                            <div class="c7-form__field">
     224                                <input name="quantity" type="text" inputmode="numeric" value="1" class="c7-product__quantity-input" tabindex="-1" disabled>
     225                            </div>
     226                        </div>
     227                        <button type="submit" class="c7-btn c7-btn--primary" tabindex="-1" disabled>
     228                            <span>Add to Cart</span>
     229                        </button>
     230                    </div>
     231                </form>
     232            <# } else { #>
     233                <div class="c7-message c7-message--alert-error" role="presentation">
     234                    <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     235                        <circle cx="12" cy="12" r="10"></circle>
     236                        <line x1="12" y1="8" x2="12" y2="12"></line>
     237                        <line x1="12" y1="16" x2="12.01" y2="16"></line>
     238                    </svg>
     239                    Please enter a product slug in the widget settings
     240                </div>
     241            <# } #>
     242        </div>
     243        <?php
     244    }
    148245    public function render_plain_content( $instance = [] ) {}
    149246
  • wp-commerce7/trunk/includes/elementor/elementor-form.php

    r3292177 r3441297  
    116116
    117117        $this->add_control(
     118            'preview_notice',
     119            [
     120                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     121                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only, it does not reflect the form you provide nor is it interactive.</div>',
     122                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
     123            ]
     124        );
     125
     126        $this->add_control(
    118127            'data',
    119128            [
     
    142151        $data = esc_attr( $settings['data'] );
    143152
    144         echo do_shortcode( '[c7wp type="form" data="' . $data . '"]' );
    145 
    146     }
    147 
    148     protected function content_template() {} //phpcs:ignore
     153        // Show preview in Elementor editor, shortcode on frontend
     154        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     155            $slugProvided = !empty( $data );
     156            ?>
     157            <div class="c7-custom-form-placeholder" data-form-code="<?php echo esc_attr( $data ); ?>">
     158                <?php if ( $slugProvided ) : ?>
     159                    <form class="c7-form">
     160                        <fieldset>
     161                            <legend class="c7-sr-only">Contact Us</legend>
     162                            <div class="c7-form__field">
     163                                <label class="c7-required">First & Last Name</label>
     164                                <input name="fullName" type="text" value="" disabled tabindex="-1">
     165                            </div>
     166                            <div class="c7-form__field">
     167                                <label class="c7-required">Country</label>
     168                                <select name="countryCode" disabled tabindex="-1">
     169                                    <option value="CA">Canada</option>
     170                                    <option value="US">United States</option>
     171                                </select>
     172                            </div>
     173                            <div class="c7-form__field">
     174                                <label>Phone</label>
     175                                <input name="phone" type="tel" value="" disabled tabindex="-1">
     176                            </div>
     177                            <div class="c7-form__field">
     178                                <label class="c7-required">Email</label>
     179                                <input name="email" type="email" value="" disabled tabindex="-1">
     180                            </div>
     181                            <div class="c7-form__field">
     182                                <label class="c7-required">Questions/Comments</label>
     183                                <textarea name="questions-comments" rows="3" disabled tabindex="-1"></textarea>
     184                            </div>
     185                        </fieldset>
     186                        <div class="c7-form__buttons">
     187                            <button type="submit" class="c7-btn c7-btn--primary" disabled tabindex="-1">
     188                                <span>Submit</span>
     189                            </button>
     190                        </div>
     191                    </form>
     192                <?php else : ?>
     193                    <div class="c7-message c7-message--alert-error" role="presentation">
     194                        <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     195                            <circle cx="12" cy="12" r="10"></circle>
     196                            <line x1="12" y1="8" x2="12" y2="12"></line>
     197                            <line x1="12" y1="16" x2="12.01" y2="16"></line>
     198                        </svg>
     199                        Please enter a form slug in the widget settings
     200                    </div>
     201                <?php endif; ?>
     202            </div>
     203            <?php
     204        } else {
     205            // Frontend - show actual shortcode
     206            echo do_shortcode( '[c7wp type="form" data="' . $data . '"]' );
     207        }
     208
     209    }
     210
     211    /**
     212     * Render widget output in the editor.
     213     *
     214     * Written as a Backbone JavaScript template and used to generate the live preview.
     215     *
     216     * @since 1.0.0
     217     * @access protected
     218     */
     219    protected function content_template() {
     220        ?>
     221        <#
     222        var slugProvided = settings.data && settings.data.length > 0;
     223        #>
     224        <div class="c7-custom-form-placeholder" data-form-code="{{{ settings.data }}}">
     225            <# if (slugProvided) { #>
     226                <form class="c7-form">
     227                    <fieldset>
     228                        <legend class="c7-sr-only">Contact Us</legend>
     229                        <div class="c7-form__field">
     230                            <label class="c7-required">First & Last Name</label>
     231                            <input name="fullName" type="text" value="" disabled tabindex="-1">
     232                        </div>
     233                        <div class="c7-form__field">
     234                            <label class="c7-required">Country</label>
     235                            <select name="countryCode" disabled tabindex="-1">
     236                                <option value="CA">Canada</option>
     237                                <option value="US">United States</option>
     238                            </select>
     239                        </div>
     240                        <div class="c7-form__field">
     241                            <label>Phone</label>
     242                            <input name="phone" type="tel" value="" disabled tabindex="-1">
     243                        </div>
     244                        <div class="c7-form__field">
     245                            <label class="c7-required">Email</label>
     246                            <input name="email" type="email" value="" disabled tabindex="-1">
     247                        </div>
     248                        <div class="c7-form__field">
     249                            <label class="c7-required">Questions/Comments</label>
     250                            <textarea name="questions-comments" rows="3" disabled tabindex="-1"></textarea>
     251                        </div>
     252                    </fieldset>
     253                    <div class="c7-form__buttons">
     254                        <button type="submit" class="c7-btn c7-btn--primary" disabled tabindex="-1">
     255                            <span>Submit</span>
     256                        </button>
     257                    </div>
     258                </form>
     259            <# } else { #>
     260                <div class="c7-message c7-message--alert-error" role="presentation">
     261                    <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     262                        <circle cx="12" cy="12" r="10"></circle>
     263                        <line x1="12" y1="8" x2="12" y2="12"></line>
     264                        <line x1="12" y1="16" x2="12.01" y2="16"></line>
     265                    </svg>
     266                    Please enter a form slug in the widget settings
     267                </div>
     268            <# } #>
     269        </div>
     270        <?php
     271    }
    149272    public function render_plain_content( $instance = [] ) {}
    150273
  • wp-commerce7/trunk/includes/elementor/elementor-joinnow.php

    r3292177 r3441297  
    116116
    117117        $this->add_control(
     118            'preview_notice',
     119            [
     120                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     121                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only, it does not reflect the club slug you provide nor is it interactive.</div>',
     122                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
     123            ]
     124        );
     125
     126        $this->add_control(
    118127            'data',
    119128            [
     
    160169        $data = esc_attr( $settings['data'] );
    161170
    162         $extras = '';
    163 
    164         if ( ! empty( $settings['join-text'] ) ) {
    165             $extras .= ' join-text="' . esc_attr( $settings['join-text'] ) . '"';
     171        // Show preview in Elementor editor, shortcode on frontend
     172        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     173            $slugProvided = !empty( $data );
     174            $joinText = !empty( $settings['join-text'] ) ? $settings['join-text'] : 'Join Now';
     175            $editText = !empty( $settings['edit-text'] ) ? $settings['edit-text'] : 'Edit Membership';
     176            ?>
     177            <div class="c7-club-join-button-placeholder" data-club-slug="<?php echo esc_attr( $data ); ?>" data-join-text="<?php echo esc_attr( $joinText ); ?>" data-edit-text="<?php echo esc_attr( $editText ); ?>">
     178                <?php if ( $slugProvided ) : ?>
     179                    <a class="c7-btn c7-btn--primary" role="link" tabindex="-1" disabled>
     180                        <?php echo esc_html( $joinText ); ?>
     181                    </a>
     182                <?php else : ?>
     183                    <div class="c7-message c7-message--alert-error" role="presentation">
     184                        <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     185                            <circle cx="12" cy="12" r="10"></circle>
     186                            <line x1="12" y1="8" x2="12" y2="12"></line>
     187                            <line x1="12" y1="16" x2="12.01" y2="16"></line>
     188                        </svg>
     189                        Please enter a club slug in the widget settings
     190                    </div>
     191                <?php endif; ?>
     192            </div>
     193            <?php
     194        } else {
     195            // Frontend - show actual shortcode
     196            $extras = '';
     197            if ( ! empty( $settings['join-text'] ) ) {
     198                $extras .= ' join-text="' . esc_attr( $settings['join-text'] ) . '"';
     199            }
     200            if ( ! empty( $settings['edit-text'] ) ) {
     201                $extras .= ' edit-text="' . esc_attr( $settings['edit-text'] ) . '"';
     202            }
     203            echo do_shortcode( '[c7wp type="joinnow" data="' . $data . '"' . $extras . ']' );
    166204        }
    167         if ( ! empty( $settings['edit-text'] ) ) {
    168             $extras .= ' edit-text="' . esc_attr( $settings['edit-text'] ) . '"';
    169         }
    170 
    171         echo do_shortcode( '[c7wp type="joinnow" data="' . $data . '"' . $extras . ']' );
    172     }
    173 
    174     protected function content_template() {} //phpcs:ignore
     205    }
     206
     207    /**
     208     * Render widget output in the editor.
     209     *
     210     * Written as a Backbone JavaScript template and used to generate the live preview.
     211     *
     212     * @since 1.0.0
     213     * @access protected
     214     */
     215    protected function content_template() {
     216        ?>
     217        <#
     218        var slugProvided = settings.data && settings.data.length > 0;
     219        var joinText = settings['join-text'] || 'Join Now';
     220        var editText = settings['edit-text'] || 'Edit Membership';
     221        #>
     222        <div class="c7-club-join-button-placeholder" data-club-slug="{{{ settings.data }}}" data-join-text="{{{ joinText }}}" data-edit-text="{{{ editText }}}">
     223            <# if (slugProvided) { #>
     224                <a class="c7-btn c7-btn--primary" role="link" tabindex="-1" disabled>
     225                    {{{ joinText }}}
     226                </a>
     227            <# } else { #>
     228                <div class="c7-message c7-message--alert-error" role="presentation">
     229                    <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     230                        <circle cx="12" cy="12" r="10"></circle>
     231                        <line x1="12" y1="8" x2="12" y2="12"></line>
     232                        <line x1="12" y1="16" x2="12.01" y2="16"></line>
     233                    </svg>
     234                    Please enter a club slug in the widget settings
     235                </div>
     236            <# } #>
     237        </div>
     238        <?php
     239    }
    175240    public function render_plain_content( $instance = [] ) {}
    176241
  • wp-commerce7/trunk/includes/elementor/elementor-loginform.php

    r3292177 r3441297  
    116116
    117117        $this->add_control(
     118            'preview_notice',
     119            [
     120                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     121                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only, it does not reflect the redirect path you provide nor is it interactive.</div>',
     122                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
     123            ]
     124        );
     125
     126        $this->add_control(
    118127            'data',
    119128            [
     
    142151        $data = esc_attr( $settings['data'] );
    143152
    144         echo do_shortcode( '[c7wp type="loginform" data="' . $data . '"]' );
    145 
    146     }
    147 
    148     protected function content_template() {} //phpcs:ignore
     153        // Show preview in Elementor editor, shortcode on frontend
     154        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     155            ?>
     156            <div id="c7-login-form-placeholder" data-redirect-to="<?php echo esc_attr( $data ); ?>">
     157                <form class="c7-form c7-form--login" data-sku="<?php echo esc_attr( $data ); ?>">
     158                    <div class="c7-form__field">
     159                        <label class="c7-required">Email</label>
     160                        <input name="email" tabindex="-1" disabled type="text">
     161                    </div>
     162                    <div class="c7-form__field">
     163                        <label class="c7-required">Password</label>
     164                        <input name="password" tabindex="-1" disabled type="password">
     165                    </div>
     166                    <div class="c7-form__buttons c7-form__buttons--wide">
     167                        <button type="submit" tabindex="-1" disabled class="c7-btn c7-btn--primary">
     168                            <span>Log in</span>
     169                        </button>
     170                    </div>
     171                </form>
     172            </div>
     173            <?php
     174        } else {
     175            // Frontend - show actual shortcode
     176            echo do_shortcode( '[c7wp type="loginform" data="' . $data . '"]' );
     177        }
     178
     179    }
     180
     181    /**
     182     * Render widget output in the editor.
     183     *
     184     * Written as a Backbone JavaScript template and used to generate the live preview.
     185     *
     186     * @since 1.0.0
     187     * @access protected
     188     */
     189    protected function content_template() {
     190        ?>
     191        <#
     192        var slugProvided = settings.data && settings.data.length > 0;
     193        #>
     194        <div id="c7-login-form-placeholder" data-redirect-to="{{{ settings.data }}}">
     195            <form class="c7-form c7-form--login" data-sku="{{{ settings.data }}}">
     196                <div class="c7-form__field">
     197                    <label class="c7-required">Email</label>
     198                    <input name="email" tabindex="-1" disabled type="text">
     199                </div>
     200                <div class="c7-form__field">
     201                    <label class="c7-required">Password</label>
     202                    <input name="password" tabindex="-1" disabled type="password">
     203                </div>
     204                <div class="c7-form__buttons c7-form__buttons--wide">
     205                    <button type="submit" tabindex="-1" disabled class="c7-btn c7-btn--primary">
     206                        <span>Log in</span>
     207                    </button>
     208                </div>
     209            </form>
     210        </div>
     211        <?php
     212    }
    149213    public function render_plain_content( $instance = [] ) {}
    150214
  • wp-commerce7/trunk/includes/elementor/elementor-reservation.php

    r3292177 r3441297  
    116116
    117117        $this->add_control(
     118            'preview_notice',
     119            [
     120                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     121                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only, it does not reflect the experience slug you provide nor is it interactive.</div>',
     122                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
     123            ]
     124        );
     125
     126        $this->add_control(
    118127            'data',
    119128            [
     
    142151        $data = esc_attr( $settings['data'] );
    143152
    144         echo do_shortcode( '[c7wp type="reservation" data="' . $data . '"]' );
    145 
    146     }
    147 
    148     protected function content_template() {} //phpcs:ignore
     153        // Show preview in Elementor editor, shortcode on frontend
     154        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     155            $slugProvided = !empty( $data );
     156            ?>
     157            <div class="c7-reservation-availability-placeholder" data-reservation-type-slug="<?php echo esc_attr( $data ); ?>">
     158                <?php if ( $slugProvided ) : ?>
     159                    <section class="c7-reservation__search">
     160                        <form class="c7-form">
     161                            <div class="c7-form__group">
     162                                <div class="c7-form__field">
     163                                    <label class="c7-required">Date</label>
     164                                    <div class="c7-date-picker-input">
     165                                        <input type="text" placeholder="MMM DD YYYY" value="" tabindex="-1" disabled>
     166                                        <button type="button" class="c7-date-picker-toggle" tabindex="-1" disabled>
     167                                            <span role="img">
     168                                                <img class="emoji" src="https://s.w.org/images/core/emoji/14.0.0/svg/1f4c5.svg">
     169                                            </span>
     170                                        </button>
     171                                    </div>
     172                                </div>
     173                                <div class="c7-form__field">
     174                                    <label class="c7-required">Time</label>
     175                                    <select tabindex="-1" disabled>
     176                                        <option value=""></option>
     177                                        <option value="10:00:00">10:00 am</option>
     178                                        <option value="10:30:00">10:30 am</option>
     179                                        <option value="11:00:00">11:00 am</option>
     180                                        <option value="11:30:00">11:30 am</option>
     181                                        <option value="12:00:00">12:00 pm</option>
     182                                        <option value="12:30:00">12:30 pm</option>
     183                                        <option value="13:00:00">1:00 pm</option>
     184                                        <option value="13:30:00">1:30 pm</option>
     185                                        <option value="14:00:00">2:00 pm</option>
     186                                        <option value="14:30:00">2:30 pm</option>
     187                                        <option value="15:00:00">3:00 pm</option>
     188                                        <option value="15:30:00">3:30 pm</option>
     189                                        <option value="16:00:00">4:00 pm</option>
     190                                    </select>
     191                                </div>
     192                                <div class="c7-form__field">
     193                                    <label class="c7-required">No of Guests</label>
     194                                    <select tabindex="-1" disabled>
     195                                        <option value=""></option>
     196                                        <option value="1">1</option>
     197                                        <option value="2">2</option>
     198                                        <option value="3">3</option>
     199                                        <option value="4">4</option>
     200                                        <option value="5">5</option>
     201                                    </select>
     202                                </div>
     203                                <button type="submit" class="c7-btn c7-btn--primary" tabindex="-1" disabled>
     204                                    <span>Check Availability</span>
     205                                </button>
     206                            </div>
     207                        </form>
     208                    </section>
     209                <?php else : ?>
     210                    <div class="c7-message c7-message--alert-error" role="presentation">
     211                        <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     212                            <circle cx="12" cy="12" r="10"></circle>
     213                            <line x1="12" y1="8" x2="12" y2="12"></line>
     214                            <line x1="12" y1="16" x2="12.01" y2="16"></line>
     215                        </svg>
     216                        Please enter the single experience slug in the widget settings
     217                    </div>
     218                <?php endif; ?>
     219            </div>
     220            <?php
     221        } else {
     222            // Frontend - show actual shortcode
     223            echo do_shortcode( '[c7wp type="reservation" data="' . $data . '"]' );
     224        }
     225
     226    }
     227
     228    /**
     229     * Render widget output in the editor.
     230     *
     231     * Written as a Backbone JavaScript template and used to generate the live preview.
     232     *
     233     * @since 1.0.0
     234     * @access protected
     235     */
     236    protected function content_template() {
     237        ?>
     238        <#
     239        var slugProvided = settings.data && settings.data.length > 0;
     240        #>
     241        <div class="c7-reservation-availability-placeholder" data-reservation-type-slug="{{{ settings.data }}}">
     242            <# if (slugProvided) { #>
     243                <section class="c7-reservation__search">
     244                    <form class="c7-form">
     245                        <div class="c7-form__group">
     246                            <div class="c7-form__field">
     247                                <label class="c7-required">Date</label>
     248                                <div class="c7-date-picker-input">
     249                                    <input type="text" placeholder="MMM DD YYYY" value="" tabindex="-1" disabled>
     250                                    <button type="button" class="c7-date-picker-toggle" tabindex="-1" disabled>
     251                                        <span role="img">
     252                                            <img class="emoji" src="https://s.w.org/images/core/emoji/14.0.0/svg/1f4c5.svg">
     253                                        </span>
     254                                    </button>
     255                                </div>
     256                            </div>
     257                            <div class="c7-form__field">
     258                                <label class="c7-required">Time</label>
     259                                <select tabindex="-1" disabled>
     260                                    <option value=""></option>
     261                                    <option value="10:00:00">10:00 am</option>
     262                                    <option value="10:30:00">10:30 am</option>
     263                                    <option value="11:00:00">11:00 am</option>
     264                                    <option value="11:30:00">11:30 am</option>
     265                                    <option value="12:00:00">12:00 pm</option>
     266                                    <option value="12:30:00">12:30 pm</option>
     267                                    <option value="13:00:00">1:00 pm</option>
     268                                    <option value="13:30:00">1:30 pm</option>
     269                                    <option value="14:00:00">2:00 pm</option>
     270                                    <option value="14:30:00">2:30 pm</option>
     271                                    <option value="15:00:00">3:00 pm</option>
     272                                    <option value="15:30:00">3:30 pm</option>
     273                                    <option value="16:00:00">4:00 pm</option>
     274                                </select>
     275                            </div>
     276                            <div class="c7-form__field">
     277                                <label class="c7-required">No of Guests</label>
     278                                <select tabindex="-1" disabled>
     279                                    <option value=""></option>
     280                                    <option value="1">1</option>
     281                                    <option value="2">2</option>
     282                                    <option value="3">3</option>
     283                                    <option value="4">4</option>
     284                                    <option value="5">5</option>
     285                                </select>
     286                            </div>
     287                            <button type="submit" class="c7-btn c7-btn--primary" tabindex="-1" disabled>
     288                                <span>Check Availability</span>
     289                            </button>
     290                        </div>
     291                    </form>
     292                </section>
     293            <# } else { #>
     294                <div class="c7-message c7-message--alert-error" role="presentation">
     295                    <svg aria-hidden="true" focusable="false" role="presentation" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     296                        <circle cx="12" cy="12" r="10"></circle>
     297                        <line x1="12" y1="8" x2="12" y2="12"></line>
     298                        <line x1="12" y1="16" x2="12.01" y2="16"></line>
     299                    </svg>
     300                    Please enter the single experience slug in the widget settings
     301                </div>
     302            <# } #>
     303        </div>
     304        <?php
     305    }
    149306    public function render_plain_content( $instance = [] ) {}
    150307
  • wp-commerce7/trunk/includes/elementor/elementor-subscribe.php

    r3292177 r3441297  
    66 * Author: Michael Bourne
    77 * -----
    8  * Last Modified: Thursday, January 9th 2025, 1:51:13 pm
     8 * Last Modified: Tuesday, September 16th 2025, 10:35:48 pm
    99 * Modified By: Michael Bourne
    1010 * -----
     
    111111                'label' => __( 'Settings', 'wp-commerce7' ),
    112112                'tab'   => \Elementor\Controls_Manager::TAB_CONTENT,
     113            ]
     114        );
     115
     116        $this->add_control(
     117            'preview_notice',
     118            [
     119                'type'            => \Elementor\Controls_Manager::RAW_HTML,
     120                'raw'             => '<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 15px;"><strong>Preview Notice:</strong> The preview shown in your editor is for example purposes only.</div>',
     121                'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
    113122            ]
    114123        );
     
    151160        $data = esc_attr( $settings['data'] );
    152161
    153         echo do_shortcode( '[c7wp type="subscribe" data="' . $data . '"]' );
    154 
    155     }
    156 
    157     protected function content_template() {} //phpcs:ignore
     162        // Show preview in Elementor editor, shortcode on frontend
     163        if ( \Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
     164            $showNameField = ( $data === 'true' );
     165            ?>
     166            <div class="c7-subscribe-placeholder" data-has-name-field="<?php echo esc_attr( $data ); ?>">
     167                <form class="c7-form">
     168                    <div class="c7-form__group">
     169                        <?php if ( $showNameField ) : ?>
     170                            <div class="c7-form__field">
     171                                <label class="c7-required">First & Last Name</label>
     172                                <input name="fullName" type="text" tabindex="-1" disabled value="">
     173                            </div>
     174                        <?php endif; ?>
     175                        <div class="c7-form__field">
     176                            <label class="c7-required">Email</label>
     177                            <input name="email" type="email" tabindex="-1" disabled value="">
     178                        </div>
     179                        <button type="submit" tabindex="-1" disabled class="c7-btn c7-btn--primary">
     180                            <span>Subscribe</span>
     181                        </button>
     182                    </div>
     183                </form>
     184            </div>
     185            <?php
     186        } else {
     187            // Frontend - show actual shortcode
     188            echo do_shortcode( '[c7wp type="subscribe" data="' . $data . '"]' );
     189        }
     190
     191    }
     192
     193    /**
     194     * Render widget output in the editor.
     195     *
     196     * Written as a Backbone JavaScript template and used to generate the live preview.
     197     *
     198     * @since 1.0.0
     199     * @access protected
     200     */
     201    protected function content_template() {
     202        ?>
     203        <#
     204        var showNameField = settings.data === 'true';
     205        #>
     206        <div class="c7-subscribe-placeholder" data-has-name-field="{{{ settings.data }}}">
     207            <form class="c7-form">
     208                <div class="c7-form__group">
     209                    <# if (showNameField) { #>
     210                        <div class="c7-form__field">
     211                            <label class="c7-required">First & Last Name</label>
     212                            <input name="fullName" type="text" tabindex="-1" disabled value="">
     213                        </div>
     214                    <# } #>
     215                    <div class="c7-form__field">
     216                        <label class="c7-required">Email</label>
     217                        <input name="email" type="email" tabindex="-1" disabled value="">
     218                    </div>
     219                    <button type="submit" tabindex="-1" disabled class="c7-btn c7-btn--primary">
     220                        <span>Subscribe</span>
     221                    </button>
     222                </div>
     223            </form>
     224        </div>
     225        <?php
     226    }
    158227    public function render_plain_content( $instance = [] ) {}
    159228
  • wp-commerce7/trunk/includes/elementor/load.php

    r3313519 r3441297  
    66 * Author: Michael Bourne
    77 * -----
    8  * Last Modified: Thursday, June 5th 2025, 5:11:55 pm
     8 * Last Modified: Tuesday, September 16th 2025, 8:35:52 pm
    99 * Modified By: Michael Bourne
    1010 * -----
     
    2222\Elementor\Plugin::instance()->widgets_manager->register_widget_type( new \C7WP_Elementor() );
    2323
    24 if ( in_array( $this->widgetsver, [ 'v2', 'v2-compat' ] ) ) {
    25     $elements = [
     24if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ), true ) ) {
     25    $elements = array(
    2626        'personalization',
    2727        'buyslug',
     
    3535        'default',
    3636        'collectionlist',
    37     ];
     37        'clubselector',
     38    );
    3839} else {
    39     $elements = [
     40    $elements = array(
    4041        'personalization',
    4142        'buy',
     
    5152        'loginform',
    5253        'createaccount',
    53     ];
     54    );
     55
    5456}
    5557
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/buyslug/c7wp-buyslug.css

    r3009702 r3441297  
    33    z-index: 1;
    44}
    5 .wp-block-c7wp-buyslug > div {
     5
     6.wp-block-c7wp-buyslug>div {
    67    max-width: 100%;
    78}
     9
    810.wp-block-c7wp-buyslug .c7-product__add-to-cart {
    911    pointer-events: none;
    1012}
     13
    1114.wp-block-c7wp-buyslug .c7-message {
    1215    pointer-events: none;
    1316}
     17
     18
     19
     20.c7wp-justify-center {
     21    .c7-buy-product {
     22        .c7-form {
     23            max-width: unset;
     24        }
     25
     26        .c7-product__add-to-cart__form {
     27            justify-content: center;
     28        }
     29
     30        .c7-product__add-to-cart__price {
     31            text-align: center;
     32        }
     33    }
     34}
     35
     36.c7wp-justify-right {
     37    .c7-buy-product {
     38        .c7-form {
     39            max-width: unset;
     40        }
     41
     42        .c7-product__add-to-cart__form {
     43            justify-content: flex-end;
     44        }
     45
     46        .c7-product__add-to-cart__price {
     47            text-align: right;
     48        }
     49    }
     50}
     51
     52.c7wp-justify-left {
     53    .c7-buy-product {
     54        margin-left: 0;
     55
     56        .c7-product__add-to-cart__form {
     57            justify-content: flex-start;
     58        }
     59    }
     60}
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/buyslug/c7wp-buyslug.js

    r3167363 r3441297  
    33    const { registerBlockType } = blocks;
    44    const { InspectorControls } = blockEditor;
    5     const { TextControl, PanelBody } = components;
     5    const { TextControl, PanelBody, SelectControl } = components;
    66    const { createElement } = element;
    77    const { __ } = i18n;
     
    2424                selector: '.c7-buy-product',
    2525                attribute: 'data-product-slug',
     26            },
     27            justifyContent: {
     28                type: 'string',
     29                default: 'center',
    2630            }
    2731        },
     
    3034            function updateData(value) {
    3135                props.setAttributes({ data: value });
     36            }
     37
     38            function updateJustifyContent(value) {
     39                props.setAttributes({ justifyContent: value });
     40            }
     41
     42            function getAlignmentClass(justifyContent) {
     43                const alignmentMap = {
     44                    'flex-start': 'c7wp-justify-left',
     45                    'center': 'c7wp-justify-center',
     46                    'flex-end': 'c7wp-justify-right'
     47                };
     48                return alignmentMap[justifyContent] || 'c7wp-justify-center';
    3249            }
    3350
     
    5269                            value: props.attributes.data,
    5370                            onChange: updateData,
     71                        }),
     72                        createElement(SelectControl, {
     73                            label: 'Form Alignment',
     74                            value: props.attributes.justifyContent || 'center',
     75                            options: [
     76                                { label: 'Left', value: 'flex-start' },
     77                                { label: 'Center', value: 'center' },
     78                                { label: 'Right', value: 'flex-end' }
     79                            ],
     80                            onChange: updateJustifyContent,
    5481                        })
    5582                    ),
    5683                ),
    57                 createElement('div', { className: props.className },
     84                createElement('div', { className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ') },
    5885                    createElement('div', {
    5986                        className: 'c7-buy-product',
     
    149176            ];
    150177        },
     178        deprecated: [
     179            {
     180                attributes: {
     181                    data: {
     182                        type: 'string',
     183                        source: 'attribute',
     184                        selector: '.c7-buy-product',
     185                        attribute: 'data-product-slug',
     186                    }
     187                },
     188                save: function (props) {
     189                    return (
     190                        createElement('div', {
     191                            className: props.className
     192                        },
     193                            createElement('div', {
     194                                className: 'c7-buy-product',
     195                                'data-product-slug': props.attributes.data,
     196                            }
     197                            )
     198                        )
     199                    );
     200                }
     201            }
     202        ],
    151203        save: function (props) {
     204            function getAlignmentClass(justifyContent) {
     205                const alignmentMap = {
     206                    'flex-start': 'c7wp-justify-left',
     207                    'center': 'c7wp-justify-center',
     208                    'flex-end': 'c7wp-justify-right'
     209                };
     210                return alignmentMap[justifyContent] || 'c7wp-justify-center';
     211            }
     212
    152213            return (
    153214                createElement('div', {
    154                     className: props.className
     215                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
    155216                },
    156217                    createElement('div', {
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/clubselector/c7wp-clubselector.css

    r3292177 r3441297  
    4242    cursor: default;
    4343    opacity: 0.8;
    44 }
     44}
     45
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/clubselector/c7wp-clubselector.js

    r3313519 r3441297  
    8888            // Force update button text and URL whenever clubs change
    8989            useEffect(() => {
    90                 const buttonBlock = wp.data.select('core/block-editor').getBlock(clientId).innerBlocks[0];
     90                const block = wp.data.select('core/block-editor').getBlock(clientId);
     91                if (!block || !block.innerBlocks) return;
     92               
     93                const buttonBlock = block.innerBlocks[0];
    9194                if (buttonBlock && clubs.length > 0) {
    9295                    const firstClub = clubs[0];
     
    105108            useEffect(() => {
    106109                const block = wp.data.select('core/block-editor').getBlock(clientId);
    107                 if (!block) return;
     110                if (!block || !block.innerBlocks) return;
     111               
    108112                // Filter to only core/button blocks
    109113                const buttonBlocks = block.innerBlocks.filter(b => b.name === 'core/button');
     
    118122                    );
    119123                }
    120                 const buttonBlock = wp.data.select('core/block-editor').getBlock(clientId).innerBlocks[0];
     124               
     125                const updatedBlock = wp.data.select('core/block-editor').getBlock(clientId);
     126                if (!updatedBlock || !updatedBlock.innerBlocks) return;
     127               
     128                const buttonBlock = updatedBlock.innerBlocks[0];
    121129                if (buttonBlock) {
    122130                    const unsubscribe = wp.data.subscribe(() => {
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/clubselector/frontend.js

    r3292177 r3441297  
    1818    // Iterate through each club selector element
    1919    clubSelectors.forEach((selector) => {
    20         // Get the button element
    21         const button = selector.querySelector('.wp-block-button__link');
     20        // Get the button element - check for both Gutenberg and Elementor buttons
     21        const button = selector.querySelector('.wp-block-button__link') || selector.querySelector('.elementor-button-link');
    2222       
    2323        // Handle radio button selection
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/collection/c7wp-collection.css

    r3009702 r3441297  
    22    overflow: hidden;
    33    z-index: 1;
     4    border: 1px solid currentColor;
     5    padding: 1rem;
    46}
    57.wp-block-c7wp-collection > div {
     
    1517    max-height: 100%;
    1618    position: absolute;
    17     top: 0;
    18     right: 0;
     19    top: 1rem;
     20    right: 1rem;
    1921    background-image: url('../../../../assets/c7bg.svg');
    2022    background-size: contain;
    2123    background-repeat: no-repeat;
    2224    background-position: center right;
    23     opacity: 0.05;
     25    opacity: 0.08;
    2426    pointer-events: none;
    2527    z-index: -1;
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/collection/c7wp-collection.js

    r3167363 r3441297  
    22
    33    const { registerBlockType } = blocks;
    4     const { InspectorControls } = blockEditor;
     4    const { InspectorControls, useBlockProps } = blockEditor; // ADD useBlockProps here
    55    const { TextControl, PanelBody } = components;
    66    const { createElement } = element;
     
    1111    );
    1212
    13     registerBlockType( 'c7wp/collection', {
    14         title: __( 'Collection Grid' ), // The title of block in editor.
    15         description: __( 'Displays a single Commerce7 Collection.' ),
    16         icon: iconEl,
    17         category: 'commerce7', // The category of block in editor.
     13    registerBlockType( 'c7wp/collection', {
     14        title: __( 'Collection Grid', 'wp-commerce7' ),
     15        description: __( 'Displays a single Commerce7 Collection.', 'wp-commerce7' ),
     16        icon: iconEl,
     17        category: 'commerce7',
    1818        keywords: [ 'commerce7', 'collection', 'shop', 'products' ],
    1919        example: {},
    20         attributes: {
     20        attributes: {
    2121            data: {
    2222                type: 'string',
     
    2626            }
    2727        },
    28         edit: function( props ) {
     28        edit: function( props ) {
     29            const blockProps = useBlockProps();
    2930
    3031            function updateData(value) {
     
    4142                            marginBottom: '5px',
    4243                        }
    43                     },
    44                         createElement('small',
    45                             null,
    46                             'The preview shown in your editor is for example purposes only, it does not reflect the collection slug you provide nor is it interactive.'
    47                         )
    48                     ),
     44                    }),
    4945                    createElement(PanelBody, { title: 'Settings' },
    5046                        createElement(TextControl, {
     
    5551                    ),
    5652                ),
    57                 slugProvided && createElement( 'div', {
    58                     className: 'components-placeholder ' + props.className
    59                     },
     53                slugProvided && createElement( 'div', blockProps,
    6054                    createElement( 'div', {
    6155                        className: 'c7-product-collection',
    6256                        'data-collection-slug': props.attributes.data,
    6357                        },
    64                         // todo: insert demo html here
    65                         createElement( 'div', { style: {
    66                             textAlign: 'center',
    67                          } },
     58                        createElement( 'div', null,
    6859                            createElement( 'h2', null, "Commerce7 Collection" ),
    69                             createElement( 'p', null, props.attributes.data )
     60                            createElement( 'p', null, "Collection slug: " + props.attributes.data )
    7061                        ),
    7162                    )
    7263                ),
    73                 !slugProvided && createElement('div', { className: 'c7-message c7-message--alert-error', role: 'presentation' },
     64                !slugProvided && createElement('div', {
     65                    ...blockProps,
     66                    className: blockProps.className + ' c7-message c7-message--alert-error',
     67                    role: 'presentation'
     68                },
    7469                    createElement('svg', {
    7570                        'aria-hidden': 'true',
     
    9388                )
    9489            ];
    95         },
    96         save: function( props ) {
     90        },
     91        save: function( props ) {
     92            const blockProps = useBlockProps.save();
    9793            return (
    98                 createElement( 'div', {
    99                     className: props.className
    100                     },
     94                createElement( 'div', blockProps,
    10195                    createElement( 'div', {
    10296                        className: 'c7-product-collection',
     
    106100                )
    107101            );
    108         },
    109     } );
     102        },
     103        // Deprecated version to handle old blocks without proper className
     104        deprecated: [
     105            {
     106                attributes: {
     107                    data: {
     108                        type: 'string',
     109                        source: 'attribute',
     110                        selector: '.c7-product-collection',
     111                        attribute: 'data-collection-slug',
     112                    }
     113                },
     114                save: function( props ) {
     115                    return (
     116                        createElement( 'div', { className: props.className },
     117                            createElement( 'div', {
     118                                className: 'c7-product-collection',
     119                                'data-collection-slug': props.attributes.data,
     120                                }
     121                            )
     122                        )
     123                    );
     124                }
     125            }
     126        ]
     127    } );
    110128})(window.wp.blocks, window.wp.blockEditor, window.wp.element, window.wp.i18n, window.wp.components);
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/collectionlist/c7wp-collectionlist.css

    r3313519 r3441297  
    22
    33}
     4
     5.wp-block-c7wp-collectionlist::before {
     6    content: '';
     7    width: 50px;
     8    height: 50px;
     9    max-width: 100%;
     10    max-height: 100%;
     11    position: absolute;
     12    top: 1rem;
     13    right: 1rem;
     14    background-image: url('../../../../assets/c7bg.svg');
     15    background-size: contain;
     16    background-repeat: no-repeat;
     17    background-position: center right;
     18    opacity: 0.08;
     19    pointer-events: none;
     20}
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/collectionlist/c7wp-collectionlist.js

    r3313519 r3441297  
    2828                            'strong',
    2929                            null,
    30                             'This block will automatically populate with list of collections.'
     30                            'This block will automatically populate with list of Web Available collections.'
    3131                        ),
    3232                    )
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/default/c7wp-default.css

    r2592286 r3441297  
    22    overflow: hidden;
    33    z-index: 1;
     4    border: 1px solid currentColor;
     5    padding: 1rem;
    46}
    57.wp-block-c7wp-default > div {
    68    max-width: 100%;
    79}
    8 .wp-block-c7wp-default strong {
     10.wp-block-c7wp-default p {
    911    margin-bottom: 1rem;
    1012    display: block;
    11 }
    12 
    13 .wp-block-c7wp-default label {
    14     display: block;
    15 }
    16 .wp-block-c7wp-default input {
    17     max-width: 100%;
    18     margin-top: 0.5rem;
    19     display: block;
     13    max-width: 55ch;
    2014}
    2115.wp-block-c7wp-default::before {
     
    2620    max-height: 100%;
    2721    position: absolute;
    28     top: 0;
    29     right: 0;
     22    top: 1rem;
     23    right: 1rem;
    3024    background-image: url('../../../../assets/c7bg.svg');
    3125    background-size: contain;
    3226    background-repeat: no-repeat;
    3327    background-position: center right;
    34     opacity: 0.05;
     28    opacity: 0.08;
    3529    pointer-events: none;
    3630    z-index: -1;
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/default/c7wp-default.js

    r3167363 r3441297  
    1 ( function( blocks, editor, element ) {
     1( function( blocks, blockEditor, element ) {
    22
    33    var el = element.createElement;
     4    var useBlockProps = blockEditor.useBlockProps;
    45
    56    const { __ } = wp.i18n;
    67    const iconEl = el('svg', { preserveAspectRatio: 'xMinYMin meet', viewBox: '0 0 28 28' },
    7     el('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" } )
     8        el('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" } )
    89    );
    910
    10     blocks.registerBlockType( 'c7wp/default', {
    11         title: __( 'Default Content' ), // The title of block in editor.
    12         description: __( 'Placeholder for dynamic Commerce7 content on base routes. Required on product, club, collection, reservation, cart, and checkout pages.' ),
    13         icon: iconEl,
    14         category: 'commerce7', // The category of block in editor.
     11    blocks.registerBlockType( 'c7wp/default', {
     12        title: __( 'Default Content', 'wp-commerce7' ),
     13        description: __( 'Placeholder for dynamic Commerce7 content on base routes. Required on product, club, collection, reservation, cart, and checkout pages.', 'wp-commerce7' ),
     14        icon: iconEl,
     15        category: 'commerce7',
    1516        keywords: [ 'commerce7', 'default' ],
    1617        example: {},
    17         attributes: {},
    18         edit: function( props ) {
     18        attributes: {},
     19        supports: {
     20            html: false,
     21            customClassName: false,
     22        },
     23        edit: function( props ) {
     24            var blockProps = useBlockProps();
    1925
    20             return (
    21                 el( 'div', {
    22                     className: 'components-placeholder ' + props.className
    23                     },
    24                     el( 'div', {
    25                         id: 'c7-content'
    26                         },
    27                         el(
    28                             'strong',
    29                             null,
    30                             'Default Content. This block will automatically populate with content on a default route.'
    31                         ),
     26            return el( 'div', blockProps,
     27                el( 'div', {
     28                    id: 'c7-content'
     29                },
     30                    el( 'h2', null,
     31                        __( 'Default dynamic content for Commerce7.', 'wp-commerce7' )
     32                    ),
     33                    el( 'p', null,
     34                        __( 'This block will automatically populate with content on a default route such as product, club, collection, reservation, cart, or checkout.', 'wp-commerce7' )
    3235                    )
    3336                )
    3437            );
    35         },
    36         save: function( props ) {
    37             return (
     38        },
     39        save: function( props ) {
     40            var blockProps = useBlockProps.save();
     41
     42            return el( 'div', blockProps,
    3843                el( 'div', {
    39                     className: props.className
     44                    id: 'c7-content'
     45                })
     46            );
     47        },
     48        // Deprecated version to handle old blocks without proper className
     49        deprecated: [
     50            {
     51                attributes: {},
     52                save: function( props ) {
     53                    // Old save function that didn't use useBlockProps
     54                    return el( 'div', {
     55                        className: props.className
    4056                    },
    41                     el( 'div', {
    42                         id: 'c7-content'
    43                         }
    44                     )
    45                 )
    46             );
    47         },
    48     } );
    49 } )( window.wp.blocks, window.wp.editor, window.wp.element );
     57                        el( 'div', {
     58                            id: 'c7-content'
     59                        })
     60                    );
     61                }
     62            }
     63        ]
     64    } );
     65} )( window.wp.blocks, window.wp.blockEditor, window.wp.element );
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/form/c7wp-form.css

    r3009702 r3441297  
    1212    pointer-events: none;
    1313}
     14
     15
     16
     17.c7wp-justify-center {
     18    .c7-custom-form {
     19        .c7-form {
     20            margin-left: auto;
     21      margin-right: auto;
     22        }
     23        .c7-custom-form {
     24            justify-content: center;
     25        }
     26    }
     27}
     28
     29.c7wp-justify-right {
     30    .c7-custom-form {
     31        .c7-form {
     32            margin-right: 0;
     33      margin-left: auto;
     34        }
     35        .c7-custom-form {
     36            justify-content: flex-end;
     37        }
     38    }
     39}
     40
     41.c7wp-justify-left {
     42    .c7-custom-form {
     43    margin-left: 0;
     44        .c7-form {
     45      margin-right: auto;
     46      margin-left: 0;
     47        }
     48    }
     49}
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/form/c7wp-form.js

    r3343231 r3441297  
    33    const { registerBlockType } = blocks;
    44    const { InspectorControls } = blockEditor;
    5     const { TextControl, PanelBody } = components;
     5    const { TextControl, PanelBody, SelectControl } = components;
    66    const { createElement } = element;
    77    const { __ } = i18n;
    88
    99    const iconEl = createElement('svg', { preserveAspectRatio: 'xMinYMin meet', viewBox: '0 0 28 28' },
    10     createElement('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" } )
     10        createElement('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" })
    1111    );
    1212
    13     registerBlockType( 'c7wp/form', {
    14         title: __( 'Custom Form' ), // The title of block in editor.
    15         description: __( 'Displays a Commerce7 custom form. These can be built in the Website tab of the Commerce7 CRM.' ),
    16         icon: iconEl,
     13    registerBlockType('c7wp/form', {
     14        title: __('Custom Form'), // The title of block in editor.
     15        description: __('Displays a Commerce7 custom form. These can be built in the Website tab of the Commerce7 CRM.'),
     16        icon: iconEl,
    1717        category: 'commerce7', // The category of block in editor.
    18         keywords: [ 'commerce7', 'form', 'custom', 'custom form','contact' ],
     18        keywords: ['commerce7', 'form', 'custom', 'custom form', 'contact'],
    1919        example: {},
    20         attributes: {
     20        attributes: {
    2121            data: {
    2222                type: 'string',
     
    2424                selector: '.c7-custom-form',
    2525                attribute: 'data-form-code',
     26            },
     27            justifyContent: {
     28                type: 'string',
     29                default: 'center',
    2630            }
    2731        },
    28         edit: function( props ) {
     32        edit: function (props) {
    2933
    3034            function updateData(value) {
    3135                props.setAttributes({ data: value });
     36            }
     37
     38            function updateJustifyContent(value) {
     39                props.setAttributes({ justifyContent: value });
     40            }
     41
     42            function getAlignmentClass(justifyContent) {
     43                const alignmentMap = {
     44                    'flex-start': 'c7wp-justify-left',
     45                    'center': 'c7wp-justify-center',
     46                    'flex-end': 'c7wp-justify-right'
     47                };
     48                return alignmentMap[justifyContent] || 'c7wp-justify-center';
    3249            }
    3350
     
    4966                            value: props.attributes.data,
    5067                            onChange: updateData,
     68                        }),
     69                        createElement(SelectControl, {
     70                            label: 'Form Alignment',
     71                            value: props.attributes.justifyContent || 'center',
     72                            options: [
     73                                { label: 'Left', value: 'flex-start' },
     74                                { label: 'Center', value: 'center' },
     75                                { label: 'Right', value: 'flex-end' }
     76                            ],
     77                            onChange: updateJustifyContent,
    5178                        })
    5279                    ),
    5380                ),
    54                 createElement( 'div', {
    55                     className: props.className
    56                     },
    57                     createElement( 'div', {
     81                createElement('div', {
     82                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
     83                },
     84                    createElement('div', {
    5885                        className: 'c7-custom-form',
    5986                        'data-form-code': props.attributes.data,
    60                         },
    61                         slugProvided && createElement( 'form', {
     87                    },
     88                        slugProvided && createElement('form', {
    6289                            className: 'c7-form'
    6390                        },
    64                             createElement( 'fieldset', null,
    65                                 createElement( 'legend', { className: 'c7-sr-only' }, 'Contact Us' ),
    66                                 createElement( 'div', { className: 'c7-form__field' },
    67                                     createElement( 'label', { className: 'c7-required' }, 'First & Last Name' ),
    68                                     createElement( 'input', {
     91                            createElement('fieldset', null,
     92                                createElement('legend', { className: 'c7-sr-only' }, 'Contact Us'),
     93                                createElement('div', { className: 'c7-form__field' },
     94                                    createElement('label', { className: 'c7-required' }, 'First & Last Name'),
     95                                    createElement('input', {
    6996                                        name: 'fullName',
    7097                                        type: 'text',
     
    74101                                    })
    75102                                ),
    76                                 createElement( 'div', { className: 'c7-form__field' },
    77                                     createElement( 'label', { className: 'c7-required' }, 'Country' ),
    78                                     createElement( 'select', {
     103                                createElement('div', { className: 'c7-form__field' },
     104                                    createElement('label', { className: 'c7-required' }, 'Country'),
     105                                    createElement('select', {
    79106                                        name: 'countryCode',
    80107                                        disabled: true,
    81108                                        tabIndex: "-1"
    82109                                    },
    83                                         createElement( 'option', { value: 'CA' }, 'Canada' ),
    84                                         createElement( 'option', { value: 'US' }, 'United States' )
     110                                        createElement('option', { value: 'CA' }, 'Canada'),
     111                                        createElement('option', { value: 'US' }, 'United States')
    85112                                    )
    86113                                ),
    87                                 createElement( 'div', { className: 'c7-form__field' },
    88                                     createElement( 'label', { className: '' }, 'Phone' ),
    89                                     createElement( 'input', {
     114                                createElement('div', { className: 'c7-form__field' },
     115                                    createElement('label', { className: '' }, 'Phone'),
     116                                    createElement('input', {
    90117                                        name: 'phone',
    91118                                        type: 'tel',
     
    95122                                    })
    96123                                ),
    97                                 createElement( 'div', { className: 'c7-form__field' },
    98                                     createElement( 'label', { className: 'c7-required' }, 'Email' ),
    99                                     createElement( 'input', {
     124                                createElement('div', { className: 'c7-form__field' },
     125                                    createElement('label', { className: 'c7-required' }, 'Email'),
     126                                    createElement('input', {
    100127                                        name: 'email',
    101128                                        type: 'email',
     
    105132                                    })
    106133                                ),
    107                                 createElement( 'div', { className: 'c7-form__field' },
    108                                     createElement( 'label', { className: 'c7-required' }, 'Questions/Comments' ),
    109                                     createElement( 'textarea', {
     134                                createElement('div', { className: 'c7-form__field' },
     135                                    createElement('label', { className: 'c7-required' }, 'Questions/Comments'),
     136                                    createElement('textarea', {
    110137                                        name: 'questions-comments',
    111138                                        rows: '3',
     
    115142                                )
    116143                            ),
    117                             createElement( 'div', { className: 'c7-form__buttons' },
    118                                 createElement( 'button', {
    119                                     type: 'submit', 
    120                                     className: 'c7-btn c7-btn--primary' ,
     144                            createElement('div', { className: 'c7-form__buttons' },
     145                                createElement('button', {
     146                                    type: 'submit',
     147                                    className: 'c7-btn c7-btn--primary',
    121148                                    disabled: true,
    122149                                    tabIndex: "-1"
    123                                 }, createElement( 'span', null, 'Submit' ))
     150                                }, createElement('span', null, 'Submit'))
    124151                            )
    125152                        ),
     
    143170                                createElement('line', { x1: '12', y1: '16', x2: '12.01', y2: '16' })
    144171                            ),
    145                                 'Please enter a form slug in the block settings (generally in your right sidebar)'
     172                            'Please enter a form slug in the block settings (generally in your right sidebar)'
    146173                        )
    147174                    )
    148175                )
    149176            ];
    150         },
    151         save: function( props ) {
     177        },
     178        deprecated: [
     179            {
     180                attributes: {
     181                    data: {
     182                        type: 'string',
     183                        source: 'attribute',
     184                        selector: '.c7-custom-form',
     185                        attribute: 'data-form-code',
     186                    }
     187                },
     188                save: function (props) {
     189                    return (
     190                        createElement('div', {
     191                            className: props.className
     192                        },
     193                            createElement('div', {
     194                                className: 'c7-custom-form',
     195                                'data-form-code': props.attributes.data,
     196                            })
     197                        )
     198                    );
     199                }
     200            }
     201        ],
     202        save: function (props) {
     203            function getAlignmentClass(justifyContent) {
     204                const alignmentMap = {
     205                    'flex-start': 'c7wp-justify-left',
     206                    'center': 'c7wp-justify-center',
     207                    'flex-end': 'c7wp-justify-right'
     208                };
     209                return alignmentMap[justifyContent] || 'c7wp-justify-center';
     210            }
     211
    152212            return (
    153                 createElement( 'div', {
    154                     className: props.className
    155                     },
    156                     createElement( 'div', {
     213                createElement('div', {
     214                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
     215                },
     216                    createElement('div', {
    157217                        className: 'c7-custom-form',
    158218                        'data-form-code': props.attributes.data,
    159                         }
     219                    }
    160220                    )
    161221                )
    162222            );
    163         },
    164     } );
     223        },
     224    });
    165225})(window.wp.blocks, window.wp.blockEditor, window.wp.element, window.wp.i18n, window.wp.components);
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/joinnow/c7wp-joinnow.css

    r3009702 r3441297  
    1212    pointer-events: none;
    1313}
     14
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/loginform/c7wp-loginform.css

    r3009702 r3441297  
    99    pointer-events: none;
    1010}
     11
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/personalization/c7wp-personalization.css

    r3009702 r3441297  
    22    overflow: hidden;
    33    z-index: 1;
     4    border: 1px solid currentColor;
     5    padding: 1rem;
    46}
    57.wp-block-c7wp-personalization > div {
     
    1416    max-height: 100%;
    1517    position: absolute;
    16     top: 0;
    17     right: 0;
     18    top: 1rem;
     19    right: 1rem;
    1820    background-image: url('../../../../assets/c7bg.svg');
    1921    background-size: contain;
    2022    background-repeat: no-repeat;
    2123    background-position: center right;
    22     opacity: 0.05;
     24    opacity: 0.08;
    2325    pointer-events: none;
    2426    z-index: -1;
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/personalization/c7wp-personalization.js

    r3343231 r3441297  
    22
    33    const { registerBlockType } = blocks;
    4     const { InspectorControls } = blockEditor;
     4    const { InspectorControls, useBlockProps } = blockEditor; // ADD useBlockProps here
    55    const { TextControl, PanelBody } = components;
    66    const { createElement } = element;
     
    1111    );
    1212
    13     registerBlockType( 'c7wp/personalization', {
    14         title: __( 'Personalization Block' ), // The title of block in editor.
    15         description: __( 'Displays a Commerce7 personalization block. These are created in the Developer section inside Commerce7 and can be edited in the Website tab.' ),
    16         icon: iconEl,
    17         category: 'commerce7', // The category of block in editor.
     13    registerBlockType( 'c7wp/personalization', {
     14        title: __( 'Personalization Block', 'wp-commerce7' ),
     15        description: __( 'Displays a Commerce7 personalization block. These are created in the Developer section inside Commerce7 and can be edited in the Website tab.', 'wp-commerce7' ),
     16        icon: iconEl,
     17        category: 'commerce7',
    1818        keywords: [ 'commerce7', 'personalization', 'personal' ],
    1919        example: {},
    20         attributes: {
     20        attributes: {
    2121            data: {
    2222                type: 'string',
     
    2626            }
    2727        },
    28         edit: function( props ) {
     28        supports: {
     29            html: false,
     30            customClassName: false,
     31        },
     32        edit: function( props ) {
     33            const blockProps = useBlockProps(); // Move inside edit function
    2934
    3035            function updateData(value) {
     
    4449                        createElement('small',
    4550                            null,
    46                             'The preview shown in your editor is for example purposes only, it does not reflect the personalization block code you provide nor is it interactive.'
     51                            'The preview shown in your editor is for example purposes only.'
    4752                        )
    4853                    ),
     
    5560                    ),
    5661                ),
    57                 slugProvided && createElement( 'div', {
    58                     className: 'components-placeholder ' + props.className
    59                     },
     62                slugProvided && createElement( 'div', blockProps, // Use blockProps
    6063                    createElement( 'div', {
    6164                        className: 'c7-personalization',
    6265                        'data-block-code': props.attributes.data,
    6366                        },
    64                         // todo: insert sample html here
    65                         createElement( 'div', { style: {
    66                             textAlign: 'center',
    67                          } },
     67                        createElement( 'div', null, // Add style wrapper
    6868                            createElement( 'h2', null, "Commerce7 Personalization" ),
    69                             createElement( 'p', null, props.attributes.data )
     69                            createElement( 'p', null, "Personalization block code: " + props.attributes.data )
    7070                        ),
    7171                    )
    7272                ),
    73                 !slugProvided && createElement('div', { className: 'c7-message c7-message--alert-error', role: 'presentation' },
     73                !slugProvided && createElement('div', {
     74                    ...blockProps, // Spread blockProps
     75                    className: blockProps.className + ' c7-message c7-message--alert-error',
     76                    role: 'presentation'
     77                },
    7478                    createElement('svg', {
    7579                        'aria-hidden': 'true',
     
    9397                )
    9498            ];
    95         },
    96         save: function( props ) {
     99        },
     100        save: function( props ) {
     101            const blockProps = useBlockProps.save(); // Move inside save function
    97102            return (
    98                 createElement( 'div', {
    99                     className: props.className
    100                     },
     103                createElement( 'div', blockProps, // Use blockProps
    101104                    createElement( 'div', {
    102105                        className: 'c7-personalization',
     
    106109                )
    107110            );
    108         },
    109     } );
     111        },
     112        // Deprecated version to handle old blocks without proper className
     113        deprecated: [
     114            {
     115                attributes: {
     116                    data: {
     117                        type: 'string',
     118                        source: 'attribute',
     119                        selector: '.c7-personalization',
     120                        attribute: 'data-block-code',
     121                    }
     122                },
     123                save: function( props ) {
     124                    return (
     125                        createElement( 'div', {
     126                            className: props.className
     127                            },
     128                            createElement( 'div', {
     129                                className: 'c7-personalization',
     130                                'data-block-code': props.attributes.data,
     131                                }
     132                            )
     133                        )
     134                    );
     135                }
     136            }
     137        ]
     138    } );
    110139})(window.wp.blocks, window.wp.blockEditor, window.wp.element, window.wp.i18n, window.wp.components);
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/reservation/c7wp-reservation.css

    r3009702 r3441297  
    1212    pointer-events: none;
    1313}
     14
     15
     16.c7wp-justify-center {
     17    .c7-reservation__search {
     18        .c7-form__group {
     19            justify-content: center;
     20        }
     21    }
     22}
     23
     24.c7wp-justify-right {
     25    .c7-reservation__search {
     26        margin-right: 0;
     27        .c7-form__group {
     28            justify-content: flex-end;
     29        }
     30    }
     31}
     32
     33.c7wp-justify-left {
     34    .c7-reservation__search {
     35        margin-left: 0;
     36        .c7-form__group {
     37            justify-content: flex-start;
     38        }
     39    }
     40}
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/reservation/c7wp-reservation.js

    r3167363 r3441297  
    33    const { registerBlockType } = blocks;
    44    const { InspectorControls } = blockEditor;
    5     const { TextControl, PanelBody } = components;
     5    const { TextControl, PanelBody, SelectControl } = components;
    66    const { createElement } = element;
    77    const { __ } = i18n;
     
    2424                selector: '.c7-reservation-availability',
    2525                attribute: 'data-reservation-type-slug',
     26            },
     27            justifyContent: {
     28                type: 'string',
     29                default: 'center',
    2630            }
    2731        },
     
    3034            function updateData(value) {
    3135                props.setAttributes({ data: value });
     36            }
     37
     38            function updateJustifyContent(value) {
     39                props.setAttributes({ justifyContent: value });
     40            }
     41
     42            function getAlignmentClass(justifyContent) {
     43                const alignmentMap = {
     44                    'flex-start': 'c7wp-justify-left',
     45                    'center': 'c7wp-justify-center',
     46                    'flex-end': 'c7wp-justify-right'
     47                };
     48                return alignmentMap[justifyContent] || 'c7wp-justify-center';
    3249            }
    3350
     
    4966                            value: props.attributes.data,
    5067                            onChange: updateData,
     68                        }),
     69                        createElement(SelectControl, {
     70                            label: 'Form Alignment',
     71                            value: props.attributes.justifyContent || 'center',
     72                            options: [
     73                                { label: 'Left', value: 'flex-start' },
     74                                { label: 'Center', value: 'center' },
     75                                { label: 'Right', value: 'flex-end' }
     76                            ],
     77                            onChange: updateJustifyContent,
    5178                        })
    5279                    ),
    5380                ),
    54                 createElement('div', { className: props.className },
     81                createElement('div', {
     82                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
     83                },
    5584                    createElement('div', {
    5685                        className: 'c7-reservation-availability',
     
    133162            ];
    134163        },
     164        deprecated: [
     165            {
     166                attributes: {
     167                    data: {
     168                        type: 'string',
     169                        source: 'attribute',
     170                        selector: '.c7-reservation-availability',
     171                        attribute: 'data-reservation-type-slug',
     172                    }
     173                },
     174                save: function (props) {
     175                    return (
     176                        createElement('div', {
     177                            className: props.className
     178                        },
     179                            createElement('div', {
     180                                className: 'c7-reservation-availability',
     181                                'data-reservation-type-slug': props.attributes.data,
     182                            }
     183                            )
     184                        )
     185                    );
     186                }
     187            }
     188        ],
    135189        save: function (props) {
     190            function getAlignmentClass(justifyContent) {
     191                const alignmentMap = {
     192                    'flex-start': 'c7wp-justify-left',
     193                    'center': 'c7wp-justify-center',
     194                    'flex-end': 'c7wp-justify-right'
     195                };
     196                return alignmentMap[justifyContent] || 'c7wp-justify-center';
     197            }
     198
    136199            return (
    137200                createElement('div', {
    138                     className: props.className
     201                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
    139202                },
    140203                    createElement('div', {
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/subscribe/c7wp-subscribe.css

    r3009702 r3441297  
    99    pointer-events: none;
    1010}
     11
     12.c7wp-justify-center {
     13    .c7-subscribe {
     14        .c7-form {
     15            margin-left: auto;
     16      margin-right: auto;
     17        }
     18    }
     19}
     20
     21.c7wp-justify-right {
     22    .c7-subscribe {
     23        .c7-form {
     24            margin-right: 0;
     25      margin-left: auto;
     26        }
     27    }
     28}
     29
     30.c7wp-justify-left {
     31    .c7-subscribe {
     32    margin-left: 0;
     33        .c7-form {
     34      margin-right: auto;
     35      margin-left: 0;
     36        }
     37    }
     38}
  • wp-commerce7/trunk/includes/gutenberg/blocks-v2/subscribe/c7wp-subscribe.js

    r3009702 r3441297  
    33    const { registerBlockType } = blocks;
    44    const { InspectorControls } = blockEditor;
    5     const { PanelBody, RadioControl } = components;
     5    const { PanelBody, RadioControl, SelectControl } = components;
    66    const { createElement } = element;
    77    const { __ } = i18n;
    88
    99    const iconEl = createElement('svg', { preserveAspectRatio: 'xMinYMin meet', viewBox: '0 0 28 28' },
    10     createElement('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" } )
     10        createElement('path', { fill: '#333', d: "M12.7.8c-2.6.3-5 1.3-7 2.9S2.2 7.5 1.5 10c-.8 2.4-.8 5.1-.2 7.5.7 2.5 2.1 4.7 4 6.4.1.1.2.1.3.2h.3c.1 0 .2 0 .3-.1.1-.1.2-.1.2-.2.1-.1.1-.2.1-.3v-.3c0-.1 0-.2-.1-.3-.1-.1-.1-.2-.2-.2-1.5-1.3-2.6-2.9-3.2-4.8s-.7-3.9-.4-5.8c.3-1.9 1.1-3.7 2.3-5.3s2.8-2.7 4.6-3.5c1.8-.8 3.8-1.1 5.7-.9 1.9.2 3.8.9 5.4 2 1.6 1.1 2.9 2.6 3.8 4.4.9 1.8 1.3 3.7 1.2 5.6-.1 2.3-.9 4.5-2.3 6.3-1.3 1.8-3.2 3.3-5.3 4.1-2.1.8-4.3 1-6.5.5l8-15.1c.1-.2.2-.5.2-.8 0-.1 0-.2-.1-.3 0-.1-.1-.2-.2-.3-.1-.1-.2-.1-.3-.2-.1 0-.2-.1-.4 0h-10c-.2 0-.5.1-.7.2 0 .2 0 .3-.1.4 0 .1-.1.2 0 .3 0 .1 0 .2.1.3 0 .2 0 .3.1.4.2.2.4.2.7.2h8.5L9.6 24.7c-.7 1.1-.3 1.7 1.1 2.1 1.9.5 3.8.5 5.7.2 1.9-.4 3.7-1.1 5.3-2.3 1.6-1.1 2.9-2.6 3.8-4.3s1.5-3.6 1.6-5.5c.1-1.9-.1-3.9-.8-5.7-.7-1.8-1.8-3.4-3.1-4.8s-3-2.4-4.9-3C16.5.9 14.6.6 12.7.8z" })
    1111    );
    1212
    13     registerBlockType( 'c7wp/subscribe', {
    14         title: __( 'Subscribe Form' ), // The title of block in editor.
    15         description: __( 'Displays a Commerce7 email subscription form with optional name field.' ),
    16         icon: iconEl,
     13    registerBlockType('c7wp/subscribe', {
     14        title: __('Subscribe Form'), // The title of block in editor.
     15        description: __('Displays a Commerce7 email subscription form with optional name field.'),
     16        icon: iconEl,
    1717        category: 'commerce7', // The category of block in editor.
    18         keywords: [ 'commerce7', 'subscribe', 'personal', 'newsletter' ],
     18        keywords: ['commerce7', 'subscribe', 'personal', 'newsletter'],
    1919        example: {},
    20         attributes: {
     20        attributes: {
    2121            nameFields: {
    2222                type: 'string',
     
    2525                attribute: 'data-has-name-field',
    2626                default: 'false'
     27            },
     28            justifyContent: {
     29                type: 'string',
     30                default: 'center',
    2731            }
    2832        },
    29         edit: function( props ) {
     33        edit: function (props) {
    3034
    31             const updateData = function( value ) {
    32                 return props.setAttributes( {
     35            const updateData = function (value) {
     36                return props.setAttributes({
    3337                    nameFields: value,
    34                 } );
     38                });
     39            }
     40
     41            const updateJustifyContent = function (value) {
     42                return props.setAttributes({
     43                    justifyContent: value,
     44                });
     45            }
     46
     47            function getAlignmentClass(justifyContent) {
     48                const alignmentMap = {
     49                    'flex-start': 'c7wp-justify-left',
     50                    'center': 'c7wp-justify-center',
     51                    'flex-end': 'c7wp-justify-right'
     52                };
     53                return alignmentMap[justifyContent] || 'c7wp-justify-center';
    3554            }
    3655
     
    5271                            }],
    5372                            onChange: updateData,
     73                        }),
     74                        createElement(SelectControl, {
     75                            label: 'Form Alignment',
     76                            value: props.attributes.justifyContent || 'center',
     77                            options: [
     78                                { label: 'Left', value: 'flex-start' },
     79                                { label: 'Center', value: 'center' },
     80                                { label: 'Right', value: 'flex-end' }
     81                            ],
     82                            onChange: updateJustifyContent,
    5483                        })
    5584                    ),
    5685                ),
    57                 createElement( 'div', {
    58                     className: props.className
     86                createElement('div', {
     87                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
     88                },
     89                    createElement('div', {
     90                        className: 'c7-subscribe',
    5991                    },
    60                     createElement( 'div', {
    61                         className: 'c7-subscribe',
     92                        createElement('form', {
     93                            className: 'c7-form'
    6294                        },
    63                         createElement( 'form', {
    64                             className: 'c7-form'
     95                            createElement('div', {
     96                                className: 'c7-form__group'
    6597                            },
    66                             createElement( 'div', {
    67                                 className: 'c7-form__group'
     98                                showNameField && createElement('div', {
     99                                    className: 'c7-form__field'
    68100                                },
    69                                 showNameField && createElement( 'div', {
    70                                     className: 'c7-form__field'
    71                                     },
    72                                     createElement( 'label', { className: 'c7-required' }, 'First & Last Name' ),
    73                                     createElement( 'input', {
     101                                    createElement('label', { className: 'c7-required' }, 'First & Last Name'),
     102                                    createElement('input', {
    74103                                        name: 'fullName',
    75104                                        type: 'text',
    76105                                        tabindex: '-1',
    77106                                        disabled: true,
    78                                         value: '' 
     107                                        value: ''
    79108                                    })
    80109                                ),
    81                                 createElement( 'div', {
     110                                createElement('div', {
    82111                                    className: 'c7-form__field'
    83                                     },
    84                                     createElement( 'label', { className: 'c7-required' }, 'Email' ),
    85                                     createElement( 'input', {
     112                                },
     113                                    createElement('label', { className: 'c7-required' }, 'Email'),
     114                                    createElement('input', {
    86115                                        name: 'email',
    87116                                        type: 'email',
    88117                                        tabindex: '-1',
    89118                                        disabled: true,
    90                                         value: '' 
     119                                        value: ''
    91120                                    })
    92121                                ),
    93                                 createElement( 'button', {
     122                                createElement('button', {
    94123                                    type: 'submit',
    95124                                    tabindex: '-1',
    96125                                    disabled: true,
    97126                                    className: 'c7-btn c7-btn--primary'
    98                                     },
    99                                     createElement( 'span', null, 'Subscribe' )
     127                                },
     128                                    createElement('span', null, 'Subscribe')
    100129                                )
    101130                            )
     
    104133                )
    105134            ];
    106         },
    107         save: function( props ) {
     135        },
     136        deprecated: [
     137            {
     138                attributes: {
     139                    nameFields: {
     140                        type: 'string',
     141                        source: 'attribute',
     142                        selector: '.c7-subscribe',
     143                        attribute: 'data-has-name-field',
     144                        default: 'false'
     145                    }
     146                },
     147                save: function (props) {
     148                    return (
     149                        createElement('div', {
     150                            className: props.className
     151                        },
     152                            createElement('div', {
     153                                className: 'c7-subscribe',
     154                                'data-has-name-field': props.attributes.nameFields,
     155                            })
     156                        )
     157                    );
     158                }
     159            }
     160        ],
     161        save: function (props) {
     162            function getAlignmentClass(justifyContent) {
     163                const alignmentMap = {
     164                    'flex-start': 'c7wp-justify-left',
     165                    'center': 'c7wp-justify-center',
     166                    'flex-end': 'c7wp-justify-right'
     167                };
     168                return alignmentMap[justifyContent] || 'c7wp-justify-center';
     169            }
     170
    108171            return (
    109                 createElement( 'div', {
    110                     className: props.className
    111                     },
    112                     createElement( 'div', {
     172                createElement('div', {
     173                    className: [props.className, getAlignmentClass(props.attributes.justifyContent)].filter(Boolean).join(' ')
     174                },
     175                    createElement('div', {
    113176                        className: 'c7-subscribe',
    114177                        'data-has-name-field': props.attributes.nameFields,
    115                         }
     178                    }
    116179                    )
    117180                )
    118181            );
    119         },
    120     } );
     182        },
     183    });
    121184})(window.wp.blocks, window.wp.blockEditor, window.wp.element, window.wp.i18n, window.wp.components);
  • wp-commerce7/trunk/includes/gutenberg/load.php

    r3343231 r3441297  
    66 * Author: Michael Bourne
    77 * -----
    8  * Last Modified: Thursday, August 7th 2025, 9:42:01 pm
     8 * Last Modified: Friday, December 12th 2025, 1:10:02 pm
    99 * Modified By: Michael Bourne
    1010 * -----
     
    2020// Check if Gutenberg is active.
    2121if ( ! function_exists( 'register_block_type' ) ) {
    22     return;
     22    return;
    2323}
    2424
     25// Load validation helper
     26require_once C7WP_ROOT . '/includes/class-c7wp-validation.php';
     27
    2528if ( in_array( $this->widgetsver, array( 'v2', 'v2-compat' ) ) ) {
    26     $elements = array(
    27         'default',
    28         'personalization',
    29         'buyslug',
    30         'subscribe',
    31         'collection',
    32         'reservation',
    33         'form',
    34         'joinnow',
    35         'loginform',
    36         'clubselector',
    37         'collectionlist',
    38     );
    39     $dir = 'blocks-v2';
     29    $elements = array(
     30        'default',
     31        'personalization',
     32        'buyslug',
     33        'subscribe',
     34        'collection',
     35        'reservation',
     36        'form',
     37        'joinnow',
     38        'loginform',
     39        'clubselector',
     40        'collectionlist',
     41    );
     42    $dir      = 'blocks-v2';
    4043} else {
    41     $elements = array(
    42         'default',
    43         'personalization',
    44         'buy',
    45         'buyslug',
    46         'subscribe',
    47         'collection',
    48         'reservation',
    49         'form',
    50         'joinnow',
    51         'quickshop',
    52         'loginform',
    53         'createaccount',
    54     );
    55     $dir = 'blocks';
     44    $elements = array(
     45        'default',
     46        'personalization',
     47        'buy',
     48        'buyslug',
     49        'subscribe',
     50        'collection',
     51        'reservation',
     52        'form',
     53        'joinnow',
     54        'quickshop',
     55        'loginform',
     56        'createaccount',
     57    );
     58    $dir      = 'blocks';
    5659}
    5760
    5861foreach ( $elements as $element ) {
     62    $block_slug = 'c7wp-' . $element;
    5963
    60     $block_slug = 'c7wp-' . $element;
    61     // Add block script.
    62     wp_register_script(
    63         $block_slug,
    64         plugins_url( $dir . '/' . $element . '/' . $block_slug . '.js', __FILE__ ),
    65         array( 'wp-blocks', 'wp-element' ),
    66         C7WP_VERSION,
    67         1
    68     );
     64    // Check if block.json exists (new format)
     65    $block_json_path = C7WP_ROOT . '/includes/gutenberg/' . $dir . '/' . $element . '/block.json';
     66    if ( file_exists( $block_json_path ) ) {
     67        // Register block using block.json metadata
     68        register_block_type_from_metadata( $block_json_path );
     69    } else {
     70        // Fallback to old registration method
     71        // Add block script.
     72        wp_register_script(
     73            $block_slug,
     74            plugins_url( $dir . '/' . $element . '/' . $block_slug . '.js', __FILE__ ),
     75            array( 'wp-blocks', 'wp-element' ),
     76            C7WP_VERSION,
     77            1
     78        );
    6979
    70     // Add block style.
    71     wp_register_style(
    72         $block_slug,
    73         plugins_url( $dir . '/' . $element . '/' . $block_slug . '.css', __FILE__ ),
    74         array(),
    75         C7WP_VERSION
    76     );
     80        // Add block style.
     81        wp_register_style(
     82            $block_slug,
     83            plugins_url( $dir . '/' . $element . '/' . $block_slug . '.css', __FILE__ ),
     84            array(),
     85            C7WP_VERSION
     86        );
    7787
    78     // Register block script and style.
    79     register_block_type( 'c7wp/' . $element, array(
    80         'editor_style'  => $block_slug, // Loads both on editor.
    81         'editor_script' => $block_slug, // Loads only on editor.
    82     ) );
     88        // Register block script and style.
     89        register_block_type(
     90            'c7wp/' . $element,
     91            array(
     92                'editor_style'  => $block_slug, // Loads both on editor.
     93                'editor_script' => $block_slug, // Loads only on editor.
     94            )
     95        );
     96    }
    8397
    84     // Check for and load frontend assets
    85     $frontend_js_path = $dir . '/' . $element . '/frontend.js';
    86     $frontend_css_path = $dir . '/' . $element . '/frontend.css';
     98    // Check for and load frontend assets
     99    $frontend_js_path = $dir . '/' . $element . '/frontend.js';
     100    $frontend_css_path = $dir . '/' . $element . '/frontend.css';
    87101
    88     // Register and enqueue frontend script if it exists
    89     if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_js_path ) && empty( $_GET['ct_builder'] ) ) {
    90         $frontend_script_handle = 'c7wp-' . $element . '-frontend';
    91         wp_register_script(
    92             $frontend_script_handle,
    93             plugins_url( $frontend_js_path, __FILE__ ),
    94             array(),
    95             C7WP_VERSION,
    96             true
    97         );
     102    // Register and enqueue frontend script if it exists
     103    if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_js_path ) && empty( $_GET['ct_builder'] ) ) {
     104        $frontend_script_handle = 'c7wp-' . $element . '-frontend';
     105        wp_register_script(
     106            $frontend_script_handle,
     107            plugins_url( $frontend_js_path, __FILE__ ),
     108            array(),
     109            C7WP_VERSION,
     110            true
     111        );
    98112
    99         // Localize settings for clubselector
    100         if ( 'clubselector' === $element ) {
    101             $options = get_option( 'c7wp_settings' );
    102             wp_localize_script( $frontend_script_handle, 'c7wp_settings', array(
    103                 'c7wp_frontend_routes' => isset( $options['c7wp_frontend_routes'] ) ? $options['c7wp_frontend_routes'] : array( 'club' => 'club' ),
    104             ) );
    105         }
     113        // Localize settings for clubselector
     114        if ( 'clubselector' === $element ) {
     115            $options = get_option( 'c7wp_settings' );
     116            wp_localize_script(
     117                $frontend_script_handle,
     118                'c7wp_settings',
     119                array(
     120                    'c7wp_frontend_routes' => isset( $options['c7wp_frontend_routes'] ) ? $options['c7wp_frontend_routes'] : array( 'club' => 'club' ),
     121                )
     122            );
     123        }
    106124
    107         wp_enqueue_script( $frontend_script_handle );
    108     }
     125        wp_enqueue_script( $frontend_script_handle );
     126    }
    109127
    110     // Register and enqueue frontend styles if they exist
    111     if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_css_path ) ) {
    112         wp_register_style(
    113             'c7wp-' . $element . '-frontend',
    114             plugins_url( $frontend_css_path, __FILE__ ),
    115             array(),
    116             C7WP_VERSION
    117         );
    118         wp_enqueue_style( 'c7wp-' . $element . '-frontend' );
    119     }
     128    // Register and enqueue frontend styles if they exist
     129    if ( file_exists( C7WP_ROOT . '/includes/gutenberg/' . $frontend_css_path ) ) {
     130        wp_register_style(
     131            'c7wp-' . $element . '-frontend',
     132            plugins_url( $frontend_css_path, __FILE__ ),
     133            array(),
     134            C7WP_VERSION
     135        );
     136        wp_enqueue_style( 'c7wp-' . $element . '-frontend' );
     137    }
    120138}
  • wp-commerce7/trunk/includes/rankmath/load.php

    r2832594 r3441297  
    66 * Author: Michael Bourne
    77 * -----
    8  * Last Modified: Monday, December 12th 2022, 10:19:35 am
     8 * Last Modified: Friday, October 17th 2025, 6:55:09 pm
    99 * Modified By: Michael Bourne
    1010 * -----
     
    1818 */
    1919
    20 add_action( 'wp_head', function( $canonical_url ) {
     20add_action( 'rank_math/frontend/canonical', function( $canonical ) {
    2121
    2222    $options = get_option( 'c7wp_settings' );
     
    3333    // If the current page is a product or collection page, remove action to disable canonical URL.
    3434    if ( is_page( [ $product_route, $collection_route ] ) ) {
    35         remove_all_actions( 'rank_math/head' );
     35        return false;
    3636    }
    3737
    3838}, 1 );
     39
Note: See TracChangeset for help on using the changeset viewer.