array( __( 'Dashboard', 'nexvio' ), 'dashicons-dashboard', 'page_dashboard' ), self::WIDGETS => array( __( 'Widgets', 'nexvio' ), 'dashicons-screenoptions', 'page_widgets' ), self::FILTERS => array( __( 'Filters', 'nexvio' ), 'dashicons-filter', 'page_filters' ), self::CART => array( __( 'Add to Cart', 'nexvio' ), 'dashicons-cart', 'page_cart' ), self::WISHLIST => array( __( 'Wishlist', 'nexvio' ), 'dashicons-heart', 'page_wishlist' ), self::SETTINGS => array( __( 'Settings', 'nexvio' ), 'dashicons-admin-generic', 'page_settings' ), self::DOCS => array( __( 'Documentation', 'nexvio' ), 'dashicons-book-alt', 'page_docs' ), ); } private static function menu_icon() { /* 'none': the logo is drawn by menu_icon_css() so it has the menu colour from the first paint */ return 'none'; } /** The menu logo as a CSS mask: it takes the colour of the menu text (grey, white on hover / active) at once, with no script. */ public static function menu_icon_css() { $svg = ''; $url = 'url("data:image/svg+xml,' . rawurlencode( $svg ) . '")'; $css = '#adminmenu .toplevel_page_' . self::SLUG . ' div.wp-menu-image:before{content:"";display:block;width:20px;height:20px;margin:7px auto 0;padding:0;background-color:currentColor;-webkit-mask:' . $url . ' center/contain no-repeat;mask:' . $url . ' center/contain no-repeat}'; wp_add_inline_style( 'wp-admin', $css ); } public static function menu() { /* 55.4: just above the WooCommerce menu */ add_menu_page( __( 'Nexvio for Woo', 'nexvio' ), __( 'Nexvio for Woo', 'nexvio' ), 'manage_options', self::SLUG, array( __CLASS__, 'page_dashboard' ), self::menu_icon(), '55.4' ); foreach ( self::pages() as $slug => $p ) { add_submenu_page( self::SLUG, $p[0], $p[0], 'manage_options', $slug, array( __CLASS__, $p[2] ) ); } } private static function is_ours() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification return array_key_exists( $page, self::pages() ) ? $page : ''; } public static function body_class( $classes ) { return self::is_ours() ? $classes . ' nexvio-screen' : $classes; } public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Widgets', 'nexvio' ) . '', '' . esc_html__( 'Docs', 'nexvio' ) . '' ); return $links; } public static function assets() { if ( ! self::is_ours() ) { return; } wp_enqueue_style( 'wp-color-picker' ); if ( self::FILTERS === self::is_ours() ) { wp_enqueue_style( 'nexvio-filter-preview', NEXVIO_URL . 'assets/css/product-filter.css', array(), Nexvio_Plugin::ver( 'assets/css/product-filter.css' ) ); wp_enqueue_script( 'nexvio-filters', NEXVIO_URL . 'admin/assets/filters.js', array( 'jquery', 'wp-color-picker' ), Nexvio_Plugin::ver( 'admin/assets/filters.js' ), true ); wp_localize_script( 'nexvio-filters', 'NEXVIO_FILTERS', self::filters_data() ); } if ( self::CART === self::is_ours() ) { wp_enqueue_media(); wp_enqueue_style( 'nexvio-side-cart-preview', NEXVIO_URL . 'assets/css/side-cart.css', array(), Nexvio_Plugin::ver( 'assets/css/side-cart.css' ) ); } wp_enqueue_style( 'nexvio-admin', NEXVIO_URL . 'admin/assets/admin.css', array( 'dashicons', 'wp-color-picker' ), Nexvio_Plugin::ver( 'admin/assets/admin.css' ) ); wp_enqueue_script( 'nexvio-admin', NEXVIO_URL . 'admin/assets/admin.js', array( 'jquery', 'wp-color-picker' ), Nexvio_Plugin::ver( 'admin/assets/admin.js' ), true ); wp_localize_script( 'nexvio-admin', 'NEXVIO_ADMIN', array( 'ajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'nexvio_admin' ), 'total' => count( Nexvio_Plugin::widgets() ), 'i18n' => array( 'active' => __( 'Active', 'nexvio' ), 'inactive' => __( 'Inactive', 'nexvio' ), 'saved' => __( 'Saved', 'nexvio' ), 'error' => __( 'Could not save. Please reload the page and try again.', 'nexvio' ), 'enabled' => __( 'enabled', 'nexvio' ), 'disabled' => __( 'disabled', 'nexvio' ), /* translators: 1: number of enabled widgets, 2: total number of widgets */ 'of' => __( '%1$s of %2$s enabled', 'nexvio' ), ), ) ); } /* ------------------------------------------------------------------ * App layout * ------------------------------------------------------------------ */ private static function open( $current, $title, $subtitle = '' ) { $missing = Nexvio_Plugin::instance()->missing_requirements(); ?>


count( $keys ), 'active' => count( $keys ) - $off, 'inactive' => $off ); } /* ------------------------------------------------------------------ * Dashboard (overview) * ------------------------------------------------------------------ */ public static function page_dashboard() { if ( ! current_user_can( 'manage_options' ) ) { return; } $c = self::counts(); $wl_page = (int) get_option( 'nexvio_wishlist_page', 0 ); $wl_ok = $wl_page && 'publish' === get_post_status( $wl_page ); $missing = Nexvio_Plugin::instance()->missing_requirements(); $steps = array( array( empty( $missing ), __( 'Elementor and WooCommerce are active', 'nexvio' ), admin_url( 'plugins.php' ), __( 'Plugins', 'nexvio' ) ), array( $c['active'] > 0, __( 'Choose the widgets you need', 'nexvio' ), admin_url( 'admin.php?page=' . self::WIDGETS ), __( 'Widgets', 'nexvio' ) ), array( $wl_ok, __( 'Set the wishlist page', 'nexvio' ), admin_url( 'admin.php?page=' . self::WISHLIST ), __( 'Wishlist', 'nexvio' ) ), array( null, __( 'Add the widgets to your Elementor templates', 'nexvio' ), admin_url( 'admin.php?page=' . self::DOCS . '#setup' ), __( 'Guide', 'nexvio' ) ), ); self::open( self::SLUG, __( 'Dashboard', 'nexvio' ), __( 'Everything you need to build your WooCommerce shop with Elementor.', 'nexvio' ) ); ?>

    $st ) : ?>
  1. ' : esc_html( $i + 1 ); ?>

