id values for built-in WordPress admin pages. Core screens * share no common prefix, so these are matched exactly rather than by prefix. * * @var string[] */ private const CORE_SCREEN_IDS = [ 'dashboard', 'edit', 'post', 'edit-page', 'page', 'upload', 'media', 'edit-comments', 'comment', 'themes', 'widgets', 'nav-menus', 'plugins', 'plugin-install', 'plugin-editor', 'users', 'user-new', 'profile', 'tools', 'import', 'export', 'site-health', 'export-personal-data', 'erase-personal-data', 'options-general', 'options-writing', 'options-reading', 'options-discussion', 'options-media', 'options-permalink', 'options-privacy', ]; /** * Map of screen ID patterns to plugin names * * @var array */ private const SCREEN_ID_MAP = [ 'eventin' => [ 'toplevel_page_eventin', 'eventin_page_', ], 'wpcafe' => [ 'toplevel_page_wpcafe', 'wpcafe_page_', ], 'timetics' => [ 'toplevel_page_timetics', 'timetics_page_', ], 'booktics' => [ 'toplevel_page_booktics', 'booktics_page_', ], 'aisentic' => [ 'toplevel_page_aisentic', 'aisentic_page_', ], // 'pointics_page_' is future-proofing: every core submenu is a hash route on one top-level slug, so only a third-party extension's own submenu would ever match it. 'pointics' => [ 'toplevel_page_pointics', 'pointics_page_', ], ]; /** * Get plugin name from screen ID * * @param string $screen_id WordPress screen ID. * @return string Plugin name (eventin, wpcafe, timetics, booktics, aisentic, * pointics), the WordPress core key (wordpress-core), the * theme key (themewinter-theme), or empty string if not matched. */ public static function get( string $screen_id ): string { if ( empty( $screen_id ) ) { return ''; } foreach ( self::SCREEN_ID_MAP as $plugin => $patterns ) { foreach ( $patterns as $pattern ) { if ( 0 === strpos( $screen_id, $pattern ) ) { return $plugin; } } } foreach ( self::theme_screen_patterns() as $pattern ) { if ( 0 === strpos( $screen_id, $pattern ) ) { return self::THEME_KEY; } } // Built-in WordPress admin screens (matched exactly — no shared prefix). if ( in_array( $screen_id, self::CORE_SCREEN_IDS, true ) ) { return self::WORDPRESS_CORE_KEY; } return ''; } /** * Screen ID patterns that indicate a Themewinter theme page. * * @return string[] */ private static function theme_screen_patterns(): array { $patterns = [ 'customize', // WP Customizer (generic to all themes). 'toplevel_page_exhibz', // Exhibz legacy menu. 'exhibz_page_', ]; // Standard Themewinter menu convention: theme-. foreach ( Active_Theme_Resolver::get_theme_slugs() as $slug ) { $patterns[] = 'toplevel_page_theme-' . $slug; } return $patterns; } /** * Get all available plugins * * @return array */ public static function get_all_plugins(): array { return array_merge( self::get_integration_plugins(), [ self::THEME_KEY, self::WORDPRESS_CORE_KEY ] ); } /** * Get the slugs used by integration screens. * * Search tools use these same slugs as documentation scopes. Keeping the * source here prevents a newly mapped integration from being silently * rejected by one of the tools. * * @return string[] */ public static function get_integration_plugins(): array { return array_keys( self::SCREEN_ID_MAP ); } }