diff --git a/adminpages/admin_header.php b/adminpages/admin_header.php index 69375ad..8bf72a1 100644 --- a/adminpages/admin_header.php +++ b/adminpages/admin_header.php @@ -16,6 +16,7 @@ diff --git a/adminpages/menus.php b/adminpages/menus.php new file mode 100644 index 0000000..e77b0a6 --- /dev/null +++ b/adminpages/menus.php @@ -0,0 +1,477 @@ + 0 ) { + $new_menu_id = memberlite_duplicate_menu( $source_menu_id, $new_menu_name ); + } else { + $new_menu_id = wp_create_nav_menu( $new_menu_name ); + } + + if ( $new_menu_id && ! is_wp_error( $new_menu_id ) ) { + // Redirect back to the custom menus page with success message. + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&created=' . $new_menu_id ) ); + exit; + } else { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=create' ) ); + exit; + } + } + + // Check for duplicate action. + $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; + if ( $action === 'duplicate' && isset( $_GET['menu'] ) ) { + // Verify nonce. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'memberlite_duplicate_menu_' . intval( $_GET['menu'] ) ) ) { + wp_die( esc_html__( 'Security check failed.', 'memberlite' ) ); + } + + // Check capabilities. + if ( ! current_user_can( 'edit_theme_options' ) ) { + wp_die( esc_html__( 'You do not have permission to do this.', 'memberlite' ) ); + } + + $menu_id = intval( $_GET['menu'] ); + $source_menu = wp_get_nav_menu_object( $menu_id ); + + if ( $source_menu ) { + /* translators: %s: original menu name */ + $new_menu_name = sprintf( __( '%s (Copy)', 'memberlite' ), $source_menu->name ); + $new_menu_id = memberlite_duplicate_menu( $menu_id, $new_menu_name ); + + if ( $new_menu_id ) { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&duplicated=' . $new_menu_id ) ); + exit; + } + } + + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=duplicate' ) ); + exit; + } + + // Check for delete action. + if ( $action === 'delete' && isset( $_GET['menu'] ) ) { + // Verify nonce. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'memberlite_delete_menu_' . intval( $_GET['menu'] ) ) ) { + wp_die( esc_html__( 'Security check failed.', 'memberlite' ) ); + } + + // Check capabilities. + if ( ! current_user_can( 'edit_theme_options' ) ) { + wp_die( esc_html__( 'You do not have permission to do this.', 'memberlite' ) ); + } + + $menu_id = intval( $_GET['menu'] ); + $result = wp_delete_nav_menu( $menu_id ); + + if ( $result && ! is_wp_error( $result ) ) { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&deleted=1' ) ); + } else { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=delete' ) ); + } + exit; + } +} +add_action( 'admin_init', 'memberlite_process_menu_actions' ); + +/** + * Display admin notices for menu actions on the nav-menus.php page. + * + * @since TBD + */ +function memberlite_menu_admin_notices() { + $screen = get_current_screen(); + if ( ! $screen || $screen->base !== 'nav-menus' ) { + return; + } + + if ( isset( $_GET['memberlite_duplicated'] ) ) { + ?> +
+

+
+ +
+

+
+ $menu_id ) { + if ( $menu_id && isset( $locations[ $location ] ) ) { + if ( ! isset( $menu_to_locations[ $menu_id ] ) ) { + $menu_to_locations[ $menu_id ] = array(); + } + $menu_to_locations[ $menu_id ][] = $locations[ $location ]; + } + } + + // Load the Memberlite dashboard-area header. + require_once __DIR__ . '/admin_header.php'; + ?> + +
+ + ', + esc_html( $created_menu->name ), + '' + ); + $class = 'notice-success'; + } + } elseif ( isset( $_GET['duplicated'] ) ) { + $duplicated_menu_id = intval( $_GET['duplicated'] ); + $duplicated_menu = wp_get_nav_menu_object( $duplicated_menu_id ); + if ( $duplicated_menu ) { + $edit_url = admin_url( 'nav-menus.php?action=edit&menu=' . $duplicated_menu_id ); + $message = sprintf( + /* translators: %1$s: opening link tag, %2$s: menu name, %3$s: closing link tag */ + __( 'Menu duplicated successfully. %1$sEdit "%2$s" menu%3$s', 'memberlite' ), + '', + esc_html( $duplicated_menu->name ), + '' + ); + $class = 'notice-success'; + } + } elseif ( isset( $_GET['deleted'] ) ) { + $message = __( 'Menu deleted successfully.', 'memberlite' ); + $class = 'notice-success'; + } elseif ( isset( $_GET['error'] ) ) { + $error = sanitize_key( wp_unslash( $_GET['error'] ) ); + switch ( $error ) { + case 'duplicate': + $message = __( 'There was an error duplicating the menu.', 'memberlite' ); + break; + case 'delete': + $message = __( 'There was an error deleting the menu.', 'memberlite' ); + break; + case 'empty_name': + $message = __( 'Please enter a valid menu name.', 'memberlite' ); + break; + case 'name_exists': + $message = __( 'A menu with that name already exists. Please choose another name.', 'memberlite' ); + break; + case 'create': + $message = __( 'There was an error creating the menu.', 'memberlite' ); + break; + } + } + + // Output notice if needed. + if ( $message ) { + printf( + '

