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;
}
}