*/ private static $settings_defaults = array( 'css_delivery' => 'file', 'google_fonts' => true, 'pattern_library' => true, 'remove_data' => false, ); /** * Constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'admin_menu' ), 20 ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_assets' ) ); add_action( 'rest_api_init', array( $this, 'register_routes' ) ); add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); add_action( 'save_post', array( self::class, 'flush_usage_cache' ) ); add_action( 'deleted_post', array( self::class, 'flush_usage_cache' ) ); } /* --------------------------------------------------------------- * Settings helpers * ------------------------------------------------------------- */ /** * Get the general settings, merged over the defaults. * * @return array Settings array. */ public static function get_settings() { $stored = get_option( self::SETTINGS_OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } return wp_parse_args( $stored, self::$settings_defaults ); } /** * Get a single setting value. * * @param string $key Setting key. * @param mixed $default Fallback when the key is unknown. * @return mixed Setting value. */ public static function get_setting( $key, $default = null ) { $settings = self::get_settings(); return array_key_exists( $key, $settings ) ? $settings[ $key ] : $default; } /** * Build the option key that stores a block's enabled state. * * @param string $block_name Block directory name. * @return string Option key. */ public static function block_option_key( $block_name ) { return 'gut_' . str_replace( '-', '_', $block_name ); } /** * Read the block definitions from the shared data file. * * @return array> Block definitions. */ public function get_blocks() { $blocks_file = GUTSLIDER_DIR . '/includes/Api/blocks.php'; if ( ! file_exists( $blocks_file ) ) { return array(); } $blocks = include $blocks_file; return is_array( $blocks ) ? $blocks : array(); } /** * Decorate the block definitions with their current state. * * @return array> Block definitions. */ public function get_blocks_with_state() { $has_pro = defined( 'GUTSLIDER_PRO_VERSION' ); $blocks = array(); foreach ( $this->get_blocks() as $block ) { $is_pro = ! empty( $block['is_pro'] ); $block['is_pro'] = $is_pro; $block['locked'] = $is_pro && ! $has_pro; $block['option_key'] = self::block_option_key( $block['name'] ); $block['enabled'] = $block['locked'] ? false : (bool) get_option( $block['option_key'], true ); $blocks[] = $block; } return $blocks; } /** * Count how many blocks are currently enabled. * * @param array> $blocks Decorated blocks. * @return int Enabled block count. */ public static function count_enabled( array $blocks ) { $count = 0; foreach ( $blocks as $block ) { if ( ! empty( $block['enabled'] ) ) { ++$count; } } return $count; } /* --------------------------------------------------------------- * Menu + assets * ------------------------------------------------------------- */ /** * Register the admin menu and its sub pages. * * @return void */ public function admin_menu() { $icon = 'data:image/svg+xml;base64,' . base64_encode( '' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Inline menu icon. add_menu_page( __( 'GutSlider', 'slider-blocks' ), __( 'GutSlider', 'slider-blocks' ), 'manage_options', self::MENU_SLUG, array( $this, 'render_dashboard_page' ), $icon, 100 ); add_submenu_page( self::MENU_SLUG, __( 'Dashboard', 'slider-blocks' ), __( 'Dashboard', 'slider-blocks' ), 'manage_options', self::MENU_SLUG, array( $this, 'render_dashboard_page' ) ); add_submenu_page( self::MENU_SLUG, __( 'Blocks', 'slider-blocks' ), __( 'Blocks', 'slider-blocks' ), 'manage_options', self::BLOCKS_SLUG, array( $this, 'render_blocks_page' ) ); add_submenu_page( self::MENU_SLUG, __( 'Settings', 'slider-blocks' ), __( 'Settings', 'slider-blocks' ), 'manage_options', self::SETTINGS_SLUG, array( $this, 'render_settings_page' ) ); } /** * Screen IDs that should receive the dashboard assets. * * @return array Screen IDs. */ public static function screen_ids() { return array( 'toplevel_page_' . self::MENU_SLUG, 'gutslider_page_' . self::BLOCKS_SLUG, 'gutslider_page_' . self::SETTINGS_SLUG, 'gutslider_page_gutslider-license', ); } /** * Whether the current request is a GutSlider dashboard screen. * * @param string $screen Current screen ID. * @return bool True on a dashboard screen. */ public static function is_dashboard_screen( $screen = '' ) { if ( '' === $screen ) { $current = function_exists( 'get_current_screen' ) ? get_current_screen() : null; $screen = $current ? $current->id : ''; } return in_array( $screen, self::screen_ids(), true ); } /** * Enqueue the dashboard stylesheet and script. * * @param string $screen Current screen ID. * @return void */ public function admin_assets( $screen ) { if ( ! self::is_dashboard_screen( $screen ) ) { return; } wp_enqueue_style( 'gutslider-admin', GUTSLIDER_URL . 'admin/css/admin.css', array(), GUTSLIDER_VERSION ); wp_enqueue_script( 'gutslider-admin', GUTSLIDER_URL . 'admin/js/admin.js', array( 'wp-i18n' ), GUTSLIDER_VERSION, true ); wp_localize_script( 'gutslider-admin', 'gutslider', array( 'version' => GUTSLIDER_VERSION, 'isPro' => defined( 'GUTSLIDER_PRO_VERSION' ), 'proVersion' => defined( 'GUTSLIDER_PRO_VERSION' ) ? GUTSLIDER_PRO_VERSION : '', 'restUrl' => esc_url_raw( rest_url( 'gutslider/v1/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'pricingUrl' => 'https://gutslider.com/pricing', 'i18n' => array( 'saved' => __( 'Changes saved', 'slider-blocks' ), 'saveFailed' => __( 'Could not save. Please try again.', 'slider-blocks' ), 'discarded' => __( 'Changes discarded', 'slider-blocks' ), 'cacheCleared' => __( 'Style cache cleared', 'slider-blocks' ), /* translators: %d: number of unsaved changes. */ 'changeSingle' => __( '%d unsaved change', 'slider-blocks' ), /* translators: %d: number of unsaved changes. */ 'changePlural' => __( '%d unsaved changes', 'slider-blocks' ), 'leaveWarning' => __( 'You have unsaved changes.', 'slider-blocks' ), /* translators: 1: enabled block count, 2: total block count. */ 'enabledCount' => __( '%1$d of %2$d enabled', 'slider-blocks' ), /* translators: 1: free block count, 2: pro block count, 3: enabled count, 4: total count. */ 'blockSummary' => __( '%1$d free blocks · %2$d pro blocks · %3$d of %4$d enabled', 'slider-blocks' ), 'allEnabled' => __( 'All blocks enabled', 'slider-blocks' ), /* translators: %d: number of disabled blocks. */ 'someDisabled' => __( '%d turned off', 'slider-blocks' ), ), ) ); } /** * Add a marker class so the stylesheet can reset the admin chrome. * * @param string $classes Existing body classes. * @return string Filtered body classes. */ public function admin_body_class( $classes ) { if ( self::is_dashboard_screen() ) { $classes .= ' gutslider-admin-page '; } return $classes; } /* --------------------------------------------------------------- * Dashboard data * ------------------------------------------------------------- */ /** * Scan post content for GutSlider blocks. * * Counts how many slider blocks are in use and how many posts they * live on. The result is cached because it is a full-text scan. * * @return array{sliders:int, posts:int, capped:bool} Usage figures. */ public static function get_usage_stats() { $cached = get_transient( self::USAGE_TRANSIENT ); if ( is_array( $cached ) ) { return $cached; } global $wpdb; $needle = '