addons->get('accordion'); // single value * $s->addons->get(); // full array * $s->addons->is_enabled('accordion'); // bool * $s->addons->enabled(); // all enabled with Config data * $s->addons->group('basic')->all(); // Config items in sub-group * $s->addons->group('basic')->enabled(); // enabled items in sub-group * $s->addons->group('basic')->counts(); // ['total' => n, 'enabled' => n] * $s->addons->group('basic')->enable(); // bulk-enable sub-group * * 2. Global helper function: * jltma_settings()->addons->get('accordion'); * jltma_settings()->api->get('google_maps_api_key'); * jltma_settings()->get('jltma_white_label_settings'); // any wp_option * jltma_settings()->get('jltma_white_label_settings', 'sub_key'); // sub-key access * * 3. Static facade shorthand (one-liner reads): * Settings::addons('accordion'); // = instance()->addons->get('accordion') * Settings::api('google_maps_api_key'); // = instance()->api->get(...) * * 4. Classic static methods (backward-compatible): * Settings::get_addons('key'); * Settings::is_addon_enabled('key'); * Settings::save_addons($array); * Settings::get_enabled_addons_by_group('basic'); * * Security: * - Option keys validated against known constants before any DB read/write * - Toggle maps sanitized to integer 0|1; API values run through sanitize_text_field() * - All array keys sanitized with sanitize_key() * * @package MasterAddons\Inc\Admin\Settings * @since 2.0.0 */ class Settings { /** * Option key constants */ const ADDONS_KEY = 'jltma_addons'; const EXTENSIONS_KEY = 'jltma_extensions'; const PLUGINS_KEY = 'jltma_plugins'; const ICONS_KEY = 'jltma_icons'; const API_KEY = 'jltma_api'; const WHITE_LABEL = 'jltma_white_label_settings'; const VERSION_KEY = '_master_addons_version'; /** * Group name → option key map (used by __get and __callStatic) */ const PROXY_MAP = [ 'addons' => self::ADDONS_KEY, 'extensions' => self::EXTENSIONS_KEY, 'plugins' => self::PLUGINS_KEY, 'icons' => self::ICONS_KEY, 'api' => self::API_KEY, ]; /** * Legacy option keys for migration */ const LEGACY_KEYS = [ 'maad_el_save_settings' => self::ADDONS_KEY, 'ma_el_extensions_save_settings' => self::EXTENSIONS_KEY, 'ma_el_third_party_plugins_save_settings' => self::PLUGINS_KEY, 'jltma_icons_library_save_settings' => self::ICONS_KEY, 'jltma_api_save_settings' => self::API_KEY, ]; /** * Cache for settings */ private static $cache = []; public function __construct() { add_action('admin_menu', [$this, 'register_admin_menu'], 10); add_action('network_admin_menu', [$this, 'register_admin_menu'], 10); add_action('current_screen', [$this, 'jltma_set_page_title']); add_action('admin_enqueue_scripts', [$this, 'jltma_admin_settings_scripts'], 99); add_action('admin_body_class', [$this, 'jltma_admin_body_class']); add_action('admin_enqueue_scripts', [$this, 'jltma_global_admin_css']); add_action('wp_ajax_jltma_subscribe', [$this, 'handle_subscribe_ajax']); } /** * Set page title for MA admin pages before admin-header.php runs. * Freemius may override menu registration, leaving global $title unset. */ public function jltma_set_page_title($screen) { global $title; if (!empty($title)) { return; } if (strpos($screen->id, 'master-addons-setup-wizard') !== false) { $title = __('Setup Wizard', 'master-addons'); } elseif (strpos($screen->id, 'master-addons-settings') !== false) { $title = apply_filters('master_addons/white_label/page_title', __('Master Addons for Elementor', 'master-addons')); } } /** * Handle subscribe AJAX request * Delegates to the Subscribe notification class */ public function handle_subscribe_ajax() { $subscribe = new \MasterAddons\Inc\Classes\Notifications\Subscribe(); $subscribe->jltma_subscribe(); } /** * Global admin CSS for menu styling (runs on all admin pages) */ public function jltma_global_admin_css() { $css = ' /* Hide separator below Master Addons menu */ #adminmenu #toplevel_page_master-addons-settings + .wp-menu-separator { display: none !important; } #toplevel_page_master-addons-settings { margin-bottom: 0 !important; padding-bottom: 0 !important; } /* Strip ALL borders from submenu items first */ #toplevel_page_master-addons-settings .wp-submenu li { border: none !important; } /* Then add back only our separators */ #toplevel_page_master-addons-settings .wp-submenu li.jltma-menu-separator { border-bottom: 1px solid hsla(0, 0%, 100%, 0.12) !important; padding-bottom: 6px; margin-bottom: 6px; } /* Pricing submenu - full-width green button like Spectra */ #toplevel_page_master-addons-settings .wp-submenu li.jltma-menu-pricing a { background: #ffa500 !important; font-weight: 600 !important; padding: 8px 12px !important; } #toplevel_page_master-addons-settings .wp-submenu li.jltma-menu-pricing a span { color: #000 !important; } #toplevel_page_master-addons-settings .wp-submenu li.jltma-menu-pricing a:hover { color: #fff !important; background: #16a34a !important; } #toplevel_page_master-addons-settings .wp-submenu li.jltma-menu-pricing a:hover span { color: #fff !important; } /* ── CPT List Page: Injected Buttons (next to native WP "Add New") ── */ .wrap .jltma-cpt-btn { display: inline-flex; align-items: center; gap: 5px; padding: 1px 12px; font-size: 13px; font-weight: 600; line-height: 2.15384615; min-height: 28px; text-decoration: none; cursor: pointer; white-space: nowrap; border-radius: 3px; box-sizing: content-box; vertical-align: baseline; margin-left: 8px; } .wrap .jltma-cpt-btn svg { flex-shrink: 0; } .wrap .jltma-cpt-btn-secondary { background: linear-gradient(135deg, rgb(153, 41, 234) 0%, rgb(88, 8, 251) 100%); color: #fff; border: none; transition: opacity 0.15s ease; } .wrap .jltma-cpt-btn-secondary:hover, .wrap .jltma-cpt-btn-secondary:focus { opacity: 0.88; color: #fff; } .wrap .jltma-cpt-btn-youtube { background: linear-gradient(135deg, #fff5f5 0%, #ffe0e0 100%); color: #cc0000; border: 1px solid #fecaca; transition: border-color 0.15s ease, background 0.15s ease; } .wrap .jltma-cpt-btn-youtube:hover, .wrap .jltma-cpt-btn-youtube:focus { background: linear-gradient(135deg, #ffe0e0 0%, #fecaca 100%); border-color: #f87171; color: #b91c1c; } .wrap .jltma-cpt-btn-youtube svg { fill: #ff0000; } '; // JS to reorder submenu items and add separator/pricing classes $js = ' document.addEventListener("DOMContentLoaded", function() { var menu = document.getElementById("toplevel_page_master-addons-settings"); if (!menu) return; var submenu = menu.querySelector(".wp-submenu"); if (!submenu) return; // Reorder: Move "Template Kits" right after "Template Library" var items = submenu.querySelectorAll("li a"); var libraryItem = null, kitsItem = null, wizardItem = null, recommendedItem = null, widgetsLibItem = null; items.forEach(function(a) { var href = a.getAttribute("href") || ""; if (href.indexOf("jltma-widgets-library") !== -1) widgetsLibItem = a.parentElement; if (href.indexOf("jltma-template-library") !== -1) libraryItem = a.parentElement; if (href.indexOf("jltma-template-kits") !== -1) kitsItem = a.parentElement; if (href.indexOf("master-addons-setup-wizard") !== -1) wizardItem = a.parentElement; if (href.indexOf("master-addons-recommended") !== -1) recommendedItem = a.parentElement; }); // Place Template Kits right after Template Library if (libraryItem && kitsItem && libraryItem.nextElementSibling !== kitsItem) { submenu.insertBefore(kitsItem, libraryItem.nextElementSibling); } // Place Widgets Library immediately before Template Library if (libraryItem && widgetsLibItem && libraryItem.previousElementSibling !== widgetsLibItem) { submenu.insertBefore(widgetsLibItem, libraryItem); } // Move "Setup Wizard" just before "Recommended" if (wizardItem && recommendedItem) { submenu.insertBefore(wizardItem, recommendedItem); } // Separators and pricing var separatorAfter = [ "page=master-addons-settings", "jltma-template-kits", "post_type=jltma_widget" ]; submenu.querySelectorAll("li a").forEach(function(a) { var href = a.getAttribute("href") || ""; for (var i = 0; i < separatorAfter.length; i++) { var pos = href.indexOf(separatorAfter[i]); if (pos === -1) { continue; } // Require a param boundary after the match so // "page=master-addons-settings" does NOT also match // "page=master-addons-settings-account" (the Account item). var nextChar = href.charAt(pos + separatorAfter[i].length); if (nextChar === "" || nextChar === "&" || nextChar === "#" || nextChar === "\"") { a.parentElement.classList.add("jltma-menu-separator"); break; } } var text = (a.textContent || "").trim().toLowerCase(); if (href.indexOf("pricing") !== -1 || text.indexOf("pricing") !== -1 || text.indexOf("upgrade") !== -1) { a.parentElement.classList.add("jltma-menu-pricing"); } }); }); '; // Load the menu styling and ordering through the core enqueue APIs // (inline-only handles) instead of hardcoded