[ label, url, screen ID ]. */ public static function tabs() : array { $tabs = [ 'pages' => [ 'label' => __( 'Pages', 'deadbolt' ), 'url' => Pages::url(), 'screen' => self::pages_screen_id(), ], 'templates' => [ 'label' => __( 'Templates', 'deadbolt' ), 'url' => admin_url( 'edit.php?post_type=' . Templates::POST_TYPE ), 'screen' => 'edit-' . Templates::POST_TYPE, ], 'settings' => [ 'label' => __( 'Settings', 'deadbolt' ), 'url' => Settings::url(), 'screen' => self::settings_screen_id(), ], ]; return apply_filters( 'deadbolt_tabs', $tabs ); } // End tabs() /** * Register the top-level menu and the Pages tab. Runs before post type submenus so Pages is the first item. * * @return void */ public function register_menu() : void { $title = __( 'Deadbolt', 'deadbolt' ); add_menu_page( $title, $title, 'manage_options', self::SLUG, [ Pages::class, 'render_page' ], 'dashicons-visibility', 81 ); add_submenu_page( self::SLUG, __( 'Pages', 'deadbolt' ), __( 'Pages', 'deadbolt' ), 'manage_options', self::SLUG, [ Pages::class, 'render_page' ] ); } // End register_menu() /** * Register the Settings tab. Runs after post type submenus so Settings comes after Templates. * * @return void */ public function register_settings_menu() : void { self::$settings_hook = (string) add_submenu_page( self::SLUG, __( 'Settings', 'deadbolt' ), __( 'Settings', 'deadbolt' ), 'manage_options', self::SETTINGS_SLUG, [ Settings::class, 'render_page' ] ); } // End register_settings_menu() /** * Get the active tab key. * * @return string */ public static function active_tab() : string { $screen = get_current_screen(); foreach ( self::tabs() as $key => $tab ) { if ( $screen && $screen->id === $tab[ 'screen' ] ) { return $key; } } return ''; } // End active_tab() /** * Add a body class on the plugin's screens. * * @param string $classes * @return string */ public function body_class( $classes ) : string { return Helpers::is_plugin_screen() ? $classes . ' deadbolt-admin-screen' : (string) $classes; } // End body_class() /** * Remove Screen Options and Help from the plugin's screens. * * @return void */ public function remove_screen_meta() : void { if ( ! Helpers::is_plugin_screen() ) { return; } add_filter( 'screen_options_show_screen', '__return_false' ); get_current_screen()->remove_help_tabs(); } // End remove_screen_meta() /** * Render the header and subheader before admin notices, on the plugin's screens only. * * @return void */ public function maybe_render_header() : void { if ( ! Helpers::is_plugin_screen() ) { return; } $tabs = self::tabs(); $active = self::active_tab(); $logo = self::logo_url(); $logo_info = self::logo_info( $logo ); ?>
decoding="sync" fetchpriority="high">

$tab ) : ?>