%2$s

', + esc_attr( $class ), + wp_kses( $message, array( 'a' => array( 'href' => array() ) ) ) + ); + } + ?> + +

+

+ + ' . esc_html__( 'Learn more about menus in Memberlite', 'memberlite' ) . ''; + ?> +

+ +
+
+ +
+
+
+ + + + + + + + + + + +
+ + + +

+
+ + + +

+
+

+ + +

+
+
+
+ +
+
+ +
+
+ +

+ + + + + + + + + + + + term_id ); + $item_count = $item_count ? count( $item_count ) : 0; + $assigned = isset( $menu_to_locations[ $menu->term_id ] ) ? implode( ', ', $menu_to_locations[ $menu->term_id ] ) : '—'; + + $edit_url = admin_url( 'nav-menus.php?action=edit&menu=' . $menu->term_id ); + $duplicate_url = wp_nonce_url( + admin_url( 'admin.php?page=memberlite-custom-menus&action=duplicate&menu=' . $menu->term_id ), + 'memberlite_duplicate_menu_' . $menu->term_id + ); + $delete_url = wp_nonce_url( + admin_url( 'admin.php?page=memberlite-custom-menus&action=delete&menu=' . $menu->term_id ), + 'memberlite_delete_menu_' . $menu->term_id + ); + + $confirm_text = sprintf( + /* translators: %s: menu name */ + esc_js( __( 'Are you sure you want to delete the "%s" menu? This cannot be undone.', 'memberlite' ) ), + esc_js( $menu->name ) + ); + ?> + + + + + + + + +
+ name ); ?> + + name ) ); ?>"> + + + name ) ); ?>"> + + + name ) ); ?>"> + + +
+ +
+
+ + + + $menu_item ) { + $args = array( + 'post_title' => $menu_item->title, + 'post_content' => $menu_item->description, + 'post_excerpt' => $menu_item->attr_title, + 'post_status' => $menu_item->post_status, + 'post_type' => 'nav_menu_item', + 'menu_order' => $index, + 'comment_status' => 'closed', + 'ping_status' => 'closed', + 'tax_input' => array( + 'nav_menu' => array( $new_menu_id ), + ), + ); + + // Copy menu item meta. + $meta_input = array(); + $meta_keys = get_post_meta( $menu_item->db_id ); + + foreach ( $meta_keys as $meta_key => $meta_values ) { + if ( $meta_key === '_menu_item_menu_item_parent' ) { + continue; // Handle parent separately. + } + $meta_value = $meta_values[0]; + if ( is_serialized( $meta_value ) ) { + $meta_value = maybe_unserialize( $meta_value ); + } + $meta_input[ $meta_key ] = $meta_value; + } + + $args['meta_input'] = $meta_input; + + // Insert the new menu item. + $new_item_id = wp_insert_post( $args ); + + // Skip this item if insertion failed. + if ( is_wp_error( $new_item_id ) ) { + continue; + } + + // Store the ID mapping. + $id_map[ $menu_item->db_id ] = $new_item_id; + + // Update parent relationship using the mapped ID. + if ( ! empty( $menu_item->menu_item_parent ) && isset( $id_map[ $menu_item->menu_item_parent ] ) ) { + update_post_meta( $new_item_id, '_menu_item_menu_item_parent', $id_map[ $menu_item->menu_item_parent ] ); + } + } + + return $new_menu_id; +} diff --git a/adminpages/sidebars.php b/adminpages/sidebars.php index fbc30ac..904e3db 100644 --- a/adminpages/sidebars.php +++ b/adminpages/sidebars.php @@ -229,7 +229,7 @@ function memberlite_custom_sidebars() { 'memberlite_delete_custom_sidebar' ); ?> - + "> $template, ); - $options = array(); + // Export theme mods if selected. + if ( ! empty( $_POST['memberlite_export_theme_mods'] ) ) { + // All theme mods = all Customizer settings for the active theme. + $mods = get_theme_mods(); + if ( ! is_array( $mods ) ) { + $mods = array(); + } + $data['mods'] = $mods; + + /** + * Filter the option keys to export when exporting Memberlite theme settings. + * By default, we export the site icon, custom sidebars, and sidebar assignments for custom post types. + * + * Note: This same filter is used for resetting options in memberlite_reset_theme_settings(). + * + * @since 6.1 + * @param array $option_keys Array of option keys to export. + */ + $option_keys = apply_filters( + 'memberlite_export_option_keys', + array( + 'site_icon', + 'memberlite_cpt_sidebars', + 'memberlite_custom_sidebars', + ) + ); + + $options = array(); + + foreach ( $option_keys as $key ) { + $value = get_option( $key, null ); + if ( null !== $value ) { + $options[ $key ] = $value; + } + } - foreach ( $option_keys as $key ) { - $value = get_option( $key, null ); - if ( null !== $value ) { - $options[ $key ] = $value; + $data['options'] = $options; + + if ( function_exists( 'wp_get_custom_css' ) ) { + $data['wp_css'] = wp_get_custom_css(); } } - $data = array( - 'template' => $template, - 'mods' => $mods, - 'options' => $options, - ); + // Export menus if selected. + if ( ! empty( $_POST['memberlite_export_menus'] ) && ! empty( $_POST['memberlite_export_menu_ids'] ) ) { + $menu_ids = array_map( 'intval', $_POST['memberlite_export_menu_ids'] ); + $menus_data = array(); + + // Get current menu location assignments to export with menus. + $menu_locations = get_nav_menu_locations(); + $menu_id_to_locations = array(); + foreach ( $menu_locations as $location => $assigned_menu_id ) { + if ( ! empty( $assigned_menu_id ) ) { + if ( ! isset( $menu_id_to_locations[ $assigned_menu_id ] ) ) { + $menu_id_to_locations[ $assigned_menu_id ] = array(); + } + $menu_id_to_locations[ $assigned_menu_id ][] = $location; + } + } - if ( function_exists( 'wp_get_custom_css' ) ) { - $data['wp_css'] = wp_get_custom_css(); + foreach ( $menu_ids as $menu_id ) { + $menu = wp_get_nav_menu_object( $menu_id ); + if ( ! $menu ) { + continue; + } + + $menu_items = wp_get_nav_menu_items( $menu_id ); + $items_data = array(); + + if ( ! empty( $menu_items ) ) { + // Build index map for parent references. + $id_to_index = array(); + foreach ( $menu_items as $index => $item ) { + $id_to_index[ $item->db_id ] = $index; + } + + foreach ( $menu_items as $index => $item ) { + // Determine parent index (null if top-level). + $parent_index = null; + if ( ! empty( $item->menu_item_parent ) && isset( $id_to_index[ $item->menu_item_parent ] ) ) { + $parent_index = $id_to_index[ $item->menu_item_parent ]; + } + + // Make internal URLs relative for portability across environments. + $item_url = $item->url; + $site_url = home_url(); + if ( strpos( $item_url, $site_url ) === 0 ) { + $item_url = substr( $item_url, strlen( $site_url ) ); + // Ensure empty path becomes root slash. + if ( empty( $item_url ) ) { + $item_url = '/'; + } + } + + $items_data[] = array( + 'label' => $item->title, + 'url' => $item_url, + 'target' => $item->target, + 'attr_title' => $item->attr_title, + 'description' => $item->description, + 'classes' => array_filter( (array) $item->classes ), + 'xfn' => $item->xfn, + 'parent' => $parent_index, + ); + } + } + + $menu_export_data = array( + 'name' => $menu->name, + 'items' => $items_data, + ); + + // Include location assignments for this menu. + if ( isset( $menu_id_to_locations[ $menu_id ] ) ) { + $menu_export_data['locations'] = $menu_id_to_locations[ $menu_id ]; + } + + $menus_data[] = $menu_export_data; + } + + if ( ! empty( $menus_data ) ) { + $data['menus'] = $menus_data; + } } - $json = wp_json_encode( $data ); + $json = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); if ( false === $json ) { wp_die( esc_html__( 'Error encoding export data.', 'memberlite' ) ); @@ -170,6 +261,7 @@ function memberlite_export_theme_settings() { * Handle Memberlite theme settings import. * * @since 6.1 + * @since TBD Added navigation menu import support. */ function memberlite_import_theme_settings() { if ( ! current_user_can( 'edit_theme_options' ) ) { @@ -196,7 +288,8 @@ function memberlite_import_theme_settings() { // Decode JSON created by the export tool. $data = json_decode( $raw, true ); - if ( ! is_array( $data ) || empty( $data['template'] ) || ! isset( $data['mods'] ) ) { + // Validate file structure - must have template and either mods or menus. + if ( ! is_array( $data ) || empty( $data['template'] ) || ( ! isset( $data['mods'] ) && ! isset( $data['menus'] ) ) ) { memberlite_import_settings_redirect( 'invalid_file' ); } @@ -207,7 +300,7 @@ function memberlite_import_theme_settings() { memberlite_import_settings_redirect( 'wrong_theme' ); } - // Overwrite current theme mods. + // Overwrite current theme mods if present. if ( isset( $data['mods'] ) && is_array( $data['mods'] ) ) { // Clear existing mods so we don't leave stale ones behind. remove_theme_mods(); @@ -229,6 +322,148 @@ function memberlite_import_theme_settings() { wp_update_custom_css_post( $data['wp_css'] ); } + // Import navigation menus if present. + if ( ! empty( $data['menus'] ) && is_array( $data['menus'] ) ) { + // Track location assignments to apply after all menus are created. + $location_assignments = array(); + + // Check if user wants to replace existing menus. + $replace_existing = ! empty( $_POST['memberlite_replace_existing_menus'] ); + + foreach ( $data['menus'] as $menu_data ) { + if ( empty( $menu_data['name'] ) ) { + continue; + } + + $menu_name = sanitize_text_field( $menu_data['name'] ); + + // Check if a menu with this exact name already exists. + $existing_menu = wp_get_nav_menu_object( $menu_name ); + + if ( $existing_menu ) { + if ( $replace_existing ) { + // Replace mode: Delete existing menu items and import new ones into existing menu. + $menu_id = $existing_menu->term_id; + $location_menu_id = $menu_id; + + $existing_items = wp_get_nav_menu_items( $menu_id ); + if ( ! empty( $existing_items ) ) { + foreach ( $existing_items as $item ) { + wp_delete_post( $item->db_id, true ); + } + } + } else { + // Non-replace mode: Create duplicate menu, but assign original to locations. + $location_menu_id = $existing_menu->term_id; + + // Create duplicate with numbered name. + $duplicate_name = $menu_name; + $counter = 1; + while ( wp_get_nav_menu_object( $duplicate_name ) ) { + $counter++; + $duplicate_name = $menu_name . ' (' . $counter . ')'; + } + + $menu_id = wp_create_nav_menu( $duplicate_name ); + + if ( is_wp_error( $menu_id ) ) { + continue; + } + } + } else { + // Menu doesn't exist - create it and use it for locations. + $menu_id = wp_create_nav_menu( $menu_name ); + + if ( is_wp_error( $menu_id ) ) { + continue; + } + + $location_menu_id = $menu_id; + } + + // Track location assignments using the appropriate menu ID. + if ( ! empty( $menu_data['locations'] ) && is_array( $menu_data['locations'] ) ) { + foreach ( $menu_data['locations'] as $location ) { + $location_assignments[ sanitize_key( $location ) ] = $location_menu_id; + } + } + + // Import menu items. + if ( ! empty( $menu_data['items'] ) && is_array( $menu_data['items'] ) ) { + // Map of old index to new menu item ID for parent relationships. + $index_to_new_id = array(); + + foreach ( $menu_data['items'] as $index => $item_data ) { + $label = isset( $item_data['label'] ) ? sanitize_text_field( $item_data['label'] ) : ''; + $url = isset( $item_data['url'] ) ? $item_data['url'] : ''; + + // Convert relative URLs to absolute using the current site URL. + if ( ! empty( $url ) && strpos( $url, '/' ) === 0 && strpos( $url, '//' ) !== 0 ) { + $url = home_url( $url ); + } + + $url = esc_url_raw( $url ); + + if ( empty( $label ) || empty( $url ) ) { + continue; + } + + // Determine parent menu item ID. + $parent_id = 0; + if ( isset( $item_data['parent'] ) && is_int( $item_data['parent'] ) && isset( $index_to_new_id[ $item_data['parent'] ] ) ) { + $parent_id = $index_to_new_id[ $item_data['parent'] ]; + } + + // Prepare menu item data. + $menu_item_data = array( + 'menu-item-title' => $label, + 'menu-item-url' => $url, + 'menu-item-status' => 'publish', + 'menu-item-type' => 'custom', + 'menu-item-parent-id' => $parent_id, + ); + + // Add optional fields if present. + if ( ! empty( $item_data['target'] ) ) { + $menu_item_data['menu-item-target'] = sanitize_text_field( $item_data['target'] ); + } + + if ( ! empty( $item_data['attr_title'] ) ) { + $menu_item_data['menu-item-attr-title'] = sanitize_text_field( $item_data['attr_title'] ); + } + + if ( ! empty( $item_data['description'] ) ) { + $menu_item_data['menu-item-description'] = sanitize_textarea_field( $item_data['description'] ); + } + + if ( ! empty( $item_data['classes'] ) && is_array( $item_data['classes'] ) ) { + $menu_item_data['menu-item-classes'] = implode( ' ', array_map( 'sanitize_html_class', $item_data['classes'] ) ); + } + + if ( ! empty( $item_data['xfn'] ) ) { + $menu_item_data['menu-item-xfn'] = sanitize_text_field( $item_data['xfn'] ); + } + + // Insert the menu item. + $new_item_id = wp_update_nav_menu_item( $menu_id, 0, $menu_item_data ); + + if ( ! is_wp_error( $new_item_id ) ) { + $index_to_new_id[ $index ] = $new_item_id; + } + } + } + } + + // Apply menu location assignments after all menus are created. + if ( ! empty( $location_assignments ) ) { + $current_locations = get_nav_menu_locations(); + foreach ( $location_assignments as $location => $menu_id ) { + $current_locations[ $location ] = $menu_id; + } + set_theme_mod( 'nav_menu_locations', $current_locations ); + } + } + memberlite_import_settings_redirect( 'import_ok' ); } add_action( 'admin_post_memberlite_import_theme_settings', 'memberlite_import_theme_settings' ); diff --git a/adminpages/tools/export-settings.php b/adminpages/tools/export-settings.php index e32bbbc..d9d2b9e 100644 --- a/adminpages/tools/export-settings.php +++ b/adminpages/tools/export-settings.php @@ -8,6 +8,8 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } + +$menus = wp_get_nav_menus(); ?>
@@ -18,11 +20,110 @@

-

- - - -

+
+ + + + + + + + + + + + + +
+
+ +

+
+
+ +

+ +
+ +

+
+ 5 ) { + $classes[] = 'memberlite_scrollable'; + } + $class = implode( ' ', $classes ); + ?> + + + +
+

