diff --git a/assets/apps/customizer-controls/src/responsive-toggle/ResponsiveToggleComponent.js b/assets/apps/customizer-controls/src/responsive-toggle/ResponsiveToggleComponent.js index 86a9f3da50..4c6c2e7544 100644 --- a/assets/apps/customizer-controls/src/responsive-toggle/ResponsiveToggleComponent.js +++ b/assets/apps/customizer-controls/src/responsive-toggle/ResponsiveToggleComponent.js @@ -15,7 +15,13 @@ const ResponsiveToggleComponent = ({ control }) => { if (e.detail.id !== control.id) { return false; } - setValue(e.detail.value); + const val = e.detail.value; + setValue(val); + control.setting.set( + val && typeof val === 'object' + ? val + : { desktop: false, tablet: false, mobile: false } + ); }); }, []); diff --git a/assets/scss/components/compat/woocommerce/_product.scss b/assets/scss/components/compat/woocommerce/_product.scss index 2fbb46533b..73de1eeab2 100644 --- a/assets/scss/components/compat/woocommerce/_product.scss +++ b/assets/scss/components/compat/woocommerce/_product.scss @@ -84,6 +84,14 @@ margin: 0; } } + + > .ppom-fieldspopup-btn-wrap { + width: 100%; + + button { + width: 100%; + } + } } .group_table { diff --git a/assets/scss/components/main/_extends.scss b/assets/scss/components/main/_extends.scss index c32d1569d1..6813b80a2e 100644 --- a/assets/scss/components/main/_extends.scss +++ b/assets/scss/components/main/_extends.scss @@ -25,6 +25,7 @@ line-height: var(--btnlineheight, 1.6); letter-spacing: var(--btnletterspacing, var(--bodyletterspacing)); text-transform: var(--btntexttransform, none); + box-shadow: var(--primarybtnshadow, none); } %nv-button-primary-hover { diff --git a/composer.lock b/composer.lock index 37857d6c15..72906e45ea 100644 --- a/composer.lock +++ b/composer.lock @@ -8,21 +8,21 @@ "packages": [ { "name": "codeinwp/themeisle-sdk", - "version": "3.3.50", + "version": "3.3.51", "source": { "type": "git", "url": "https://github.com/Codeinwp/themeisle-sdk.git", - "reference": "3c1f8dfc2390e667bbc086c5d660900a7985efa6" + "reference": "bb2a8414b0418b18c68c9ff1df3d7fb10467928d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/3c1f8dfc2390e667bbc086c5d660900a7985efa6", - "reference": "3c1f8dfc2390e667bbc086c5d660900a7985efa6", + "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/bb2a8414b0418b18c68c9ff1df3d7fb10467928d", + "reference": "bb2a8414b0418b18c68c9ff1df3d7fb10467928d", "shasum": "" }, "require-dev": { "codeinwp/phpcs-ruleset": "dev-main", - "yoast/phpunit-polyfills": "^2.0" + "yoast/phpunit-polyfills": "^4.0" }, "type": "library", "notification-url": "https://packagist.org/downloads/", @@ -43,9 +43,9 @@ ], "support": { "issues": "https://github.com/Codeinwp/themeisle-sdk/issues", - "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.50" + "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.51" }, - "time": "2025-11-25T19:36:35+00:00" + "time": "2026-03-30T07:58:49+00:00" }, { "name": "wptt/webfont-loader", diff --git a/inc/admin/dashboard/main.php b/inc/admin/dashboard/main.php index 03b0be0de7..6a11976538 100755 --- a/inc/admin/dashboard/main.php +++ b/inc/admin/dashboard/main.php @@ -56,7 +56,7 @@ public function init() { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'init', array( $this, 'register_settings' ) ); add_action( 'init', array( $this, 'register_about_page' ), 1 ); - + add_action( 'admin_notices', array( $this, 'render_custom_layout_header' ) ); } @@ -999,7 +999,7 @@ private function get_available_modules() { */ public function render_custom_layout_header() { $screen = get_current_screen(); - + if ( ! $screen || ! ( $screen->id === 'edit-neve_custom_layouts' || $screen->id === 'neve_page_neve-custom-layout-upsell' ) ) { return; } @@ -1255,18 +1255,18 @@ public function render_neve_header() { /** * Register the survey. - * + * * @param array $dash_data The dashboard data. - * + * * @return void */ public function register_survey( $dash_data ) { add_filter( 'themeisle-sdk/survey/neve', function( $data, $page_slug ) use ( $dash_data ) { - + $install_days_number = intval( ( time() - get_option( 'neve_install', time() ) ) / DAY_IN_SECONDS ); - + $data = array( 'environmentId' => 'clr0ply35522h8up0bay2de4y', 'attributes' => array( @@ -1288,43 +1288,67 @@ function( $data, $page_slug ) use ( $dash_data ) { return $data; }, 10, - 2 + 2 ); } /** * Get the Black Friday config settings. - * + * * @param array $default Optional. The default values. - * + * * @return array The data. */ public static function get_black_friday_data( $default = array() ) { $config = $default; - // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name. - $message_template = __( 'Our biggest sale of the year: %1$sup to %2$s OFF%3$s on %4$s. Don\'t miss this limited-time offer.', 'neve' ); - $product_label = __( 'Neve', 'neve' ); - $discount = '70%'; + $message = __( 'Custom layouts, WooCommerce tools, starter sites. What you\'ve been doing manually, Neve Pro handles for you. Exclusively for existing Neve users.', 'neve' ); + $cta_label = __( 'Get Neve Pro', 'neve' ); - $plan = apply_filters( 'product_neve_license_plan', 0 ); + $plan = apply_filters( 'product_neve_license_plan', false ); $is_pro = 0 < $plan; - if ( $is_pro ) { - // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name. - $message_template = __( 'Get %1$sup to %2$s off%3$s when you upgrade your %4$s plan or renew early.', 'neve' ); - $product_label = __( 'Neve Pro', 'neve' ); - $discount = '30%'; + $license_status = apply_filters( 'product_neve_license_status', false ); + $has_valid_license = 'valid' === $license_status; + $has_expired_license = 'expired' === $license_status || 'active-expired' === $license_status; + + $neve_pro_product_slug = defined( 'NEVE_PRO_BASEFILE' ) ? basename( dirname( NEVE_PRO_BASEFILE ) ) : ''; + + if ( $has_valid_license ) { + // translators: %s is the discount percentage. + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - up to %s off', 'neve' ), '30%' ); + // translators: %1$s - the discount percentage for the upgrade, %2$s - the discount percentage for the renewal. + $message = sprintf( __( 'Upgrade your Neve Pro plan: %1$s off this week. Already on the plan you need? Renew early and save up to %2$s.', 'neve' ), '30%', '20%' ); + $cta_label = __( 'See your options', 'neve' ); + } elseif ( $has_expired_license ) { + // translators: %s is the discount percentage. + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'neve' ), '50%' ); + // translators: %s is the discount percentage. + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'neve' ), '50%' ); + $message = __( 'Your Neve Pro features are still here, just locked. Renew at a reduced rate this week and pick up right where you left off.', 'neve' ); + $cta_label = __( 'Reactivate now', 'neve' ); + } else { + // translators: %s is the discount percentage. + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'neve' ), '60%' ); + // translators: %s is the discount percentage. + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'neve' ), '60%' ); + // translators: %s - the discount percentage for the upgrade. + $config['title'] = sprintf( __( 'Neve Pro: %s off this week', 'neve' ), '60%' ); + } + + if ( $has_valid_license || $has_expired_license ) { + $config['plugin_meta_targets'] = array( $neve_pro_product_slug ); } - - $product_label = sprintf( '%s', $product_label ); - $url_params = array( + + $url_params = array( 'utm_term' => $is_pro ? 'plan-' . $plan : 'free', 'lkey' => apply_filters( 'product_neve_license_key', false ), + 'expired' => $has_expired_license ? '1' : false, ); - - $config['message'] = sprintf( $message_template, '', $discount, '', $product_label ); - $config['sale_url'] = add_query_arg( + + $config['message'] = $message; + $config['cta_label'] = $cta_label; + $config['sale_url'] = add_query_arg( $url_params, tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/neve-bf', 'bfcm', 'neve' ) ) ); @@ -1334,9 +1358,9 @@ public static function get_black_friday_data( $default = array() ) { /** * Add the Black Friday data. - * + * * @param array $configs An array of configurations. - * + * * @return array The configurations. */ public function add_black_friday_data( $configs ) { diff --git a/inc/core/admin.php b/inc/core/admin.php index 27e97823a1..0aa6cdec41 100644 --- a/inc/core/admin.php +++ b/inc/core/admin.php @@ -770,7 +770,7 @@ public function welcome_notice_content() { $notice_picture .= '
'; $notice_picture .= ''; - $notice_picture .= '30+ ' . __( 'Starter Sites', 'neve' ); + $notice_picture .= '100+ ' . __( 'Starter Sites', 'neve' ); $notice_picture .= ''; $notice_picture .= '
'; $notice_picture .= ''; diff --git a/inc/views/pluggable/pagination.php b/inc/views/pluggable/pagination.php index d75bc8d270..f8eb8de4c0 100644 --- a/inc/views/pluggable/pagination.php +++ b/inc/views/pluggable/pagination.php @@ -135,10 +135,18 @@ public function filter_localization( $data ) { global $wp_query; $max_pages = $wp_query->max_num_pages; + $query = $wp_query->query; + + if ( is_tax() ) { + $post_type = get_post_type(); + if ( $post_type ) { + $query['post_type'] = $post_type; + } + } $data['infScroll'] = 'enabled'; $data['maxPages'] = $max_pages; $data['endpoint'] = rest_url( 'nv/v1/posts/page/' ); - $data['query'] = wp_json_encode( $wp_query->query ); + $data['query'] = wp_json_encode( $query ); $data['lang'] = get_locale(); // WPML language parameter @@ -300,7 +308,7 @@ public function render_post_navigation() { * Sanitize query arguments for infinite scroll to prevent query manipulation. * * This method implements a strict allowlist approach to prevent: - * - Expensive database queries (DoS risk via meta_query, tax_query, etc.) + * - Expensive database queries (DoS risk via meta_query, fields etc.) * - Exposure of unintended content types * - Manipulation of query parameters by anonymous users * @@ -371,6 +379,59 @@ private function sanitize_infinite_scroll_query_args( $args ) { $sanitized['post_type'] = $post_type; } else { $sanitized['post_type'] = 'post'; + $post_type = 'post'; + } + + // Build an allowlist of taxonomies that are both publicly queryable and registered to the resolved post type. + $allowed_taxonomies = array_filter( + get_object_taxonomies( $post_type, 'objects' ), + function ( $tax_obj ) { + return ! empty( $tax_obj->publicly_queryable ); + } + ); + $allowed_taxonomy_names = array_keys( $allowed_taxonomies ); + + $tax_queries = array(); + foreach ( $args as $key => $value ) { + if ( ! in_array( $key, $allowed_taxonomy_names, true ) ) { + continue; + } + + $terms = array(); + if ( is_array( $value ) ) { + foreach ( $value as $maybe_term ) { + if ( is_scalar( $maybe_term ) ) { + $sanitized_term = sanitize_title( (string) $maybe_term ); + if ( $sanitized_term !== '' ) { + $terms[] = $sanitized_term; + } + } + } + } elseif ( is_scalar( $value ) ) { + $sanitized_term = sanitize_title( (string) $value ); + if ( $sanitized_term !== '' ) { + $terms[] = $sanitized_term; + } + } + + if ( empty( $terms ) ) { + continue; + } + + $tax_queries[] = array( + 'taxonomy' => $key, + 'field' => 'slug', + 'terms' => $terms, + ); + } + + if ( ! empty( $tax_queries ) ) { + $sanitized['tax_query'] = array_merge( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + array( + 'relation' => 'AND', + ), + $tax_queries + ); } // Explicitly unset dangerous query args that could be smuggled in. @@ -381,7 +442,6 @@ private function sanitize_infinite_scroll_query_args( $args ) { 'meta_value', 'meta_value_num', 'meta_compare', - 'tax_query', 'fields', 'post__in', 'post__not_in',