settings = $settings; if ( !$settings->get( 'login.enabled', false ) ) { return; } add_action( 'login_enqueue_scripts', [$this, 'enqueue'] ); add_filter( 'login_body_class', [$this, 'bodyClass'] ); add_action( 'login_form', [$this, 'interimField'] ); } /** * The "session expired" dialog inside wp-admin (wp-login.php?interim-login=1). */ private static function interim() : bool { // Read-only: which layout core's login form uses, so no nonce. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only. return isset( $_REQUEST['interim-login'] ); } /** * 'light' or 'dark': the admin theme of the page that opened the dialog, * passed by Theme::authCheckStyle. */ private static function interimStyle() : string { // Read-only: picks the dialog's colours, so no nonce. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only. $style = ( isset( $_REQUEST['fd-style'] ) ? sanitize_key( wp_unslash( (string) $_REQUEST['fd-style'] ) ) : '' ); return ( $style === 'light' ? 'light' : 'dark' ); } /** * Keeps the dialog's style after a failed attempt (the form posts * without the iframe's query string). */ public function interimField() : void { if ( self::interim() ) { printf( '', esc_attr( self::interimStyle() ) ); } } /** * Footer of the Login settings card. */ public static function renderPreviewLink() : void { printf( '%s%s', esc_url( wp_login_url() ), esc_html__( 'Open the login screen', 'featherdash' ), esc_html__( 'Save your changes first; the login form shows even while you are logged in.', 'featherdash' ) ); } public function enqueue() : void { if ( !file_exists( FEATHERDASH_DIR . 'dist/login.css' ) ) { return; } wp_enqueue_style( 'featherdash-login', FEATHERDASH_URL . 'dist/login.css', ['login'], (string) filemtime( FEATHERDASH_DIR . 'dist/login.css' ) ); wp_add_inline_style( 'featherdash-login', $this->inlineCss() ); } /** * @param array $classes * * @return array */ public function bodyClass( array $classes ) : array { $classes[] = 'fd-login'; if ( $this->logo() !== null ) { $classes[] = 'fd-login-has-logo'; } if ( self::interim() ) { $classes[] = 'fd-login-interim'; } return $classes; } private function inlineCss() : string { $accent = (string) $this->settings->get( 'admin.theme.accent', '#0fc0c0' ); $background = (string) $this->settings->get( 'login.background', '#1e1e1e' ); $vars = [ '--fd-accent' => $accent, '--fd-login-bg' => $background, '--fd-login-text' => ( self::isLight( $background ) ? '#1e1e1e' : '#ffffff' ), ]; $image = false; // The session-expired dialog sits on the admin: a plain background // matching its theme, no image. if ( self::interim() ) { $light = self::interimStyle() === 'light'; $vars['--fd-login-bg'] = ( $light ? '#ebebeb' : '#1d1e1e' ); $vars['--fd-login-text'] = ( $light ? '#1e1e1e' : '#ffffff' ); $image = false; } if ( is_string( $image ) && $image !== '' ) { $vars['--fd-login-bg-image'] = 'url("' . esc_url_raw( $image ) . '")'; } $logo = $this->logo(); if ( $logo !== null ) { $vars['--fd-login-logo'] = 'url("' . esc_url_raw( $logo['url'] ) . '")'; $vars['--fd-login-logo-w'] = $logo['width'] . 'px'; $vars['--fd-login-logo-h'] = $logo['height'] . 'px'; } $css = ''; foreach ( $vars as $name => $value ) { $css .= $name . ':' . $value . ';'; } return 'body.login.fd-login{' . $css . '}'; } /** * The logo URL with its display size, scaled to fit LOGO_MAX_W × LOGO_MAX_H. * * @return array{url: string, width: int, height: int}|null */ private function logo() : ?array { $id = (int) $this->settings->get( 'login.logo', 0 ); $image = ( $id > 0 ? wp_get_attachment_image_src( $id, 'full' ) : false ); if ( !is_array( $image ) || !is_string( $image[0] ) || $image[0] === '' ) { return null; } $width = (int) $image[1]; $height = (int) $image[2]; // SVGs (and some CDN setups) report no dimensions. if ( $width <= 0 || $height <= 0 ) { return [ 'url' => $image[0], 'width' => self::LOGO_MAX_W, 'height' => 84, ]; } $scale = min( 1, self::LOGO_MAX_W / $width, self::LOGO_MAX_H / $height ); return [ 'url' => $image[0], 'width' => (int) round( $width * $scale ), 'height' => (int) round( $height * $scale ), ]; } /** * Relative luminance check for a #rrggbb colour. */ private static function isLight( string $hex ) : bool { if ( sscanf( $hex, '#%02x%02x%02x', $r, $g, $b ) !== 3 ) { return false; } $channel = static function ( int $value ) : float { $c = $value / 255; return ( $c <= 0.03928 ? $c / 12.92 : (($c + 0.055) / 1.055) ** 2.4 ); }; return 0.2126 * $channel( $r ) + 0.7151999999999999 * $channel( $g ) + 0.0722 * $channel( $b ) > 0.4; } }