[ self::KEY_SLUG, self::KEY_COLOR ],
self::FEATURE_EDITOR_GRADIENT_PRESETS => [ self::KEY_SLUG, self::KEY_GRADIENT ],
self::FEATURE_EDITOR_FONT_SIZES => [ self::KEY_SLUG, self::KEY_SIZE ],
];
/**
* Reader theme loader.
*
* @var ReaderThemeLoader
*/
private $reader_theme_loader;
/**
* ReaderThemeLoader constructor.
*
* @param ReaderThemeLoader $reader_theme_loader Reader theme loader.
*/
public function __construct( ReaderThemeLoader $reader_theme_loader ) {
$this->reader_theme_loader = $reader_theme_loader;
}
/**
* Register the service with the system.
*
* @return void
*/
public function register() {
add_filter( 'amp_options_updating', [ $this, 'filter_amp_options_updating' ] );
add_action( 'after_switch_theme', [ $this, 'handle_theme_update' ] );
add_action( self::ACTION_UPDATE_CACHED_PRIMARY_THEME_SUPPORT, [ $this, 'update_cached_theme_support' ] );
add_action(
'upgrader_process_complete',
function ( $upgrader ) {
if ( $upgrader instanceof Theme_Upgrader ) {
$this->update_cached_theme_support();
}
}
);
add_action( 'amp_post_template_head', [ $this, 'print_theme_support_styles' ] );
add_action(
'wp_head',
[ $this, 'print_theme_support_styles' ],
9 // Because wp_print_styles happens at priority 8, and we want the primary theme's colors to override any conflicting theme color assignments.
);
}
/**
* Check whether all the required props are present for a given feature item.
*
* @param string $feature Feature name.
* @param array $props Props to check.
*
* @return bool Whether all are present.
*/
public function has_required_feature_props( $feature, $props ) {
if ( empty( $props ) || ! is_array( $props ) ) {
return false;
}
foreach ( self::SUPPORTED_FEATURES[ $feature ] as $required_prop ) {
if ( ! array_key_exists( $required_prop, $props ) ) {
return false;
}
}
return true;
}
/**
* Filter the AMP options when they are updated to add the primary theme's features.
*
* @param array $options Options.
* @return array Options.
*/
public function filter_amp_options_updating( $options ) {
if ( $this->reader_theme_loader->is_enabled( $options ) ) {
$options[ Option::PRIMARY_THEME_SUPPORT ] = $this->get_theme_support_features( true );
} else {
$options[ Option::PRIMARY_THEME_SUPPORT ] = null;
}
return $options;
}
/**
* Handle updating the cached primary_theme_support after updating/switching theme.
*
* In the case of switching the theme via WP-CLI, it could be that the next request is for an AMP page and
* the `check_theme_switched()` function will run in the context of a Reader theme being loaded. In that case,
* the added theme support won't be for the primary theme and we need to schedule an immediate event in WP-Cron to
* try again in the context of a cron request in which a Reader theme will never be overriding the primary theme.
*/
public function handle_theme_update() {
if ( $this->reader_theme_loader->is_theme_overridden() ) {
wp_schedule_single_event( time(), self::ACTION_UPDATE_CACHED_PRIMARY_THEME_SUPPORT );
} else {
$this->update_cached_theme_support();
}
}
/**
* Update primary theme's cached theme support.
*/
public function update_cached_theme_support() {
if ( $this->reader_theme_loader->is_enabled() ) {
AMP_Options_Manager::update_option( Option::PRIMARY_THEME_SUPPORT, $this->get_theme_support_features( true ) );
} else {
AMP_Options_Manager::update_option( Option::PRIMARY_THEME_SUPPORT, null );
}
}
/**
* Get the theme support features.
*
* @param bool $reduced Whether to reduce the feature props down to just what is required.
* @return array Theme support features.
*/
public function get_theme_support_features( $reduced = false ) {
$features = [];
foreach ( array_keys( self::SUPPORTED_FEATURES ) as $feature_key ) {
$feature_value = current( (array) get_theme_support( $feature_key ) );
if ( ! is_array( $feature_value ) || empty( $feature_value ) ) {
continue;
}
if ( $reduced ) {
$features[ $feature_key ] = [];
foreach ( $feature_value as $item ) {
if ( $this->has_required_feature_props( $feature_key, $item ) ) {
$features[ $feature_key ][] = wp_array_slice_assoc( $item, self::SUPPORTED_FEATURES[ $feature_key ] );
}
}
} else {
$features[ $feature_key ] = $feature_value;
}
}
return $features;
}
/**
* Determines whether the request is for an AMP page in Reader mode.
*
* @return bool Whether AMP Reader request.
*/
public function is_reader_request() {
return (
( amp_is_legacy() || $this->reader_theme_loader->is_theme_overridden() )
&&
amp_is_request()
);
}
/**
* Print theme support styles.
*/
public function print_theme_support_styles() {
if ( ! $this->is_reader_request() ) {
return;
}
$features = [];
if ( $this->reader_theme_loader->is_enabled() ) {
$features = AMP_Options_Manager::get_option( Option::PRIMARY_THEME_SUPPORT );
} elseif ( amp_is_legacy() ) {
$features = $this->get_theme_support_features();
}
if ( empty( $features ) ) {
return;
}
foreach ( array_keys( self::SUPPORTED_FEATURES ) as $feature ) {
if ( empty( $features[ $feature ] ) || ! is_array( $features[ $feature ] ) ) {
continue;
}
$value = $features[ $feature ];
switch ( $feature ) {
case self::FEATURE_EDITOR_COLOR_PALETTE:
$this->print_editor_color_palette_styles( $value );
break;
case self::FEATURE_EDITOR_FONT_SIZES:
$this->print_editor_font_sizes_styles( $value );
break;
case self::FEATURE_EDITOR_GRADIENT_PRESETS:
$this->print_editor_gradient_presets_styles( $value );
break;
}
}
}
/**
* Print editor-color-palette styles.
*
* @param array $color_palette Color palette.
*/
private function print_editor_color_palette_styles( array $color_palette ) {
echo '';
}
/**
* Print editor-font-sizes styles.
*
* @param array $font_sizes Font sizes.
*/
private function print_editor_font_sizes_styles( array $font_sizes ) {
echo '';
}
/**
* Print editor-gradient-presets styles.
*
* @param array $gradient_presets Gradient presets.
*/
private function print_editor_gradient_presets_styles( array $gradient_presets ) {
echo '';
}
/**
* Get relative luminance from color hex value.
*
* Copied from `\Twenty_Twenty_One_Custom_Colors::get_relative_luminance_from_hex()`.
*
* @see https://github.com/WordPress/wordpress-develop/blob/acbbbd18b32b5429264622141a6d058b64f3a5ad/src/wp-content/themes/twentytwentyone/classes/class-twenty-twenty-one-custom-colors.php#L138-L156
*
* @param string $hex Color hex value.
* @return int Relative luminance value.
*/
public function get_relative_luminance_from_hex( $hex ) {
// Remove the "#" symbol from the beginning of the color.
$hex = ltrim( $hex, '#' );
// Make sure there are 6 digits for the below calculations.
if ( 3 === strlen( $hex ) ) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
// Get red, green, blue.
$red = hexdec( substr( $hex, 0, 2 ) );
$green = hexdec( substr( $hex, 2, 2 ) );
$blue = hexdec( substr( $hex, 4, 2 ) );
// Calculate the luminance.
$lum = ( 0.2126 * $red ) + ( 0.7152 * $green ) + ( 0.0722 * $blue );
return (int) round( $lum );
}
}