'); add_menu_page( 'Adaire Blocks', 'Adaire Blocks', 'manage_options', 'adaire-blocks-settings', array($this, 'settings_page'), $svg_icon, 30 ); } /** * Initialize settings */ public function init_settings() { register_setting( 'adaire_blocks_settings_group', $this->option_name, array($this, 'sanitize_settings') ); // Get default settings $this->settings = get_option($this->option_name, $this->get_default_settings()); // // Debug: Log settings (only in debug mode) // if (defined('WP_DEBUG') && WP_DEBUG) { // error_log('Adaire Blocks Settings: ' . print_r($this->settings, true)); // } } /** * Get default settings based on available blocks */ private function get_default_settings() { $defaults = array(); $available_blocks = $this->get_available_blocks(); // Set all available blocks (including auxiliary child blocks) to enabled by default. // Auxiliary blocks are always synced to their parent, but still need a stored setting. foreach ($available_blocks as $block_key => $block_data) { $defaults[$block_key] = true; } return $defaults; } /** * Sanitize settings */ public function sanitize_settings($input) { // // Debug: Log what's being submitted (only in debug mode) // if (defined('WP_DEBUG') && WP_DEBUG) { // error_log('Adaire Blocks Sanitize Input: ' . print_r($input, true)); // } $sanitized = array(); $available_blocks = $this->get_available_blocks(); // Get current settings to preserve disabled blocks $current_settings = get_option($this->option_name, $this->get_default_settings()); // First, read the submitted values for all blocks. foreach ($available_blocks as $block_key => $block_data) { // If the checkbox is checked, it will be in the input array. // If it's unchecked, it won't be in the input array, so we set it to false. $sanitized[$block_key] = isset($input[$block_key]) && $input[$block_key] ? true : false; } // Ensure auxiliary/child blocks always follow their parent block's setting. foreach ($available_blocks as $block_key => $block_data) { if (!empty($block_data['is_auxiliary']) && !empty($block_data['parent_key'])) { $parent_key = $block_data['parent_key']; if (isset($sanitized[$parent_key])) { $sanitized[$block_key] = $sanitized[$parent_key]; } else { // If for some reason the parent is missing, default the child to enabled. $sanitized[$block_key] = true; } } } // // Debug: Log what's being saved (only in debug mode) // if (defined('WP_DEBUG') && WP_DEBUG) { // error_log('Adaire Blocks Sanitized Output: ' . print_r($sanitized, true)); // } return $sanitized; } /** * Get available blocks by scanning the build directory and checking configuration */ public function get_available_blocks() { $blocks = array(); $build_dir = ADAIRE_BLOCKS_PLUGIN_PATH . 'build/'; $config = AdaireBlocksConfig::get_instance(); // Check if build directory exists if (!is_dir($build_dir)) { return $blocks; } // Scan for block directories $directories = glob($build_dir . '*', GLOB_ONLYDIR); foreach ($directories as $dir) { $block_name = basename($dir); // Skip if it's not a block directory or doesn't have block.json if (!file_exists($dir . '/block.json')) { continue; } // Load all blocks - we'll handle premium/free logic in the display // Read block.json to get block information $block_json = file_get_contents($dir . '/block.json'); $block_data = json_decode($block_json, true); if (!$block_data) { continue; } // Convert block name to settings key $settings_key = str_replace('-', '_', $block_name); // Get block metadata $name = isset($block_data['title']) ? $block_data['title'] : ucwords(str_replace('-', ' ', $block_name)); $description = isset($block_data['description']) ? $block_data['description'] : 'Custom block for ' . $name; // Raw category slug from block.json (e.g. adaire-blocks-free / adaire-blocks-plus / adaire-blocks-premium). $category_slug = isset($block_data['category']) ? $block_data['category'] : ''; // Human-readable category label for display. switch ($category_slug) { case 'adaire-blocks-free': $category_label = 'Free'; break; case 'adaire-blocks-plus': $category_label = 'Plus'; break; case 'adaire-blocks-premium': $category_label = 'Premium'; break; default: $category_label = $category_slug ? ucwords(str_replace('-', ' ', $category_slug)) : 'Widgets'; break; } $icon = isset($block_data['icon']) ? $block_data['icon'] : 'admin-generic'; // Determine if this is an auxiliary "child" block that should be hidden in the UI // but still follow its parent block's enabled/disabled state. $is_auxiliary = false; $parent_key = null; if (isset($block_data['parent']) && is_array($block_data['parent']) && !empty($block_data['parent'])) { // Use the first declared parent as the "owning" block. $parent_name = $block_data['parent'][0]; // Normalise to the slug used for build directory (strip common prefixes). $parent_slug = preg_replace('#^(create-block/|adaire/|adaire-blocks/)#', '', $parent_name); $parent_key = str_replace('-', '_', $parent_slug); // Treat any block with a parent as an auxiliary block for settings UI purposes. $is_auxiliary = true; } // Add upgrade message if block has limitations $upgrade_message = ''; $limits = array(); // Determine if this is a premium block (disabled in free version) $is_premium_block = false; // // Debug: Log block status (only in debug mode) // if (defined('WP_DEBUG') && WP_DEBUG) { // error_log("Adaire Blocks Settings: Block $block_name - enabled: " . ($config->is_block_enabled($block_name) ? 'true' : 'false') . ", premium: " . ($is_premium_block ? 'true' : 'false')); // } $blocks[$settings_key] = array( 'name' => $name, 'description' => $description, 'category' => $category_label, 'category_slug' => $category_slug, 'icon' => $icon, 'block_name' => $block_name, 'upgrade_message' => $upgrade_message, 'limits' => $limits, 'is_premium' => $is_premium_block, // Auxiliary (child/inner) blocks are hidden from the UI but kept in settings. 'is_auxiliary' => $is_auxiliary, 'parent_key' => $parent_key, ); } return $blocks; } /** * Allowed SVG tags/attributes for block icons. */ private function get_allowed_svg_tags() { static $allowed_svg = null; if ($allowed_svg !== null) { return $allowed_svg; } $shared_attributes = array( 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'strokewidth' => true, 'strokelinecap' => true, 'strokelinejoin' => true, 'opacity' => true, 'fill-opacity' => true, 'stroke-opacity' => true, 'style' => true, 'class' => true, 'id' => true, 'transform' => true, 'clip-path' => true, 'clip-rule' => true, 'mask' => true, 'vector-effect' => true ); $allowed_svg = array( 'svg' => array_merge( $shared_attributes, array( 'xmlns' => true, 'xmlns:xlink' => true, 'width' => true, 'height' => true, 'viewbox' => true, 'viewBox' => true, 'preserveAspectRatio' => true, 'aria-hidden' => true, 'role' => true, 'focusable' => true ) ), 'g' => $shared_attributes, 'path' => array_merge($shared_attributes, array('d' => true)), 'rect' => array_merge($shared_attributes, array('x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true)), 'circle' => array_merge($shared_attributes, array('cx' => true, 'cy' => true, 'r' => true)), 'ellipse' => array_merge($shared_attributes, array('cx' => true, 'cy' => true, 'rx' => true, 'ry' => true)), 'line' => array_merge($shared_attributes, array('x1' => true, 'y1' => true, 'x2' => true, 'y2' => true)), 'polyline' => array_merge($shared_attributes, array('points' => true)), 'polygon' => array_merge($shared_attributes, array('points' => true)), 'defs' => $shared_attributes, 'lineargradient' => array_merge( $shared_attributes, array( 'id' => true, 'gradientunits' => true, 'gradienttransform' => true, 'x1' => true, 'x2' => true, 'y1' => true, 'y2' => true ) ), 'radialgradient' => array_merge( $shared_attributes, array( 'id' => true, 'gradientunits' => true, 'gradienttransform' => true, 'cx' => true, 'cy' => true, 'r' => true, 'fx' => true, 'fy' => true ) ), 'stop' => array( 'offset' => true, 'stop-color' => true, 'stop-opacity' => true, 'style' => true ), 'clippath' => array_merge($shared_attributes, array('id' => true)), 'mask' => array_merge($shared_attributes, array('id' => true, 'x' => true, 'y' => true, 'width' => true, 'height' => true)), 'title' => array(), 'desc' => array(), 'use' => array_merge($shared_attributes, array('xlink:href' => true, 'href' => true)), 'symbol' => array_merge($shared_attributes, array('viewbox' => true, 'viewBox' => true)), 'pattern' => array_merge($shared_attributes, array('id' => true, 'patternunits' => true, 'patterntransform' => true, 'width' => true, 'height' => true)) ); return $allowed_svg; } /** * Enqueue admin scripts and styles */ public function enqueue_admin_scripts($hook) { // Get plugin version for cache busting $main_plugin_file = dirname(dirname(__FILE__)) . '/adaire-blocks.php'; $plugin_version = '1.0.0'; if (file_exists($main_plugin_file)) { $plugin_data = get_file_data($main_plugin_file, array('Version' => 'Version'), false); $plugin_version = !empty($plugin_data['Version']) ? $plugin_data['Version'] : '1.0.0'; } wp_register_style('adaire-blocks-admin-menu', false, array(), $plugin_version); wp_enqueue_style('adaire-blocks-admin-menu'); wp_add_inline_style( 'adaire-blocks-admin-menu', '#adminmenu .toplevel_page_adaire-blocks-settings .wp-menu-image:before,' . PHP_EOL . '#adminmenu .current .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu .wp-has-current-submenu .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu a.current:hover .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu a.wp-has-current-submenu:hover .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu li.wp-has-current-submenu a:focus .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu li.wp-has-current-submenu.opensub .toplevel_page_adaire-blocks-settings div.wp-menu-image:before,' . PHP_EOL . '#adminmenu li.wp-has-current-submenu:hover .toplevel_page_adaire-blocks-settings div.wp-menu-image:before {' . PHP_EOL . ' display: none !important;' . PHP_EOL . '}' ); if ($hook !== 'toplevel_page_adaire-blocks-settings') { return; } wp_register_style( 'adaire-blocks-admin', plugin_dir_url(__FILE__) . 'css/admin-settings.css', array(), $plugin_version ); wp_enqueue_style('adaire-blocks-admin'); wp_register_script( 'adaire-blocks-admin', plugin_dir_url(__FILE__) . 'js/admin-settings.js', array('jquery'), '1.0.0', true ); wp_enqueue_script('adaire-blocks-admin'); wp_localize_script( 'adaire-blocks-admin', 'AdaireBlocksAdminData', array( 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('adaire_blocks_settings'), 'optionName' => $this->option_name, 'strings' => array( 'saveSuccess' => esc_html__('Settings saved successfully!', 'adaire-blocks'), 'saveError' => esc_html__('Unable to save settings. Please try again.', 'adaire-blocks'), ), ) ); } /** * Settings page HTML */ public function settings_page() { // Debug: Check if class exists if (!class_exists('AdaireBlocksConfig')) { wp_die('Error: AdaireBlocksConfig class not found. Please check if the plugin is properly loaded.'); } $config = AdaireBlocksConfig::get_instance(); // Safety check - ensure config is loaded if (!$config) { wp_die('Error: Adaire Blocks configuration not loaded. Please try refreshing the page.'); } // // Debug: Log config status (only in debug mode) // if (defined('WP_DEBUG') && WP_DEBUG) { // error_log('Adaire Blocks Settings: Config loaded - is_premium: ' . ($config->is_premium() ? 'true' : 'false')); // error_log('Adaire Blocks Settings: Plugin version: ' . $config->get_plugin_version()); // } $available_blocks = $this->get_available_blocks(); $settings = get_option($this->option_name, $this->get_default_settings()); // Show success message if settings were just saved // Note: WordPress core handles nonce verification during form submission via settings_fields(). // The settings-updated parameter is set by WordPress core on redirect after successful save. // We verify both capability and nonce/referer to ensure security. if (isset($_GET['settings-updated']) && current_user_can('manage_options')) { // Verify nonce if present, or check admin referer // On redirect, nonce may not be in URL, so we check referer as fallback $nonce_action = 'adaire_blocks_settings_group-options'; $nonce_verified = false; if (isset($_REQUEST['_wpnonce'])) { $nonce = sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])); if (wp_verify_nonce($nonce, $nonce_action)) { $nonce_verified = true; } } if (!$nonce_verified && check_admin_referer($nonce_action, '', false)) { // check_admin_referer with false parameter only checks referer, not nonce $nonce_verified = true; } // Only proceed if nonce/referer is verified if ($nonce_verified) { // Sanitize and verify the input $settings_updated = sanitize_text_field(wp_unslash($_GET['settings-updated'])); // Only show message if value is 'true' or '1' (WordPress core sets it to 'true') if ($settings_updated === 'true' || $settings_updated === '1') { echo '

