Current Path : /storage/v11800/andslite-new/public_html/wp-content/plugins/amp/src/ |
Linux v11800 5.3.0-1023-aws #25~18.04.1-Ubuntu SMP Fri Jun 5 15:19:18 UTC 2020 aarch64
|
Current File : /storage/v11800/andslite-new/public_html/wp-content/plugins/amp/src/ReaderThemeLoader.php |
<?php
/**
* Class ReaderThemeLoader.
*
* @package AmpProject\AmpWP
*/
namespace AmpProject\AmpWP;
use AmpProject\AmpWP\Infrastructure\Registerable;
use AmpProject\AmpWP\Infrastructure\Service;
use AMP_Options_Manager;
use AMP_Theme_Support;
use AmpProject\AmpWP\Admin\ReaderThemes;
use WP_Theme;
use WP_Customize_Manager;
/**
* Switches to the designated Reader theme when template mode enabled and when requesting an AMP page.
*
* This class does not implement Conditional because other services need to always be able to access this
* service in order to determine whether or a Reader theme is loaded, and if so, what the previously-active theme was.
*
* @package AmpProject\AmpWP
* @since 2.0
* @internal
*/
final class ReaderThemeLoader implements Service, Registerable {
/**
* Paired routing service.
*
* @var PairedRouting
*/
private $paired_routing;
/**
* Reader theme.
*
* @var WP_Theme|null
*/
private $reader_theme;
/**
* Active theme.
*
* Theme which was active before switching to the Reader theme.
*
* @var WP_Theme
*/
private $active_theme;
/**
* Whether the active theme was overridden with the Reader theme.
*
* @var bool
*/
private $theme_overridden = false;
/**
* ReaderThemeLoader constructor.
*
* @param PairedRouting $paired_routing Paired routing service.
*/
public function __construct( PairedRouting $paired_routing ) {
$this->paired_routing = $paired_routing;
}
/**
* Is Reader mode with a Reader theme selected.
*
* @param array $options Options to check. If omitted, the currently-saved options are used.
* @return bool Whether new Reader mode.
*/
public function is_enabled( $options = null ) {
// If the theme was overridden then we know it is enabled. We can't check get_template() at this point because
// it will be identical to $reader_theme.
if ( $this->is_theme_overridden() ) {
return true;
}
if ( null === $options ) {
$options = AMP_Options_Manager::get_options();
}
// If Reader mode is not enabled, then a Reader theme is definitely not going to be served.
if (
! isset( $options[ Option::THEME_SUPPORT ] )
||
AMP_Theme_Support::READER_MODE_SLUG !== $options[ Option::THEME_SUPPORT ]
) {
return false;
}
// If the Legacy Reader mode is active, then a Reader theme is not going to be served.
if ( ! isset( $options[ Option::READER_THEME ] ) ) {
return false;
}
$reader_theme = $options[ Option::READER_THEME ];
if ( ReaderThemes::DEFAULT_READER_THEME === $reader_theme ) {
return false;
}
// If the Reader theme does not exist, then we cannot switch to the reader theme. The Legacy Reader theme will
// be used as a fallback instead.
if ( ! wp_get_theme( $reader_theme )->exists() ) {
return false;
}
// Lastly, if the active theme is not the same as the reader theme, then we can switch to the reader theme.
// Otherwise, the site should instead be in Transitional mode. Note that get_stylesheet() is used as opposed
// to get_template() because the active theme should be allowed to be a child theme of a Reader theme.
return get_stylesheet() !== $reader_theme;
}
/**
* Whether the active theme was overridden with the reader theme.
*
* @return bool Whether theme overridden.
*/
public function is_theme_overridden() {
return $this->theme_overridden;
}
/**
* Register the service with the system.
*
* @return void
*/
public function register() {
// The following needs to run at plugins_loaded because that is when _wp_customize_include runs. Otherwise, the
// most logical action would be setup_theme.
add_action( 'plugins_loaded', [ $this, 'override_theme' ], 9 );
add_filter( 'wp_prepare_themes_for_js', [ $this, 'filter_wp_prepare_themes_to_indicate_reader_theme' ] );
add_action( 'admin_print_footer_scripts-themes.php', [ $this, 'inject_theme_single_template_modifications' ] );
}
/**
* Filter themes for JS to remove action to delete the selected Reader theme and show a notice.
*
* @param array $prepared_themes Array of theme data.
* @return array Themes.
*/
public function filter_wp_prepare_themes_to_indicate_reader_theme( $prepared_themes ) {
if ( ! $this->is_enabled() ) {
return $prepared_themes;
}
$reader_theme_obj = $this->get_reader_theme();
if ( ! $reader_theme_obj instanceof WP_Theme ) {
return $prepared_themes;
}
$reader_theme = $reader_theme_obj->get_stylesheet();
if ( isset( $prepared_themes[ $reader_theme ] ) ) {
// Make sure the AMP Reader theme appears right after the active theme in the list.
$stylesheet = get_stylesheet();
if ( isset( $prepared_themes[ $stylesheet ] ) ) {
$prepared_themes = array_merge(
[
$stylesheet => $prepared_themes[ $stylesheet ],
$reader_theme => $prepared_themes[ $reader_theme ],
],
$prepared_themes
);
}
// Prevent Reader theme from being deleted.
unset( $prepared_themes[ $reader_theme ]['actions']['delete'] );
// Make sure the Customize link goes to AMP.
$prepared_themes[ $reader_theme ]['actions']['customize'] = amp_get_customizer_url();
// Force the theme to be styled as Active.
$prepared_themes[ $reader_theme ]['active'] = true;
// Make sure the hacked Backbone template will use the AMP action links.
$prepared_themes[ $reader_theme ]['ampActiveReaderTheme'] = true;
// Add AMP Reader theme notice.
$prepared_themes[ $reader_theme ]['ampReaderThemeNotice'] = sprintf(
wp_kses(
/* translators: placeholder is link to AMP settings screen */
__( 'This has been <a href="%s">selected</a> as the AMP Reader theme.', 'amp' ),
[
'a' => [ 'href' => true ],
]
),
esc_url( add_query_arg( 'page', AMP_Options_Manager::OPTION_NAME, admin_url( 'admin.php' ) ) . '#reader-themes' )
);
}
return $prepared_themes;
}
/**
* Inject new logic into the Backbone templates for rendering a theme lightbox.
*
* This is admittedly hacky, but WordPress doesn't provide a much better option.
*/
public function inject_theme_single_template_modifications() {
if ( ! $this->is_enabled() ) {
return;
}
$reader_theme = $this->get_reader_theme();
if ( ! $reader_theme instanceof WP_Theme ) {
return;
}
?>
<script>
(function( themeSingleTmpl ) {
if ( ! themeSingleTmpl ) {
return;
}
let text = themeSingleTmpl.text;
text = text.replace(
/(?=<p class="theme-description">)/,
`
<# if ( data.ampReaderThemeNotice ) { #>
<div class="notice notice-info notice-alt inline">
<p>{{{ data.ampReaderThemeNotice }}}</p><?php // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation -- Contains link and already sanitized with Kses. ?>
</div>
<# } #>
`
);
// Inject our action links.
text = text.replace(
/(<div class="active-theme">)((?:.|\s)+?)(<\/div>)/,
( match, startDiv, actionLinks, endDiv ) => {
return `
${startDiv}
<# if ( data.ampActiveReaderTheme ) { #>
<a href="{{ data.actions.customize }}" class="button button-primary customize load-customize hide-if-no-customize">
<?php esc_html_e( 'Customize', 'default' ); ?>
</a>
<a href="<?php echo esc_url( add_query_arg( 'page', AMP_Options_Manager::OPTION_NAME, admin_url( 'admin.php' ) ) ); ?>" class="button button-secondary">
<?php esc_html_e( 'AMP Settings', 'amp' ); ?>
</a>
<# } else { #>
${actionLinks}
<# } #>
${endDiv}
`;
}
);
themeSingleTmpl.text = text;
})( document.getElementById( 'tmpl-theme-single' ) );
(function ( themeTmpl, ssrThemeNamePrefixElement ) {
// First update the card that was rendered server-side in wp-admin/themes.php.
if ( ssrThemeNamePrefixElement ) {
ssrThemeNamePrefixElement.textContent = <?php echo wp_json_encode( _x( 'Reader:', 'prefix for theme card in list', 'amp' ) ); ?>;
}
// Then update the Mustache template so that future renders will also have the proper prefix.
if ( themeTmpl ) {
themeTmpl.text = themeTmpl.text.replace(
/(<div class="theme-id-container">)(\s*<# if)/,
function ( match, startDiv ) {
return `
${startDiv}
<# if ( data.ampActiveReaderTheme ) { #>
<h2 class="theme-name" id="{{ data.id }}-name">
<span><?php echo esc_html( _x( 'Reader:', 'prefix for theme card in list', 'amp' ) ); ?></span> {{ data.name }}
</h2>
<# } else if
`;
}
);
}
}) (
document.getElementById( 'tmpl-theme' ),
document.querySelector( <?php echo wp_json_encode( sprintf( '#%s-name > span', $reader_theme->get_stylesheet() ) ); ?> )
);
</script>
<?php
}
/**
* Get reader theme.
*
* If the Reader template mode is enabled
*
* @return WP_Theme|null Theme if selected and no errors.
*/
public function get_reader_theme() {
if ( $this->reader_theme instanceof WP_Theme ) {
return $this->reader_theme;
}
$reader_theme_slug = AMP_Options_Manager::get_option( Option::READER_THEME );
if ( ! $reader_theme_slug ) {
return null;
}
$reader_theme = wp_get_theme( $reader_theme_slug );
if ( $reader_theme->errors() ) {
return null;
}
return $reader_theme;
}
/**
* Get active theme.
*
* The theme that was active before switching to the Reader theme.
*
* @return WP_Theme WP_Theme instance.
*/
public function get_active_theme() {
return $this->active_theme;
}
/**
* Switch theme if in Reader mode, a Reader theme was selected, and the AMP query var is present.
*
* Note that AMP_Theme_Support will redirect to the non-AMP version if AMP is not available for the query.
*
* @see WP_Customize_Manager::start_previewing_theme() which provides for much of the inspiration here.
* @see switch_theme() which ensures the new theme includes the old theme's theme mods.
*/
public function override_theme() {
$this->theme_overridden = false;
if ( ! $this->is_enabled() || ! $this->paired_routing->has_endpoint() ) {
return;
}
$theme = $this->get_reader_theme();
if ( ! $theme instanceof WP_Theme ) {
return;
}
$this->active_theme = wp_get_theme();
$this->reader_theme = $theme;
$this->theme_overridden = true;
$get_template = function () {
return $this->reader_theme->get_template();
};
$get_stylesheet = function () {
return $this->reader_theme->get_stylesheet();
};
add_filter( 'stylesheet', $get_stylesheet );
add_filter( 'template', $get_template );
add_filter(
'pre_option_current_theme',
function () {
return $this->reader_theme->display( 'Name' );
}
);
// @link: https://core.trac.wordpress.org/ticket/20027
add_filter( 'pre_option_stylesheet', $get_stylesheet );
add_filter( 'pre_option_template', $get_template );
// Handle custom theme roots.
add_filter(
'pre_option_stylesheet_root',
function () {
return get_raw_theme_root( $this->reader_theme->get_stylesheet(), true );
}
);
add_filter(
'pre_option_template_root',
function () {
return get_raw_theme_root( $this->reader_theme->get_template(), true );
}
);
$this->disable_widgets();
add_filter( 'customize_previewable_devices', [ $this, 'customize_previewable_devices' ] );
add_action( 'customize_register', [ $this, 'remove_customizer_themes_panel' ], 11 );
}
/**
* Disable widgets.
*/
public function disable_widgets() {
add_filter( 'sidebars_widgets', '__return_empty_array', PHP_INT_MAX );
add_filter(
'customize_loaded_components',
static function( $components ) {
return array_diff( $components, [ 'widgets' ] );
}
);
remove_theme_support( 'widgets-block-editor' );
}
/**
* Make tablet (smartphone) the default device when opening AMP Customizer.
*
* @param array $devices Devices.
* @return array Devices.
*/
public function customize_previewable_devices( $devices ) {
if ( isset( $devices['tablet'] ) ) {
unset( $devices['desktop']['default'] );
$devices['tablet']['default'] = true;
}
return $devices;
}
/**
* Remove themes panel from AMP Customizer.
*
* @param WP_Customize_Manager $wp_customize Customize manager.
*/
public function remove_customizer_themes_panel( WP_Customize_Manager $wp_customize ) {
$wp_customize->remove_panel( 'themes' );
}
}