$value ) { $channel = $value / 255; $total += $weights[ $index ] * ( $channel <= 0.03928 ? $channel / 12.92 : ( ( $channel + 0.055 ) / 1.055 ) ** 2.4 ); } return $total; } // End luminance() /** * Get the header logo URL. * * @return string */ private static function logo_url() : string { return (string) apply_filters( 'deadbolt_logo', add_query_arg( 'ver', Bootstrap::script_version(), Bootstrap::url() . 'inc/img/logo.png' ) ); } // End logo_url() /** * Get the width, height and average luminance of a local logo image. Cached until the file changes. * * Luminance is weighted by opacity. Values are null when the logo cannot be read. * * @param string $url * @return array [ width, height, lum ] */ private static function logo_info( string $url ) : array { $info = [ 'width' => null, 'height' => null, 'lum' => null, 'data' => '' ]; $uploads = wp_upload_dir( null, false ); $path = ''; if ( 0 === strpos( $url, Bootstrap::url() ) ) { $path = Bootstrap::dir() . ltrim( substr( strtok( $url, '?' ), strlen( Bootstrap::url() ) ), '/' ); } elseif ( 0 === strpos( $url, $uploads[ 'baseurl' ] ) ) { $path = $uploads[ 'basedir' ] . substr( strtok( $url, '?' ), strlen( $uploads[ 'baseurl' ] ) ); } if ( '' === $path || ! is_readable( $path ) ) { return $info; } $key = md5( '2|' . $path . filemtime( $path ) ); $cached = get_transient( 'deadbolt_logo_info' ); if ( is_array( $cached ) && ( $cached[ 'key' ] ?? '' ) === $key ) { return array_merge( $info, (array) $cached[ 'info' ] ); } $type = wp_check_filetype( $path )[ 'type' ]; $raw = filesize( $path ) <= 24 * KB_IN_BYTES ? file_get_contents( $path ) : false; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- reads a local file. if ( $raw && $type && 0 === strpos( $type, 'image/' ) ) { $info[ 'data' ] = 'data:' . $type . ';base64,' . base64_encode( $raw ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- embeds a small local image. } $size = wp_getimagesize( $path ); if ( $size ) { $info[ 'width' ] = (int) $size[ 0 ]; $info[ 'height' ] = (int) $size[ 1 ]; } $data = filesize( $path ) <= 2 * MB_IN_BYTES && function_exists( 'imagecreatefromstring' ) ? file_get_contents( $path ) : false; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- reads a local file. $source = $data ? @imagecreatefromstring( $data ) : false; // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- unsupported image data returns false. if ( $source ) { $sample = 32; $small = imagecreatetruecolor( $sample, $sample ); imagealphablending( $small, false ); imagesavealpha( $small, true ); imagefill( $small, 0, 0, imagecolorallocatealpha( $small, 0, 0, 0, 127 ) ); imagecopyresampled( $small, $source, 0, 0, 0, 0, $sample, $sample, imagesx( $source ), imagesy( $source ) ); $total = 0.0; $weight = 0.0; for ( $y = 0; $y < $sample; $y++ ) { for ( $x = 0; $x < $sample; $x++ ) { $rgba = imagecolorat( $small, $x, $y ); $alpha = 1 - ( ( $rgba >> 24 ) & 127 ) / 127; $total += self::luminance( ( $rgba >> 16 ) & 255, ( $rgba >> 8 ) & 255, $rgba & 255 ) * $alpha; $weight += $alpha; } } $info[ 'lum' ] = $weight > 0 ? $total / $weight : null; } set_transient( 'deadbolt_logo_info', [ 'key' => $key, 'info' => $info ], MONTH_IN_SECONDS ); return $info; } // End logo_info() /** * Check whether the logo has too little contrast against the header and should be inverted. * * @param string $url * @param float|null $lum * @return bool */ private static function logo_needs_invert( string $url, ?float $lum ) : bool { if ( null === $lum ) { return (bool) apply_filters( 'deadbolt_invert_logo', false, $url ); } $hex = ltrim( self::header_color(), '#' ); $hex = 3 === strlen( $hex ) ? preg_replace( '/(.)/', '$1$1', $hex ) : $hex; $bg = self::luminance( (int) hexdec( substr( $hex, 0, 2 ) ), (int) hexdec( substr( $hex, 2, 2 ) ), (int) hexdec( substr( $hex, 4, 2 ) ) ); $ratio = ( max( $lum, $bg ) + 0.05 ) / ( min( $lum, $bg ) + 0.05 ); return (bool) apply_filters( 'deadbolt_invert_logo', $ratio < 3, $url ); } // End logo_needs_invert() /** * Preload the logo so it is ready when the header renders. * * @return void */ public function preload_logo() : void { $logo = self::logo_url(); if ( ! Helpers::is_plugin_screen() || self::logo_info( $logo )[ 'data' ] ) { return; } ?>