[amber_mega_menu id="' . (int) $post_id . '"]'; } } public static function assets( $hook ) { global $post; if ( ! $post || AMBER_MEGA_MENU_CPT !== get_post_type( $post ) ) { return; } self::enqueue( (int) $post->ID ); } /** Shared by the post editor and the plugin's own settings screens. */ public static function enqueue( $post_id ) { wp_enqueue_script( 'jquery-ui-sortable' ); wp_enqueue_script( 'jquery-ui-draggable' ); wp_enqueue_script( 'jquery-ui-droppable' ); wp_enqueue_media(); wp_enqueue_style( 'smm-admin', AMBER_MEGA_MENU_URL . 'assets/admin.css', array(), AMBER_MEGA_MENU_VERSION ); wp_enqueue_script( 'smm-admin', AMBER_MEGA_MENU_URL . 'assets/admin.js', array( 'jquery', 'jquery-ui-sortable' ), AMBER_MEGA_MENU_VERSION, true ); wp_localize_script( 'smm-admin', 'AMBER_MEGA_MENU_ADMIN', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'smm_preview' ), 'postId' => (int) $post_id, 'i18n' => array( 'confirm' => __( 'You have unsaved changes to the menu structure. Changing the source will discard them. Continue?', 'amber-mega-menu' ), 'loading' => __( 'Loading items…', 'amber-mega-menu' ), 'error' => __( 'Could not load items for this source.', 'amber-mega-menu' ), ), ) ); } /** Live reload of the structure list when the source changes. */ public static function ajax_preview_structure() { check_ajax_referer( 'smm_preview', 'nonce' ); $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; // A brand new menu has no post yet, so fall back to the plugin capability. $allowed = current_user_can( 'manage_options' ) || ( $post_id && current_user_can( 'edit_post', $post_id ) ); if ( ! $allowed ) { wp_send_json_error(); } $settings = Amber_Mega_Menu_Source::get_settings( $post_id ); $settings['source'] = ( isset( $_POST['source'] ) && 'menu' === $_POST['source'] ) ? 'menu' : 'taxonomy'; $settings['taxonomy'] = isset( $_POST['taxonomy'] ) ? sanitize_key( wp_unslash( $_POST['taxonomy'] ) ) : 'product_cat'; $settings['menu_id'] = isset( $_POST['menu_id'] ) ? (int) $_POST['menu_id'] : 0; $tree = Amber_Mega_Menu_Source::merged_tree( $post_id, $settings ); ob_start(); if ( empty( $tree ) ) { echo '

' . esc_html__( 'No items found for the selected source.', 'amber-mega-menu' ) . '

'; } else { self::render_branch( $tree, 1 ); } wp_send_json_success( array( 'html' => ob_get_clean() ) ); } public static function meta_boxes() { add_meta_box( 'smm_settings', __( 'Mega menu settings', 'amber-mega-menu' ), array( __CLASS__, 'box_settings' ), AMBER_MEGA_MENU_CPT, 'side', 'default' ); add_meta_box( 'smm_structure', __( 'Menu structure (drag to reorder / re-nest)', 'amber-mega-menu' ), array( __CLASS__, 'box_structure' ), AMBER_MEGA_MENU_CPT, 'normal', 'high' ); add_meta_box( 'smm_usage', __( 'How to use', 'amber-mega-menu' ), array( __CLASS__, 'box_usage' ), AMBER_MEGA_MENU_CPT, 'side', 'low' ); } public static function box_usage( $post ) { $id = (int) $post->ID; ?>


[amber_mega_menu id=""]

id —
layout columns | sideways
columns 2–6
depth 1–6 | 0
trigger trigger="Catalog"
open 1

[amber_mega_menu id="" trigger="Catalog" layout="sideways"]
[amber_mega_menu id="" layout="columns" columns="4" depth="2"]
[amber_mega_menu id="" open="1" depth="3"]


<?php echo do_shortcode('[amber_mega_menu id=""]'); ?>

ID ); $menus = wp_get_nav_menus(); $taxes = get_taxonomies( array( 'public' => true ), 'objects' ); ?>

ID ); ?>

'; } } /** Render one colour picker with an independent return-to-default action. */ public static function color_control( $s, $key, $label, $default ) { ?> ID ); $defaults = Amber_Mega_Menu_Source::style_defaults(); ?>

ID ); $defaults = Amber_Mega_Menu_Source::style_defaults(); $colors = array( 'accent_color' => __( 'Accent color', 'amber-mega-menu' ), 'panel_bg' => __( 'Panel background', 'amber-mega-menu' ), 'font_color' => __( 'Font color', 'amber-mega-menu' ), ); ?>

$label ) : ?>
ID ); ?>


