[view, menu title, page title, optional module suffix]. * * The Pro screens exist only when the Pro code does; the free plugin shows one "A11yMate Pro" screen * instead. * * @return array */ public static function screens() { $screens = array( 'a11ymate' => array( 'dashboard', __( 'Dashboard', 'a11ymate' ), __( 'Dashboard', 'a11ymate' ) ), 'a11ymate-scan' => array( 'scan', __( 'Scan', 'a11ymate' ), __( 'Site scan', 'a11ymate' ) ), 'a11ymate-issues' => array( 'issues', __( 'Issues', 'a11ymate' ), __( 'Issues', 'a11ymate' ) ), ); $screens['a11ymate-fixes'] = array( 'fixes', __( 'Safe Fixes', 'a11ymate' ), __( 'Safe Fixes', 'a11ymate' ) ); $screens['a11ymate-settings'] = array( 'settings', __( 'Settings', 'a11ymate' ), __( 'Settings', 'a11ymate' ) ); if ( ! Plan::has_pro() ) { $screens['a11ymate-pro'] = array( 'pro', __( 'A11yMate Pro', 'a11ymate' ), __( 'A11yMate Pro', 'a11ymate' ) ); } return $screens; } /** * Hooks. * * @return void */ public function register() { add_action( 'admin_menu', array( $this, 'menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'assets' ) ); add_filter( 'script_loader_tag', array( $this, 'module_tag' ), 10, 2 ); add_filter( 'plugin_action_links_' . plugin_basename( A11YMATE_FILE ), array( $this, 'action_links' ) ); add_filter( 'admin_body_class', array( $this, 'body_class' ) ); } /** * Is the current admin screen one of ours? * * @return bool */ private function is_ours() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended return isset( self::screens()[ $page ] ); } /** * Body class for scoped styles. * * @param string $classes Classes. * @return string */ public function body_class( $classes ) { return $this->is_ours() ? $classes . ' a11ymate-screen' : $classes; } /** * Plugin list links. * * @param array $links Links. * @return array */ public function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Open A11yMate', 'a11ymate' ) . '' ); return $links; } /** * Menu icon: the A11yMate mark, a focus ring around a target. * * @return string */ private function icon() { $svg = ''; return 'data:image/svg+xml;base64,' . base64_encode( $svg ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } /** * Register the menu. * * @return void */ public function menu() { $cap = Settings::capability(); $top = 'a11ymate'; add_menu_page( __( 'A11yMate', 'a11ymate' ), __( 'A11yMate', 'a11ymate' ), $cap, $top, array( $this, 'render' ), $this->icon(), 58 ); foreach ( self::screens() as $slug => $s ) { add_submenu_page( $top, $s[2] . ' - A11yMate', $s[1], $cap, $slug, array( $this, 'render' ) ); } } /** * Print the container the JS app mounts into. * * @return void */ public function render() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : 'a11ymate'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $screens = self::screens(); $view = isset( $screens[ $page ] ) ? $screens[ $page ][0] : 'dashboard'; echo '

' . esc_html( $screens[ $page ][2] ) . '

'; echo '
'; echo '

' . esc_html__( 'Loading A11yMate…', 'a11ymate' ) . '

'; echo ''; echo '
'; } /** * Cache-busting version. On local dev sites it follows file changes. * * @return string */ public static function asset_version() { if ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() ) { $latest = 0; $it = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( A11YMATE_DIR . 'assets', \FilesystemIterator::SKIP_DOTS ) ); foreach ( $it as $file ) { $latest = max( $latest, $file->getMTime() ); } return A11YMATE_VERSION . '.' . $latest; } return A11YMATE_VERSION; } /** * Load assets on our screens only. * * @return void */ public function assets() { if ( ! $this->is_ours() ) { return; } $ver = self::asset_version(); wp_enqueue_style( 'a11ymate-admin', A11YMATE_URL . 'assets/admin/css/admin.css', array(), $ver ); wp_enqueue_script( 'a11ymate-color', A11YMATE_URL . 'assets/shared/color.js', array(), $ver, true ); wp_enqueue_script( 'a11ymate-rendered', A11YMATE_URL . 'assets/admin/js/rendered-scan.js', array( 'a11ymate-color' ), $ver, true ); wp_enqueue_script( 'a11ymate-admin', A11YMATE_URL . 'assets/admin/js/app.js', array( 'wp-api-fetch', 'wp-i18n', 'a11ymate-color', 'a11ymate-rendered' ), $ver, true ); wp_set_script_translations( 'a11ymate-admin', 'a11ymate', A11YMATE_DIR . 'languages' ); $pages = array(); foreach ( self::screens() as $slug => $s ) { $pages[ $s[0] ] = array( 'slug' => $slug, 'url' => admin_url( 'admin.php?page=' . $slug ), 'label' => $s[1], 'module' => $s[0] . ( isset( $s[3] ) ? $s[3] : '' ), ); } $data = array( 'version' => $ver, 'assets' => A11YMATE_URL . 'assets/', 'pages' => $pages, 'home' => home_url( '/' ), 'canManage' => current_user_can( 'manage_options' ), 'canStyle' => current_user_can( 'edit_theme_options' ), 'canPublish' => current_user_can( 'edit_pages' ), 'mediaUrl' => admin_url( 'upload.php' ), 'optionsUrl' => admin_url( 'options-general.php' ), 'pro' => Plan::has_pro(), 'proUrl' => Plan::PRICING_URL, 'phrases' => \A11yMate\Rules\Registry::phrases(), ); wp_localize_script( 'a11ymate-admin', 'A11yMateAdmin', $data ); } /** * Load the admin script as an ES module. * * @param string $tag Tag. * @param string $handle Handle. * @return string */ public function module_tag( $tag, $handle ) { if ( 'a11ymate-admin' !== $handle ) { return $tag; } // Keep the tag WordPress printed for the enqueued script (its id, version and any inline // scripts attached to it) and only make it a module. Inline scripts have no src, so only the // main tag changes. $tag = str_replace( array( " type='text/javascript'", ' type="text/javascript"' ), '', $tag ); return str_replace( ' src=', ' type="module" src=', $tag ); } }