$w ) : $on = ! in_array( $key, $disabled, true ); $search = strtolower( $w['title'] . ' ' . $w['desc'] . ' ' . $w['group'] ); ?>

' . esc_html__( 'Settings saved.', 'nexvio' ) . ''; } ?>

[nexvio_related_products limit="8" layout="slider"]
[nexvio_wishlist_count]
' . esc_html__( 'Settings saved.', 'nexvio' ) . ''; } ?>

'nexvio_wishlist_page', 'id' => 'nexvio_wishlist_page', 'selected' => absint( $page ), 'show_option_none' => esc_html__( '— Select a page —', 'nexvio' ), 'option_none_value' => 0, 'echo' => 0, ) ); echo wp_kses( (string) $nexvio_dropdown, array( 'select' => array( 'name' => true, 'id' => true, 'class' => true ), 'option' => array( 'value' => true, 'selected' => true, 'class' => true ), ) ); ?>

$t ) { $types[ $k ] = array( 'label' => $t[0], 'icon' => $t[1], 'single' => $t[2], 'looks' => $t[3], 'desc' => $t[4] ); } $attrs = array(); foreach ( Nexvio_Filter_Config::attributes() as $tax => $a ) { $terms = taxonomy_exists( $tax ) ? get_terms( array( 'taxonomy' => $tax, 'hide_empty' => false, 'number' => 8 ) ) : array(); $vals = array(); foreach ( is_wp_error( $terms ) ? array() : (array) $terms as $t ) { $vals[] = array( 'name' => $t->name, 'color' => class_exists( 'Nexvio_Swatches' ) ? Nexvio_Swatches::color_for( $t->name, (int) $t->term_id ) : '', 'image' => class_exists( 'Nexvio_Swatches' ) ? Nexvio_Swatches::image_for( (int) $t->term_id ) : '', 'count' => (int) $t->count, ); } $attrs[] = array( 'taxonomy' => $tax, 'label' => $a[0], 'guess' => $a[1], 'terms' => $vals ); } $cats = array(); $terms = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => false, 'parent' => 0, 'number' => 6 ) ); foreach ( is_wp_error( $terms ) ? array() : (array) $terms as $t ) { $kids = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => false, 'parent' => (int) $t->term_id, 'number' => 4 ) ); $cats[] = array( 'name' => $t->name, 'count' => (int) $t->count, 'children' => is_wp_error( $kids ) ? array() : array_map( function ( $k ) { return array( 'name' => $k->name, 'count' => (int) $k->count ); }, (array) $kids ) ); } $tags = get_terms( array( 'taxonomy' => 'product_tag', 'hide_empty' => false, 'number' => 6 ) ); return array( 'fields' => Nexvio_Filter_Config::fields(), 'types' => $types, 'attributes' => $attrs, 'categories' => $cats, 'tags' => is_wp_error( $tags ) ? array() : wp_list_pluck( (array) $tags, 'name' ), 'currency' => function_exists( 'get_woocommerce_currency_symbol' ) ? html_entity_decode( get_woocommerce_currency_symbol() ) : '€', 'attrUrl' => admin_url( 'edit.php?post_type=product&page=product_attributes' ), 'i18n' => array( 'look' => __( 'How it looks', 'nexvio' ), 'name' => __( 'Name shown above the field', 'nexvio' ), 'up' => __( 'Move up', 'nexvio' ), 'down' => __( 'Move down', 'nexvio' ), 'remove' => __( 'Remove', 'nexvio' ), 'more' => __( 'Options', 'nexvio' ), 'open' => __( 'Open when the page loads', 'nexvio' ), 'counts' => __( 'Show the number of products', 'nexvio' ), 'hideEmpty' => __( 'Hide values without products', 'nexvio' ), 'collapse' => __( 'Sub-categories hidden until + is clicked', 'nexvio' ), 'all' => __( 'Show "All categories"', 'nexvio' ), 'limit' => __( '"Show more" after', 'nexvio' ), 'placeholder'=> __( 'Text inside the search box', 'nexvio' ), 'text' => __( 'Checkbox text', 'nexvio' ), 'logic' => __( 'With several values chosen, show products that have', 'nexvio' ), 'any' => __( 'any of them', 'nexvio' ), 'every' => __( 'all of them', 'nexvio' ), 'showOut' => __( 'Also offer "Sold out"', 'nexvio' ), 'added' => __( 'Already added', 'nexvio' ), 'attrs' => __( 'Attributes', 'nexvio' ), 'other' => __( 'Other fields', 'nexvio' ), 'noAttrs' => __( 'No product attributes yet. Create Colour, Size… in Products > Attributes.', 'nexvio' ), 'empty' => __( 'No fields yet. Click "Add a filter field" to start.', 'nexvio' ), 'colour' => __( 'Colour', 'nexvio' ), 'size' => __( 'Size', 'nexvio' ), 'searchPh' => __( 'Search products…', 'nexvio' ), 'allCats' => __( 'All categories', 'nexvio' ), 'inStock' => __( 'In stock', 'nexvio' ), 'preorder' => __( 'Pre-order', 'nexvio' ), 'soldOut' => __( 'Sold out', 'nexvio' ), 'onSale' => __( 'On sale', 'nexvio' ), 'featured' => __( 'Featured', 'nexvio' ), /* translators: %d: number of stars */ 'starsUp' => __( '%d stars & up', 'nexvio' ), 'items' => __( 'items', 'nexvio' ), 'perValues' => __( 'Numbers to offer (comma separated)', 'nexvio' ), 'defaultTxt' => __( 'Default', 'nexvio' ), 'sorts' => array( __( 'Default sorting', 'nexvio' ), __( 'Popularity', 'nexvio' ), __( 'Average rating', 'nexvio' ), __( 'Newest', 'nexvio' ), __( 'Price: low to high', 'nexvio' ), __( 'Price: high to low', 'nexvio' ) ), 'image' => __( 'Image', 'nexvio' ), 'noAttrsHelp'=> __( 'Only global attributes can be filtered: create them in Products > Attributes (Colour, Size…), add their values there, then choose them on your products.', 'nexvio' ), 'newAttr' => __( 'Create an attribute', 'nexvio' ), ), ); } public static function page_filters() { if ( ! current_user_can( 'manage_options' ) ) { return; } self::open( self::FILTERS, __( 'Filters', 'nexvio' ), __( 'Choose the fields of the Product Filter widget: what to filter by, how each field looks and in which order.', 'nexvio' ) ); if ( isset( $_GET['updated'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification echo '
' . esc_html__( 'Filters saved. Every Product Filter widget now uses them.', 'nexvio' ) . '
'; } ?>
  1. 1
  2. 2
  3. 3

array( 'light' => __( 'Light', 'nexvio' ), 'dark' => __( 'Dark', 'nexvio' ) ) ) ), $fst['preset'], 'nexvio_filter_style' ); self::cart_field( 'main_color', array( 'color', __( 'Main colour', 'nexvio' ), '#3d8bff', array( 'help' => __( 'Text on it turns black or white automatically so it stays readable.', 'nexvio' ) ) ), $fst['main_color'], 'nexvio_filter_style' ); ?>

Style).', 'nexvio' ); ?>

    Attributes > Configure terms.', 'nexvio' ); ?>
    ' . esc_html__( 'Settings saved.', 'nexvio' ) . ''; } ?>
    1. 1
    2. 2
    3. 3

    $f ) { self::cart_field( $key, $f, $values[ $key ] ); } ?>
    ' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- inline sample image. }; $t = function ( $key ) { return Nexvio_Cart_Settings::text( $key ); }; ?>

    3

    • ' . esc_html( $it[2] ) . ' ' . esc_html( $it[3] ) . '' : esc_html( $it[3] ); ?>
    €250.00
    €250.00
    '; echo 'toggle' === $type || 'checks' === $type || 'pages' === $type || 'icons' === $type ? '' . esc_html( $label ) . '' : ''; if ( ! empty( $extra['help'] ) ) { echo '

    ' . esc_html( $extra['help'] ) . '

    '; } echo '
    '; switch ( $type ) { case 'toggle': echo ''; break; case 'select': echo ''; break; case 'style': echo '
    '; foreach ( $extra['options'] as $k => $l ) { echo ''; } echo '
    '; break; case 'color': echo '
    '; echo ''; echo '
    '; foreach ( array( '#b08d57' => __( 'Gold', 'nexvio' ), '#111111' => __( 'Black', 'nexvio' ), '#0f5132' => __( 'Emerald', 'nexvio' ), '#1e3a8a' => __( 'Navy', 'nexvio' ), '#9f1239' => __( 'Burgundy', 'nexvio' ), '#c2410c' => __( 'Terracotta', 'nexvio' ), '#d8b4a0' => __( 'Blush', 'nexvio' ), '#6d28d9' => __( 'Violet', 'nexvio' ) ) as $hex => $nm ) { echo ''; } echo '
    '; break; case 'number': echo '' . esc_html( isset( $extra['unit'] ) ? $extra['unit'] : '' ) . ''; break; case 'icons': echo '
    '; foreach ( $extra['options'] as $k => $l ) { $custom = (string) Nexvio_Cart_Settings::get( 'fl_icon_url' ); if ( 'custom' === $k ) { $glyph = $custom ? '' : ''; } else { $glyph = Nexvio_Side_Cart::icon( $k ); } echo wp_kses( '', Nexvio_Plugin::allowed_html() ); } echo '
    '; break; case 'media': echo '
    '; echo '
    '; echo '' . ( $value ? '' : '' ) . ''; echo '
    '; echo '
    '; echo ''; echo ''; echo '
    '; echo '

    ' . esc_html__( 'Opens your Media Library. The icon shows in the live preview straight away.', 'nexvio' ) . '

    '; echo '
    '; echo '
    ' . esc_html__( 'Or paste an image address', 'nexvio' ) . ''; echo '
    '; echo '
    '; break; case 'font': echo ''; break; case 'url': echo ''; break; case 'page': $dd = wp_dropdown_pages( array( 'name' => esc_attr( $name ), 'id' => esc_attr( $id ), 'selected' => absint( $value ), 'show_option_none' => esc_html__( 'WooCommerce default', 'nexvio' ), 'option_none_value' => 0, 'echo' => 0, ) ); echo wp_kses( (string) $dd, array( 'select' => array( 'name' => true, 'id' => true, 'class' => true ), 'option' => array( 'value' => true, 'selected' => true, 'class' => true ) ) ); break; case 'pages': $pages = get_pages( array( 'sort_column' => 'post_title' ) ); echo '
    '; foreach ( (array) $pages as $pg ) { echo ''; } echo '
    '; break; case 'checks': foreach ( $extra['options'] as $k => $l ) { echo ''; } break; default: $auto = isset( $extra['auto'] ) ? Nexvio_Texts::get( $extra['auto'] ) : ''; echo ''; if ( $auto ) { echo '' . esc_html__( 'Automatic when empty', 'nexvio' ) . ''; } } echo '
    '; } public static function save_cart_settings() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Not allowed.', 'nexvio' ) ); } check_admin_referer( 'nexvio_save_cart_settings' ); $input = isset( $_POST['nexvio_cart'] ) && is_array( $_POST['nexvio_cart'] ) ? map_deep( wp_unslash( $_POST['nexvio_cart'] ), 'sanitize_text_field' ) : array(); update_option( Nexvio_Cart_Settings::OPTION, Nexvio_Cart_Settings::sanitize( $input ) ); Nexvio_Cart_Settings::flush(); wp_safe_redirect( admin_url( 'admin.php?page=' . self::CART . '&updated=1' ) ); exit; } /* ------------------------------------------------------------------ * Documentation * ------------------------------------------------------------------ */ private static function doc_sections() { $w = admin_url( 'admin.php?page=' . self::WIDGETS ); $s = admin_url( 'admin.php?page=' . self::SETTINGS ); return array( 'setup' => array( 'dashicons-flag', 'Getting started', '

    Nexvio adds WooCommerce widgets to Elementor. They are listed in the Elementor panel under Nexvio, right after the Pro category.

    1. Requirements: WordPress 5.8+, PHP 7.4+, WooCommerce and Elementor (the free version is enough; Elementor Pro is needed only for Theme Builder templates).
    2. Choose your widgets in Widgets. Disabled widgets are removed from the Elementor panel.
    3. Set the wishlist page in Wishlist (click "Create the page"), then edit it with Elementor and add Wishlist Card (or Wishlist Card Layouts).
    4. Build your templates:
      • Header: Header Search + Wishlist Icon
      • Products archive (shop and categories): Product Filter in a sidebar + Products Archive (or Products Archive Layouts)
      • Single product: Add to Cart + Related Products (or Related Products Layouts)
      • Home page: Product Slider
    ' ), 'slider' => array( 'dashicons-images-alt2', 'Product Slider', '

    A carousel of products with the card design (badges, wishlist heart, stock, price, add to cart).

    Design Layout (Content tab, first section): pick one of 12 card designs from preview images: Classic Vault, Minimal Light, Image Overlay, Horizontal, Glass, Neon Outline, Elevated Shop, Compact Tiles, Editorial, Price Tag, Spotlight and Boutique. The same picker is in Products Card Layouts, Products Archive Layouts, Related Products Layouts and Wishlist Card Layouts. The colours, image shape and effects of the chosen design are in Style > Layout design; only the controls that apply to that design are shown. You can also choose an icon or text add-to-cart button and show star ratings.

    Motion (Slider section): Slide works with arrows, swipe and optional autoplay. Continuous infinite scroll moves the products at a steady speed in an endless loop with no visible restart; set the speed per device, the direction and pause on hover. Arrows and drag / swipe still work in this mode.

    Tips: the Horizontal design looks best with 2 or 3 visible cards, Compact Tiles with 5 or 6. Glass is made for colourful or image backgrounds.

    ' ), 'products-card' => array( 'dashicons-index-card', 'Products Card', '

    A grid of simple product cards: the image on top, then the title, description, price and a full-width button. It uses the same product query, columns and pagination as Products Card Layouts, with one plain design and a short Style tab.

    ' ), 'products-grid' => array( 'dashicons-screenoptions', 'Products Card Layouts', '

    Products Card Layouts (the Products Grid widget) is a grid of chosen products on any page, with 12 selectable card designs: latest, featured, on sale, best sellers, categories, tags or product IDs.

    ' ), 'products-archive' => array( 'dashicons-grid-view', 'Products Archive', '

    The shop and product archive widget with the Products Card design: image on top, then title, description, price and button. Put it in your shop / category template.

    ' ), 'grid' => array( 'dashicons-grid-view', 'Products Archive Layouts', '

    Products Archive Layouts is the product grid with 12 selectable card designs for the shop page and the category / tag / attribute archives, with pagination, result count and sorting.

    ' ), 'filter' => array( 'dashicons-filter', 'Product Filter', '

    An AJAX filter: only the product area reloads, the URL updates (shareable, back button works).

    ' ), 'filter-popup' => array( 'dashicons-filter', 'Filter Popup (side panel)', '

    For shops that don\'t want the filter always visible: visitors see a Filters button; a click slides the filter in from the left (or right).

    ' ), 'search' => array( 'dashicons-search', 'Header Search', '

    A search icon for the header. A click opens a search bar (dropdown or full-width). The search filters the shop by product title, and on a category page it searches inside that category. When the page has a Product Filter, results update without reloading. Press Esc to close.

    ' ), 'related-products' => array( 'dashicons-randomize', 'Related Products', '

    Related products for the single product page, with the Products Card design. It uses the same query as Related Products Layouts: other products of the same category, most popular first (or best rated, random, newest).

    ' ), 'related' => array( 'dashicons-randomize', 'Related Products Layouts', '

    Related Products Layouts has 12 selectable card designs. On the product page, it shows other products of the same category, most popular first (or best rated, random, newest).

    ' ), 'cart' => array( 'dashicons-cart', 'Add to Cart', '

    Quantity selector, "Add to cart" button and wishlist heart for the single product page. Simple products are added with AJAX and the mini-cart updates. Stock, minimum / maximum quantity and "sold individually" are respected. Variable and grouped products use the WooCommerce form, styled to match.

    ' ), 'side-cart' => array( 'dashicons-cart', 'Side cart, Header Cart and Floating Cart', '

    A luxury cart panel that slides in from the right. It uses the real WooCommerce cart: prices, variations, stock limits, coupons, shipping and totals are all calculated by WooCommerce.

    ' ), 'wishlist' => array( 'dashicons-heart', 'Wishlist', '

    The wishlist is built in: no other plugin is needed.

    ' ), 'wishlist-card' => array( 'dashicons-star-filled', 'Wishlist Card', '

    The My favorites page widget with the Products Card design. It shows the products the visitor saved in the built-in wishlist, newest first.

    ' ), 'wishlist-layouts' => array( 'dashicons-star-filled', 'Wishlist Card Layouts', '

    The same wishlist page widget (saved products, live removal, empty message) with the 12 selectable card designs of the Product Slider. Pick the design in Content > Design Layout; its colours are in Style > Layout design.

    ' ), 'gallery-hover' => array( 'dashicons-format-gallery', 'Hover gallery image', '

    Products Card, Products Archive, Related Products, Wishlist Card and the three Layouts widgets (Products Archive Layouts, Related Products Layouts, Wishlist Card Layouts) can show another picture of the product when the pointer is over the card. Each widget has its own settings in Content > Hover gallery image.

    ' ), 'desc-rating' => array( 'dashicons-star-half', 'Short description & rating', '

    In every product widget (Slider, Products Card Layouts, Products Archive Layouts, Related Products Layouts, Wishlist Card Layouts): Content > Description & rating. Each widget has its own settings.

    ' ), 'swatches' => array( 'dashicons-art', 'Colour swatches', '

    Every product widget (Slider and the Layouts widgets) can show a dot per colour under the image: Content > Colour swatches > Show colour swatches.

    ' ), 'shortcodes' => array( 'dashicons-shortcode', 'Shortcodes', '
    [nexvio_related_products limit="8" layout="slider" orderby="popularity"]Related products on a product page. Options: layout slider|grid, orderby popularity|rating|rand|date|price, related_by specific|primary|top|all, fill_parent yes|no, title, columns.
    [nexvio_wishlist_count]Live number of products in the wishlist.
    ' ), 'developers' => array( 'dashicons-editor-code', 'Developers', '

    Filter URL parameters: nexvio_q, min_price, max_price, nexvio_tax_{taxonomy}=a,b (+ nexvio_tax_{taxonomy}_all=1), nexvio_stock, nexvio_sale=1, nexvio_featured=1.

    PHP filters: nexvio_product_query_args, nexvio_grid_pagination_args, nexvio_related_query_args, nexvio_elementor_category_after.

    PHP actions: nexvio_wishlist_added, nexvio_wishlist_removed.

    JavaScript events (jQuery, on body): nexvio_wishlist_changed, nexvio_filter_updated.

    ' ), 'faq' => array( 'dashicons-editor-help', 'FAQ', '

    The widgets are not in Elementor. Check that they are enabled in Widgets and that Elementor and WooCommerce are active, then reload the editor.

    The filter reloads the whole page. The products container was not found: use the Products Archive widget, or set your grid\'s selector in the filter settings.

    The wishlist counter shows an old number. A page cache served an old page. The counter refreshes itself after load; also exclude the wishlist page from the cache.

    ' ), ); } public static function page_docs() { if ( ! current_user_can( 'manage_options' ) ) { return; } $sections = self::doc_sections(); self::open( self::DOCS, __( 'Documentation', 'nexvio' ), __( 'How to set up and use every widget.', 'nexvio' ) ); ?>
    $sec ) : ?>

    $key, 'enabled' => $enabled, 'counts' => self::counts() ) ); } public static function ajax_bulk() { check_ajax_referer( 'nexvio_admin', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( null, 403 ); } $mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : ''; update_option( 'nexvio_disabled_widgets', 'disable' === $mode ? array_keys( Nexvio_Plugin::widgets() ) : array() ); wp_send_json_success( array( 'enabled' => 'disable' !== $mode, 'counts' => self::counts() ) ); } public static function save_settings() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Not allowed.', 'nexvio' ) ); } check_admin_referer( 'nexvio_save_settings' ); $p = wp_unslash( $_POST ); $section = isset( $p['nexvio_section'] ) && 'wishlist' === $p['nexvio_section'] ? 'wishlist' : 'general'; if ( 'wishlist' === $section ) { update_option( 'nexvio_wishlist_page', isset( $p['nexvio_wishlist_page'] ) ? absint( $p['nexvio_wishlist_page'] ) : 0 ); update_option( 'nexvio_wishlist_guests', ! empty( $p['nexvio_wishlist_guests'] ) ? 'yes' : 'no' ); update_option( 'nexvio_wishlist_days', max( 1, min( 730, isset( $p['nexvio_wishlist_days'] ) ? absint( $p['nexvio_wishlist_days'] ) : 60 ) ) ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::WISHLIST . '&updated=1' ) ); exit; } update_option( 'nexvio_per_page', min( 200, isset( $p['nexvio_per_page'] ) ? absint( $p['nexvio_per_page'] ) : 0 ) ); update_option( 'nexvio_auto_related', ! empty( $p['nexvio_auto_related'] ) ? 'yes' : 'no' ); update_option( 'nexvio_related_title', isset( $p['nexvio_related_title'] ) ? sanitize_text_field( $p['nexvio_related_title'] ) : '' ); update_option( 'nexvio_related_limit', max( 1, min( 40, isset( $p['nexvio_related_limit'] ) ? absint( $p['nexvio_related_limit'] ) : 8 ) ) ); $orderby = isset( $p['nexvio_related_orderby'] ) ? sanitize_key( $p['nexvio_related_orderby'] ) : 'popularity'; update_option( 'nexvio_related_orderby', in_array( $orderby, array( 'popularity', 'rating', 'rand', 'date' ), true ) ? $orderby : 'popularity' ); update_option( 'nexvio_related_layout', isset( $p['nexvio_related_layout'] ) && 'grid' === $p['nexvio_related_layout'] ? 'grid' : 'slider' ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::SETTINGS . '&updated=1' ) ); exit; } public static function create_wishlist_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Not allowed.', 'nexvio' ) ); } check_admin_referer( 'nexvio_create_wishlist_page' ); $id = wp_insert_post( array( 'post_title' => 'My wishlist', 'post_name' => 'wishlist', 'post_type' => 'page', 'post_status' => 'publish', ) ); if ( $id && ! is_wp_error( $id ) ) { update_option( 'nexvio_wishlist_page', (int) $id ); } wp_safe_redirect( admin_url( 'admin.php?page=' . self::WISHLIST . '&updated=1' ) ); exit; } }