ID ); $panes = array( array( 'label' => '' !== trim( (string) $settings['tab_label'] ) ? $settings['tab_label'] : __( 'Main menu', 'amber-mega-menu' ), 'tree' => Amber_Mega_Menu_Source::merged_tree( $post->ID, $settings, 0 ), ), ); /** Premium distributions may add further structure panes (one per extra menu tab). */ $panes = apply_filters( 'amber_mega_menu_structure_panes', $panes, $post, $settings ); ?>

1 ) : ?>

$pane ) : ?>

'; foreach ( $nodes as $node ) { $node_mode = ! empty( $node['hidden'] ) ? ( isset( $node['hide_mode'] ) ? $node['hide_mode'] : 'branch' ) : ''; $li_class = 'smm-item'; if ( $node_mode ) { $li_class .= ' is-hidden'; if ( 'branch' === $node_mode ) { $li_class .= ' is-hidden-branch'; } } echo '
  • '; echo '
    '; echo ''; echo '' . esc_html( $node['label'] ) . ''; if ( $node['count'] ) { echo '' . (int) $node['count'] . ''; } echo ''; $mode = ! empty( $node['hidden'] ) ? ( 'self' === ( isset( $node['hide_mode'] ) ? $node['hide_mode'] : 'branch' ) ? 'self' : 'branch' ) : ''; echo ''; echo '
    '; if ( ! empty( $node['children'] ) ) { self::render_branch( $node['children'], $level + 1 ); } else { echo '
      '; } echo '
    1. '; } echo ''; } public static function save( $post_id, $post ) { if ( ! isset( $_POST['smm_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['smm_nonce'] ) ), 'smm_save' ) ) { return; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_post', $post_id ) ) { return; } $in = isset( $_POST['smm'] ) && is_array( $_POST['smm'] ) ? wp_unslash( $_POST['smm'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- every value is sanitised field by field below. $settings = self::sanitize_input( $in, $post_id ); update_post_meta( $post_id, '_smm_settings', $settings ); if ( isset( $_POST['smm_order'] ) && '' !== $_POST['smm_order'] ) { $order = json_decode( sanitize_text_field( wp_unslash( $_POST['smm_order'] ) ), true ); if ( is_array( $order ) ) { update_post_meta( $post_id, '_smm_order', wp_json_encode( self::sanitize_order( $order ) ) ); } } // Additional tabs keep their own manual ordering, one entry per tab index. if ( isset( $_POST['smm_order_tab'] ) && is_array( $_POST['smm_order_tab'] ) ) { $stored = get_post_meta( $post_id, '_smm_order_tabs', true ); if ( is_string( $stored ) && '' !== $stored ) { $stored = json_decode( $stored, true ); } if ( ! is_array( $stored ) ) { $stored = array(); } foreach ( map_deep( wp_unslash( $_POST['smm_order_tab'] ), 'sanitize_text_field' ) as $index => $json ) { if ( '' === $json ) { continue; } $order = json_decode( $json, true ); if ( is_array( $order ) ) { $stored[ (int) $index ] = self::sanitize_order( $order ); } } update_post_meta( $post_id, '_smm_order_tabs', wp_json_encode( $stored ) ); } } /** Sanitise posted settings without saving. */ public static function sanitize_input( $in, $post_id ) { $saved = Amber_Mega_Menu_Source::get_settings( $post_id ); $settings = array( 'source' => in_array( isset( $in['source'] ) ? $in['source'] : '', array( 'menu', 'taxonomy' ), true ) ? $in['source'] : 'taxonomy', 'taxonomy' => isset( $in['taxonomy'] ) ? sanitize_key( $in['taxonomy'] ) : 'product_cat', 'menu_id' => isset( $in['menu_id'] ) ? (int) $in['menu_id'] : 0, 'layout' => ( isset( $in['layout'] ) && 'sideways' === $in['layout'] ) ? 'sideways' : 'columns', 'desktop_display' => ( isset( $in['desktop_display'] ) && 'modal' === $in['desktop_display'] ) ? 'modal' : 'adaptive', 'columns' => isset( $in['columns'] ) ? max( 2, min( 6, (int) $in['columns'] ) ) : 4, 'depth' => isset( $in['depth'] ) ? max( 0, min( 6, (int) $in['depth'] ) ) : 3, 'trigger' => isset( $in['trigger'] ) ? sanitize_text_field( $in['trigger'] ) : '', 'button_bg' => isset( $in['button_bg'] ) && sanitize_hex_color( $in['button_bg'] ) ? sanitize_hex_color( $in['button_bg'] ) : '#0878a8', 'button_bg_hover' => isset( $in['button_bg_hover'] ) && sanitize_hex_color( $in['button_bg_hover'] ) ? sanitize_hex_color( $in['button_bg_hover'] ) : '#06678f', 'button_text' => isset( $in['button_text'] ) && sanitize_hex_color( $in['button_text'] ) ? sanitize_hex_color( $in['button_text'] ) : '#ffffff', 'button_text_hover' => isset( $in['button_text_hover'] ) && sanitize_hex_color( $in['button_text_hover'] ) ? sanitize_hex_color( $in['button_text_hover'] ) : '#ffffff', 'button_border' => isset( $in['button_border'] ) && sanitize_hex_color( $in['button_border'] ) ? sanitize_hex_color( $in['button_border'] ) : '#0878a8', 'button_border_hover' => isset( $in['button_border_hover'] ) && sanitize_hex_color( $in['button_border_hover'] ) ? sanitize_hex_color( $in['button_border_hover'] ) : '#06678f', 'button_border_width' => isset( $in['button_border_width'] ) ? max( 0, min( 12, (int) $in['button_border_width'] ) ) : 1, 'button_radius' => isset( $in['button_radius'] ) ? max( 0, min( 100, (int) $in['button_radius'] ) ) : 4, 'button_font_size' => isset( $in['button_font_size'] ) ? max( 10, min( 40, (int) $in['button_font_size'] ) ) : 15, 'button_font_weight' => isset( $in['button_font_weight'] ) && in_array( (int) $in['button_font_weight'], array( 400, 500, 600, 700, 800, 900 ), true ) ? (int) $in['button_font_weight'] : 700, 'button_padding_y' => isset( $in['button_padding_y'] ) ? max( 0, min( 40, (int) $in['button_padding_y'] ) ) : 11, 'button_padding_x' => isset( $in['button_padding_x'] ) ? max( 0, min( 80, (int) $in['button_padding_x'] ) ) : 18, 'icon_only_breakpoint' => isset( $in['custom_icon_breakpoint'] ) && '' !== trim( $in['custom_icon_breakpoint'] ) ? max( 0, min( 2560, (int) $in['custom_icon_breakpoint'] ) ) : ( isset( $in['icon_only_breakpoint'] ) ? max( 0, min( 9999, (int) $in['icon_only_breakpoint'] ) ) : 0 ), 'show_cart' => empty( $in['show_cart'] ) ? 0 : 1, 'cart_position' => isset( $in['cart_position'] ) && 'before' === $in['cart_position'] ? 'before' : 'after', 'cart_icon' => isset( $in['cart_icon'] ) ? esc_url_raw( $in['cart_icon'] ) : '', 'cart_bubble_bg' => isset( $in['cart_bubble_bg'] ) && sanitize_hex_color( $in['cart_bubble_bg'] ) ? sanitize_hex_color( $in['cart_bubble_bg'] ) : '#f0b429', 'cart_bubble_text' => isset( $in['cart_bubble_text'] ) && sanitize_hex_color( $in['cart_bubble_text'] ) ? sanitize_hex_color( $in['cart_bubble_text'] ) : '#17324d', 'accent_color' => isset( $in['accent_color'] ) && sanitize_hex_color( $in['accent_color'] ) ? sanitize_hex_color( $in['accent_color'] ) : '#0878a8', 'panel_bg' => isset( $in['panel_bg'] ) && sanitize_hex_color( $in['panel_bg'] ) ? sanitize_hex_color( $in['panel_bg'] ) : '#ffffff', 'sideways_left_bg' => isset( $in['sideways_left_bg'] ) && sanitize_hex_color( $in['sideways_left_bg'] ) ? sanitize_hex_color( $in['sideways_left_bg'] ) : '#eef5f8', 'font_color' => isset( $in['font_color'] ) && sanitize_hex_color( $in['font_color'] ) ? sanitize_hex_color( $in['font_color'] ) : '#17324d', 'muted_color' => isset( $in['muted_color'] ) && sanitize_hex_color( $in['muted_color'] ) ? sanitize_hex_color( $in['muted_color'] ) : '#5d7185', 'border_color' => isset( $in['border_color'] ) && sanitize_hex_color( $in['border_color'] ) ? sanitize_hex_color( $in['border_color'] ) : '#dbe6ed', 'highlight_color' => isset( $in['highlight_color'] ) && sanitize_hex_color( $in['highlight_color'] ) ? sanitize_hex_color( $in['highlight_color'] ) : '#ffe08a', 'font_family' => isset( $in['font_family'] ) && in_array( $in['font_family'], array( 'inherit', 'Arial, sans-serif', 'Helvetica, Arial, sans-serif', 'Verdana, sans-serif', 'Georgia, serif', 'Tahoma, sans-serif', 'Trebuchet MS, sans-serif', 'system-ui, sans-serif' ), true ) ? $in['font_family'] : 'inherit', 'font_size' => isset( $in['font_size'] ) ? max( 10, min( 24, (int) $in['font_size'] ) ) : 14, 'parent_font_size' => isset( $in['parent_font_size'] ) ? max( 10, min( 30, (int) $in['parent_font_size'] ) ) : 15, 'category_font_weight' => isset( $in['category_font_weight'] ) && in_array( (int) $in['category_font_weight'], array( 400, 500, 600, 700, 800, 900 ), true ) ? (int) $in['category_font_weight'] : 600, 'panel_radius' => isset( $in['panel_radius'] ) ? max( 0, min( 60, (int) $in['panel_radius'] ) ) : 4, 'control_radius' => isset( $in['control_radius'] ) ? max( 0, min( 60, (int) $in['control_radius'] ) ) : 4, 'image_size' => isset( $in['image_size'] ) ? max( 12, min( 96, (int) $in['image_size'] ) ) : 20, 'image_radius' => isset( $in['image_radius'] ) ? max( 0, min( 48, (int) $in['image_radius'] ) ) : 0, 'show_counts' => empty( $in['show_counts'] ) ? 0 : 1, 'show_images' => empty( $in['show_images'] ) ? 0 : 1, 'hide_empty' => empty( $in['hide_empty'] ) ? 0 : 1, 'width' => isset( $in['width'] ) ? max( 600, min( 1920, (int) $in['width'] ) ) : 1200, 'all_link' => isset( $in['all_link'] ) ? esc_url_raw( $in['all_link'] ) : '', 'all_label' => isset( $in['all_label'] ) ? sanitize_text_field( $in['all_label'] ) : '', 'tab_label' => isset( $in['tab_label'] ) ? sanitize_text_field( $in['tab_label'] ) : '', 'tabs' => self::sanitize_tabs( isset( $in['tabs'] ) ? $in['tabs'] : array() ), 'frontend_text' => self::sanitize_frontend_text( isset( $in['frontend_text'] ) ? $in['frontend_text'] : array(), $saved ), ); /** * Extension modules may add their own sanitised settings. * Only the already-sanitised settings array is passed, never raw input. */ $settings = apply_filters( 'smm_sanitize_settings', $settings, $post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- legacy 'smm_' prefix kept for compatibility between the free and premium distributions. return $settings; } /** * Per-menu overrides for built-in frontend messages. Only known keys are * kept; when nothing is posted the previously saved values are preserved. */ protected static function sanitize_frontend_text( $input, $saved ) { $keys = array( 'panel_title', 'search_label', 'search_placeholder', 'no_results', 'close_label', 'cart_label', 'empty_message', 'show_all', 'show_less' ); if ( ! is_array( $input ) || empty( $input ) ) { return isset( $saved['frontend_text'] ) && is_array( $saved['frontend_text'] ) ? $saved['frontend_text'] : array(); } $out = array(); foreach ( $keys as $key ) { if ( isset( $input[ $key ] ) && '' !== trim( (string) $input[ $key ] ) ) { $out[ $key ] = sanitize_text_field( $input[ $key ] ); } } return $out; } /** Additional menu tabs: keep only rows that point at a real source. */ protected static function sanitize_tabs( $rows ) { $out = array(); foreach ( (array) $rows as $row ) { if ( ! is_array( $row ) ) { continue; } $source = ( isset( $row['source'] ) && 'taxonomy' === $row['source'] ) ? 'taxonomy' : 'menu'; $menu_id = isset( $row['menu_id'] ) ? (int) $row['menu_id'] : 0; $tax = isset( $row['taxonomy'] ) ? sanitize_key( $row['taxonomy'] ) : ''; if ( 'menu' === $source && ! $menu_id ) { continue; } if ( 'taxonomy' === $source && ! $tax ) { continue; } $out[] = array( 'label' => isset( $row['label'] ) ? sanitize_text_field( $row['label'] ) : '', 'source' => $source, 'taxonomy' => $tax ? $tax : 'product_cat', 'menu_id' => $menu_id, ); } return $out; } protected static function sanitize_order( $nodes ) { $out = array(); foreach ( (array) $nodes as $node ) { if ( empty( $node['id'] ) ) { continue; } $out[] = array( 'id' => sanitize_text_field( $node['id'] ), 'label' => isset( $node['label'] ) ? sanitize_text_field( $node['label'] ) : '', 'hidden' => empty( $node['hidden'] ) ? 0 : 1, // 'branch' hides the item and everything under it; 'self' keeps the // children and moves them up one level. Missing value = 'branch'. 'hide_mode' => ( isset( $node['hide_mode'] ) && 'self' === $node['hide_mode'] ) ? 'self' : 'branch', // Source parent at save time; lets the merge tell manual moves apart // from items re-parented in WordPress afterwards. 'np' => isset( $node['np'] ) ? sanitize_text_field( $node['np'] ) : '', 'children' => isset( $node['children'] ) ? self::sanitize_order( $node['children'] ) : array(), ); } return $out; } }