+ +

+

Export in the WordPress admin.', 'memberlite' ); ?>

diff --git a/adminpages/tools/import-settings.php b/adminpages/tools/import-settings.php index dfa754e..862681e 100644 --- a/adminpages/tools/import-settings.php +++ b/adminpages/tools/import-settings.php @@ -18,12 +18,12 @@

-

+

- + + + +
+ @@ -33,6 +33,20 @@

+
+ +

+ +

+
+

diff --git a/css/admin.css b/css/admin.css index fe5e6ba..8ad78b9 100644 --- a/css/admin.css +++ b/css/admin.css @@ -588,3 +588,66 @@ body[class*="toplevel_page_memberlite-"] #footer-left a { display: flex; flex-direction: column; } + +/** + * Scrollable Boxes + */ +.memberlite_scrollable { + background-color: #FFFFFF; + height: 170px; + overflow: auto; +} + +.memberlite_scrollable::-webkit-scrollbar { + background-color: #c3c4c7; + width: 8px; + height: 8px; +} + +.memberlite_scrollable::-webkit-scrollbar-thumb { + background: #2c3338; + border-radius: 5px; +} + +/** + * Clickable Items + */ +.memberlite_clickable { + cursor: pointer; +} + +/** + * Checkbox Boxes + */ +.memberlite_checkbox_box { + background-color: #FFFFFF; + border: 1px solid var(--memberlite--border--color); + max-width: 300px; +} + +.memberlite_checkbox_box div { + border-bottom: 1px solid var(--memberlite--border--color); + padding: 6px; +} + +.memberlite_checkbox_box div:last-child { + border-bottom: none; +} + +.memberlite_checkbox_box .memberlite_clickable { + align-items: center; + cursor: pointer; + display: flex; +} + +.memberlite_checkbox_box .memberlite_clickable:hover { + background: rgba(255, 185, 0, 0.1); +} + +.memberlite_checkbox_box .memberlite_clickable label { + flex-grow: 1; +} + +.memberlite_checkbox_box .memberlite_clickable input[type=checkbox] { + margin-top: 0; +} diff --git a/functions.php b/functions.php index af00311..1a649f4 100644 --- a/functions.php +++ b/functions.php @@ -883,6 +883,9 @@ function memberlite_get_sidebar( $name = null ) { /* Tools */ require_once get_template_directory() . '/adminpages/tools.php'; +/* Manage Menus */ +require_once get_template_directory() . '/adminpages/menus.php'; + /* Integration for Paid Memberships Pro. */ if ( defined( 'PMPRO_VERSION' ) ) { require_once get_template_directory() . '/inc/integrations/paid-memberships-pro.php'; diff --git a/inc/admin.php b/inc/admin.php index 4b0ad9c..f0d4b19 100644 --- a/inc/admin.php +++ b/inc/admin.php @@ -5,9 +5,9 @@ * @package Memberlite */ -/* - Adds Theme Support submenu page to "Appearance" menu. -*/ +/** + * Add Memberlite admin pages. + */ function memberlite_add_pages() { // Top level menu right under Appearance. add_menu_page( __( 'Memberlite', 'memberlite' ), __( 'Memberlite', 'memberlite' ), 'edit_theme_options', 'memberlite-dashboard', 'memberlite_dashboard', 'dashicons-privacy', 61 ); @@ -15,6 +15,8 @@ function memberlite_add_pages() { // Memberlite admin subpages. add_submenu_page( 'memberlite-dashboard', __( 'Dashboard', 'memberlite' ), __( 'Dashboard', 'memberlite' ), 'edit_theme_options', 'memberlite-dashboard', 'memberlite_dashboard' ); + add_submenu_page( 'memberlite-dashboard', __( 'Custom Menus', 'memberlite' ), __( 'Custom Menus', 'memberlite' ), 'edit_theme_options', 'memberlite-custom-menus', 'memberlite_custom_menus' ); + add_submenu_page( 'memberlite-dashboard', __( 'Custom Sidebars', 'memberlite' ), __( 'Custom Sidebars', 'memberlite' ), 'edit_theme_options', 'memberlite-custom-sidebars', 'memberlite_custom_sidebars' ); add_submenu_page( 'memberlite-dashboard', __( 'Tools', 'memberlite' ), __( 'Tools', 'memberlite' ), 'edit_theme_options', 'memberlite-tools', 'memberlite_tools' ); @@ -242,3 +244,50 @@ function memberlite_hide_non_memberlite_notices() { } } add_action( 'in_admin_header', 'memberlite_hide_non_memberlite_notices' ); + +/** + * Add "Manage with Memberlite" link to Appearance > Menus page. + * + * @since 6.2 + */ +function memberlite_add_menus_page_link() { + $screen = get_current_screen(); + if ( ! $screen || $screen->base !== 'nav-menus' ) { + return; + } + + $custom_menus_url = admin_url( 'admin.php?page=memberlite-custom-menus' ); + ?> + +