Modules\Emojis::class, 'embeds' => Modules\Embeds::class, 'xmlrpc' => Modules\XmlRpc::class, 'rest_api' => Modules\RestApi::class, 'wp_version' => Modules\WpVersion::class, 'wlw_manifest' => Modules\WlwManifest::class, 'rsd_link' => Modules\RsdLink::class, 'shortlinks' => Modules\Shortlinks::class, 'rss_feeds' => Modules\RssFeeds::class, 'jquery_migrate' => Modules\JqueryMigrate::class, 'dashicons' => Modules\Dashicons::class, 'global_styles' => Modules\GlobalStyles::class, 'self_pingbacks' => Modules\SelfPingbacks::class, ]; public static function instance(): self { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } private function __construct() { // Seed safe defaults on first run. $this->maybe_seed_defaults(); add_action( 'admin_menu', [ $this, 'register_admin_menu' ] ); add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); // Init active modules. $this->init_modules(); } /** Seed recommended defaults on first run. Idempotent. */ private function maybe_seed_defaults(): void { if ( false === get_option( 'agocleanup_settings', false ) ) { update_option( 'agocleanup_settings', [ 'wp_version' => true, 'wlw_manifest' => true, 'rsd_link' => true, 'shortlinks' => true, 'emojis' => true, 'embeds' => true, 'xmlrpc' => true, 'self_pingbacks' => true, ] ); } } /* ───── Admin menu (smart pattern) ───── */ private const MENU_SLUG = 'agocleanup-tools'; public function register_admin_menu(): void { /* * Other plugins from the same author may already have added a shared * parent menu. When one is there the page goes under it, so the * dashboard does not grow a second top level entry for the same set of * tools. When there is none, this plugin creates its own parent under * its own prefix and never defines the shared slug. */ $parent = 'agolab-tools'; if ( empty( $GLOBALS['admin_page_hooks'][ $parent ] ) ) { $parent = self::MENU_SLUG; add_menu_page( __( 'aGo Tools', 'ago-cleanup' ), __( 'aGo Tools', 'ago-cleanup' ), 'manage_options', $parent, '__return_null', 'dashicons-hammer', 81 ); } add_submenu_page( $parent, __( 'aGo Cleanup', 'ago-cleanup' ), __( 'Cleanup', 'ago-cleanup' ), 'manage_options', 'agocleanup', [ Admin\Page::class, 'render' ] ); remove_submenu_page( $parent, $parent ); } /* ───── REST routes ───── */ public function register_rest_routes(): void { register_rest_route( 'agocleanup/v1', '/settings', [ [ 'methods' => 'GET', 'callback' => [ $this, 'handle_get_settings' ], 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ], [ 'methods' => 'POST', 'callback' => [ $this, 'handle_save_settings' ], 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ], ] ); } public function handle_get_settings(): \WP_REST_Response { return new \WP_REST_Response( $this->get_settings() ); } public function handle_save_settings( \WP_REST_Request $request ): \WP_REST_Response { $input = $request->get_json_params(); $settings = []; foreach ( array_keys( self::MODULES ) as $key ) { $settings[ $key ] = ! empty( $input[ $key ] ); } update_option( 'agocleanup_settings', $settings ); return new \WP_REST_Response( [ 'saved' => true, 'settings' => $settings ] ); } /* ───── Assets ───── */ public function enqueue_assets( string $hook ): void { if ( ! str_ends_with( $hook, '_page_agocleanup' ) ) { return; } wp_enqueue_style( 'agocleanup-admin', AGOCLEANUP_URL . 'assets/css/admin.css', [], AGOCLEANUP_VERSION ); wp_enqueue_script( 'agocleanup-admin', AGOCLEANUP_URL . 'assets/js/admin.js', [], AGOCLEANUP_VERSION, true ); wp_localize_script( 'agocleanup-admin', 'agocleanupData', [ 'restUrl' => rest_url( 'agocleanup/v1' ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'settings' => $this->get_settings(), 'i18n' => [ 'saving' => __( 'Saving...', 'ago-cleanup' ), 'saved' => __( 'Settings saved. Reload the frontend to see changes.', 'ago-cleanup' ), 'error' => __( 'Could not save settings.', 'ago-cleanup' ), ], ] ); } /* ───── Modules ───── */ private function init_modules(): void { $settings = $this->get_settings(); foreach ( self::MODULES as $key => $class ) { if ( ! empty( $settings[ $key ] ) ) { $class::init(); } } } /** @return array */ private function get_settings(): array { $saved = get_option( 'agocleanup_settings', [] ); $settings = []; foreach ( array_keys( self::MODULES ) as $key ) { $settings[ $key ] = ! empty( $saved[ $key ] ); } return $settings; } }