' . esc_html__('Settings saved successfully!', 'adaire-blocks') . '

'; } } } ?>

array(), 'adaire-blocks-plus' => array(), 'adaire-blocks-premium' => array(), 'other' => array(), ); foreach ($available_blocks as $block_key => $block_data) { if (!empty($block_data['is_auxiliary'])) { continue; } $slug = !empty($block_data['category_slug']) ? $block_data['category_slug'] : 'other'; if (!isset($grouped_blocks[$slug])) { $grouped_blocks[$slug] = array(); } $grouped_blocks[$slug][$block_key] = $block_data; } /** * Helper closure to render a tier section. */ $render_tier = function( $title, $blocks_for_tier ) use ( $settings ) { if (empty($blocks_for_tier)) { return; } ?>

$block_data): ?>
Premium
get_allowed_svg_tags(); // Check if icon is SVG content (starts with '; } ?>

option_name, array()); $default_settings = $this->get_default_settings(); $has_new_blocks = false; // Start with saved settings, then add any new blocks from defaults $merged_settings = $saved_settings; // Check for new blocks that exist in defaults but not in saved settings foreach ($default_settings as $block_key => $default_value) { if (!isset($saved_settings[$block_key])) { // New block detected - enable it by default $merged_settings[$block_key] = true; $has_new_blocks = true; } } // Save the merged settings if new blocks were added (to persist the defaults) if ($has_new_blocks) { update_option($this->option_name, $merged_settings); } return $merged_settings; } /** * Check if block is enabled */ public function is_block_enabled($block_key) { $settings = $this->get_settings(); return isset($settings[$block_key]) ? $settings[$block_key] : true; } /** * Check if a block exists in the build directory */ public function block_exists($block_name) { $build_dir = ADAIRE_BLOCKS_PLUGIN_PATH . 'build/' . $block_name; return is_dir($build_dir) && file_exists($build_dir . '/block.json'); } /** * AJAX handler for saving settings */ public function ajax_save_settings() { check_ajax_referer('adaire_blocks_settings', 'nonce'); if (!current_user_can('manage_options')) { wp_die('Insufficient permissions'); } $form_data = isset($_POST['formData']) ? wp_unslash($_POST['formData']) : ''; if (empty($form_data)) { wp_send_json_error(esc_html__('No settings data received.', 'adaire-blocks')); } parse_str($form_data, $parsed_form); $input_settings = isset($parsed_form[$this->option_name]) ? (array) $parsed_form[$this->option_name] : array(); $settings = $this->sanitize_settings($input_settings); update_option($this->option_name, $settings); wp_send_json_success(array( 'message' => esc_html__('Settings saved successfully!', 'adaire-blocks'), 'settings' => $settings )); } /** * AJAX handler for resetting settings */ public function ajax_reset_settings() { check_ajax_referer('adaire_blocks_settings', 'nonce'); if (!current_user_can('manage_options')) { wp_die('Insufficient permissions'); } $default_settings = $this->get_default_settings(); update_option($this->option_name, $default_settings); wp_send_json_success(array( 'message' => 'Settings reset to defaults!', 'settings' => $default_settings )); } /** * Add settings link to plugins page */ public function add_plugin_settings_link($links, $file) { // Check if this is our plugin file if (strpos($file, 'adaire-blocks.php') !== false) { $settings_link = '' . __('Settings', 'adaire-blocks') . ''; array_unshift($links, $settings_link); } return $links; } } // Initialize settings using singleton pattern AdaireBlocksSettings::get_instance();