PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@@ -0,0 +1,92 @@
<?php
/**
* Class AMP_Analytics_Options_Submenu
*
* @package AMP
*/
/**
* Class AMP_Analytics_Options_Submenu
*/
class AMP_Analytics_Options_Submenu {
/**
* Parent menu slug for the submenu.
*
* @var string
*/
private $parent_menu_slug;
/**
* Slug for the submenu.
*
* @var string
*/
private $menu_slug;
/**
* Menu page instance for rendering the content.
*
* @var AMP_Analytics_Options_Submenu_Page
*/
private $menu_page;
/**
* Constructor.
*
* @param string $parent_menu_slug Slug of the parent menu item.
*/
public function __construct( $parent_menu_slug ) {
$this->parent_menu_slug = $parent_menu_slug;
$this->menu_slug = 'amp-analytics-options';
$this->menu_page = new AMP_Analytics_Options_Submenu_Page();
}
/**
* Adds the submenu item and adds necessary hooks.
*/
public function init() {
$this->add_submenu();
add_action(
'admin_print_styles-amp_page_' . $this->menu_slug,
array( $this, 'amp_options_styles' )
);
}
/**
* Adds the submenu page and registers the rendering callback.
*/
private function add_submenu() {
add_submenu_page(
$this->parent_menu_slug,
__( 'AMP Analytics Options', 'amp' ),
__( 'Analytics', 'amp' ),
'manage_options',
$this->menu_slug,
array( $this->menu_page, 'render' )
);
}
/**
* Prints extra styles for the page content.
*/
public function amp_options_styles() {
?>
<style>
.analytics-data-container .button.delete,
.analytics-data-container .button.delete:hover,
.analytics-data-container .button.delete:active,
.analytics-data-container .button.delete:focus {
background: red;
border-color: red;
text-shadow: 0 0 0;
margin: 0 5px;
}
.amp-analytics-options.notice {
width: 300px;
}
</style>
<?php
}
}

View File

@@ -0,0 +1,665 @@
<?php
/**
* Class AMP_Options_Manager.
*
* @package AMP
*/
/**
* Class AMP_Options_Manager
*/
class AMP_Options_Manager {
/**
* Option name.
*
* @var string
*/
const OPTION_NAME = 'amp-options';
/**
* Default option values.
*
* @var array
*/
protected static $defaults = array(
'theme_support' => 'disabled',
'supported_post_types' => array( 'post' ),
'analytics' => array(),
'auto_accept_sanitization' => true,
'accept_tree_shaking' => true,
'disable_admin_bar' => false,
'all_templates_supported' => true,
'supported_templates' => array( 'is_singular' ),
'enable_response_caching' => true,
'version' => AMP__VERSION,
);
/**
* Register settings.
*/
public static function register_settings() {
register_setting(
self::OPTION_NAME,
self::OPTION_NAME,
array(
'type' => 'array',
'sanitize_callback' => array( __CLASS__, 'validate_options' ),
)
);
add_action( 'update_option_' . self::OPTION_NAME, array( __CLASS__, 'maybe_flush_rewrite_rules' ), 10, 2 );
add_action( 'admin_notices', array( __CLASS__, 'render_welcome_notice' ) );
add_action( 'admin_notices', array( __CLASS__, 'persistent_object_caching_notice' ) );
add_action( 'admin_notices', array( __CLASS__, 'render_cache_miss_notice' ) );
add_action( 'admin_notices', array( __CLASS__, 'render_php_css_parser_conflict_notice' ) );
}
/**
* Flush rewrite rules if the supported_post_types have changed.
*
* @since 0.6.2
*
* @param array $old_options Old options.
* @param array $new_options New options.
*/
public static function maybe_flush_rewrite_rules( $old_options, $new_options ) {
$old_post_types = isset( $old_options['supported_post_types'] ) ? $old_options['supported_post_types'] : array();
$new_post_types = isset( $new_options['supported_post_types'] ) ? $new_options['supported_post_types'] : array();
sort( $old_post_types );
sort( $new_post_types );
if ( $old_post_types !== $new_post_types ) {
flush_rewrite_rules( false );
}
}
/**
* Get plugin options.
*
* @return array Options.
*/
public static function get_options() {
$options = get_option( self::OPTION_NAME, array() );
if ( empty( $options ) ) {
$options = array();
}
self::$defaults['enable_response_caching'] = wp_using_ext_object_cache();
return array_merge( self::$defaults, $options );
}
/**
* Get plugin option.
*
* @param string $option Plugin option name.
* @param bool $default Default value.
*
* @return mixed Option value.
*/
public static function get_option( $option, $default = false ) {
$amp_options = self::get_options();
if ( ! isset( $amp_options[ $option ] ) ) {
return $default;
}
return $amp_options[ $option ];
}
/**
* Validate options.
*
* @param array $new_options Plugin options.
* @return array Options.
*/
public static function validate_options( $new_options ) {
$options = self::get_options();
if ( ! current_user_can( 'manage_options' ) ) {
return $options;
}
// Theme support.
$recognized_theme_supports = array(
'disabled',
'paired',
'native',
);
if ( isset( $new_options['theme_support'] ) && in_array( $new_options['theme_support'], $recognized_theme_supports, true ) ) {
$options['theme_support'] = $new_options['theme_support'];
// If this option was changed, display a notice with the new template mode.
if ( self::get_option( 'theme_support' ) !== $new_options['theme_support'] ) {
add_action( 'update_option_' . self::OPTION_NAME, array( __CLASS__, 'handle_updated_theme_support_option' ) );
}
}
$options['auto_accept_sanitization'] = ! empty( $new_options['auto_accept_sanitization'] );
$options['accept_tree_shaking'] = ! empty( $new_options['accept_tree_shaking'] );
$options['disable_admin_bar'] = ! empty( $new_options['disable_admin_bar'] );
// Validate post type support.
$options['supported_post_types'] = array();
if ( isset( $new_options['supported_post_types'] ) ) {
foreach ( $new_options['supported_post_types'] as $post_type ) {
if ( ! post_type_exists( $post_type ) ) {
add_settings_error( self::OPTION_NAME, 'unknown_post_type', __( 'Unrecognized post type.', 'amp' ) );
} else {
$options['supported_post_types'][] = $post_type;
}
}
}
$theme_support_args = AMP_Theme_Support::get_theme_support_args();
$is_template_support_required = ( isset( $theme_support_args['templates_supported'] ) && 'all' === $theme_support_args['templates_supported'] );
if ( ! $is_template_support_required && ! isset( $theme_support_args['available_callback'] ) ) {
$options['all_templates_supported'] = ! empty( $new_options['all_templates_supported'] );
// Validate supported templates.
$options['supported_templates'] = array();
if ( isset( $new_options['supported_templates'] ) ) {
$options['supported_templates'] = array_intersect(
$new_options['supported_templates'],
array_keys( AMP_Theme_Support::get_supportable_templates() )
);
}
}
// Validate analytics.
if ( isset( $new_options['analytics'] ) ) {
foreach ( $new_options['analytics'] as $id => $data ) {
// Check save/delete pre-conditions and proceed if correct.
if ( empty( $data['type'] ) || empty( $data['config'] ) ) {
add_settings_error( self::OPTION_NAME, 'missing_analytics_vendor_or_config', __( 'Missing vendor type or config.', 'amp' ) );
continue;
}
// Validate JSON configuration.
$is_valid_json = AMP_HTML_Utils::is_valid_json( $data['config'] );
if ( ! $is_valid_json ) {
add_settings_error( self::OPTION_NAME, 'invalid_analytics_config_json', __( 'Invalid analytics config JSON.', 'amp' ) );
continue;
}
$entry_vendor_type = preg_replace( '/[^a-zA-Z0-9_\-]/', '', $data['type'] );
$entry_config = trim( $data['config'] );
if ( ! empty( $data['id'] ) && '__new__' !== $data['id'] ) {
$entry_id = sanitize_key( $data['id'] );
} else {
// Generate a hash string to uniquely identify this entry.
$entry_id = substr( md5( $entry_vendor_type . $entry_config ), 0, 12 );
// Avoid duplicates.
if ( isset( $options['analytics'][ $entry_id ] ) ) {
add_settings_error( self::OPTION_NAME, 'duplicate_analytics_entry', __( 'Duplicate analytics entry found.', 'amp' ) );
continue;
}
}
if ( isset( $data['delete'] ) ) {
unset( $options['analytics'][ $entry_id ] );
} else {
$options['analytics'][ $entry_id ] = array(
'type' => $entry_vendor_type,
'config' => $entry_config,
);
}
}
}
// Store the current version with the options so we know the format.
$options['version'] = AMP__VERSION;
// Handle the caching option.
$options['enable_response_caching'] = (
wp_using_ext_object_cache()
&&
! empty( $new_options['enable_response_caching'] )
);
if ( $options['enable_response_caching'] ) {
AMP_Theme_Support::reset_cache_miss_url_option();
}
return $options;
}
/**
* Check for errors with updating the supported post types.
*
* @since 0.6
* @see add_settings_error()
*/
public static function check_supported_post_type_update_errors() {
// If all templates are supported then skip check since all post types are also supported. This option only applies with native/transitional theme support.
if ( self::get_option( 'all_templates_supported', false ) && 'disabled' !== self::get_option( 'theme_support' ) ) {
return;
}
$supported_types = self::get_option( 'supported_post_types', array() );
foreach ( AMP_Post_Type_Support::get_eligible_post_types() as $name ) {
$post_type = get_post_type_object( $name );
if ( empty( $post_type ) ) {
continue;
}
$post_type_supported = post_type_supports( $post_type->name, AMP_Post_Type_Support::SLUG );
$is_support_elected = in_array( $post_type->name, $supported_types, true );
$error = null;
$code = null;
if ( $is_support_elected && ! $post_type_supported ) {
/* translators: %s: Post type name. */
$error = __( '"%s" could not be activated because support is removed by a plugin or theme', 'amp' );
$code = sprintf( '%s_activation_error', $post_type->name );
} elseif ( ! $is_support_elected && $post_type_supported ) {
/* translators: %s: Post type name. */
$error = __( '"%s" could not be deactivated because support is added by a plugin or theme', 'amp' );
$code = sprintf( '%s_deactivation_error', $post_type->name );
}
if ( isset( $error, $code ) ) {
add_settings_error(
self::OPTION_NAME,
$code,
sprintf(
$error,
isset( $post_type->label ) ? $post_type->label : $post_type->name
)
);
}
}
}
/**
* Update plugin option.
*
* @param string $option Plugin option name.
* @param mixed $value Plugin option value.
*
* @return bool Whether update succeeded.
*/
public static function update_option( $option, $value ) {
$amp_options = self::get_options();
$amp_options[ $option ] = $value;
return update_option( self::OPTION_NAME, $amp_options, false );
}
/**
* Handle analytics submission.
*/
public static function handle_analytics_submit() {
// Request must come from user with right capabilities.
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Sorry, you do not have the necessary permissions to perform this action', 'amp' ) );
}
// Ensure request is coming from analytics option form.
check_admin_referer( 'analytics-options', 'analytics-options' );
if ( isset( $_POST['amp-options']['analytics'] ) ) {
self::update_option( 'analytics', wp_unslash( $_POST['amp-options']['analytics'] ) );
$errors = get_settings_errors( self::OPTION_NAME );
if ( empty( $errors ) ) {
add_settings_error( self::OPTION_NAME, 'settings_updated', __( 'The analytics entry was successfully saved!', 'amp' ), 'updated' );
$errors = get_settings_errors( self::OPTION_NAME );
}
set_transient( 'settings_errors', $errors );
}
/*
* Redirect to keep the user in the analytics options page.
* Wrap in is_admin() to enable phpunit tests to exercise this code.
*/
wp_safe_redirect( admin_url( 'admin.php?page=amp-analytics-options&settings-updated=1' ) );
exit;
}
/**
* Update analytics options.
*
* @codeCoverageIgnore
* @deprecated
* @param array $data Unsanitized unslashed data.
* @return bool Whether options were updated.
*/
public static function update_analytics_options( $data ) {
_deprecated_function( __METHOD__, '0.6', __CLASS__ . '::update_option' );
return self::update_option( 'analytics', wp_unslash( $data ) );
}
/**
* Renders the welcome notice on the 'AMP Settings' page.
*
* Uses the user meta values for the dismissed WP pointers.
* So once the user dismisses this notice, it will never appear again.
*/
public static function render_welcome_notice() {
if ( 'toplevel_page_' . self::OPTION_NAME !== get_current_screen()->id ) {
return;
}
$notice_id = 'amp-welcome-notice-1';
$dismissed = get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true );
if ( in_array( $notice_id, explode( ',', strval( $dismissed ) ), true ) ) {
return;
}
?>
<div class="amp-welcome-notice notice notice-info is-dismissible" id="<?php echo esc_attr( $notice_id ); ?>">
<div class="notice-dismiss"></div>
<div class="amp-welcome-icon-holder">
<img class="amp-welcome-icon" src="<?php echo esc_url( amp_get_asset_url( 'images/amp-welcome-icon.svg' ) ); ?>" alt="<?php esc_html_e( 'Illustration of WordPress running AMP plugin.', 'amp' ); ?>" />
</div>
<h1><?php esc_html_e( 'Welcome to AMP for WordPress', 'amp' ); ?></h1>
<h3><?php esc_html_e( 'Bring the speed and features of the open source AMP project to your site, complete with the tools to support content authoring and website development.', 'amp' ); ?></h3>
<h3><?php esc_html_e( 'From granular controls that help you create AMP content, to Core Gutenberg support, to a sanitizer that only shows visitors error-free pages, to a full error workflow for developers, this release enables rich, performant experiences for your WordPress site.', 'amp' ); ?></h3>
<a href="https://amp-wp.org/getting-started/" target="_blank" class="button button-primary"><?php esc_html_e( 'Learn More', 'amp' ); ?></a>
</div>
<script>
jQuery( function( $ ) {
// On dismissing the notice, make a POST request to store this notice with the dismissed WP pointers so it doesn't display again.
$( <?php echo wp_json_encode( "#$notice_id" ); ?> ).on( 'click', '.notice-dismiss', function() {
$.post( ajaxurl, {
pointer: <?php echo wp_json_encode( $notice_id ); ?>,
action: 'dismiss-wp-pointer'
} );
} );
} );
</script>
<style type="text/css">
.amp-welcome-notice {
padding: 38px;
}
.amp-welcome-notice + .notice {
clear: both;
}
.amp-welcome-icon-holder {
width: 200px;
height: 200px;
float: left;
margin: 0 38px 38px 0;
}
.amp-welcome-icon {
width: 100%;
height: 100%;
display: block;
}
.amp-welcome-notice h1 {
font-weight: bold;
}
.amp-welcome-notice h3 {
font-size: 16px;
font-weight: 500;
}
</style>
<?php
}
/**
* Outputs an admin notice if persistent object cache is not present.
*
* @return void
*/
public static function persistent_object_caching_notice() {
if ( ! wp_using_ext_object_cache() && 'toplevel_page_' . self::OPTION_NAME === get_current_screen()->id ) {
printf(
'<div class="notice notice-warning"><p>%s</p></div>',
sprintf(
/* translators: %s: Persistent object cache support URL */
__( 'The AMP plugin performs at its best when persistent object cache is enabled. <a href="%s">More details</a>', 'amp' ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_url( __( 'https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching', 'amp' ) )
)
);
}
}
/**
* Render the cache miss admin notice.
*
* @return void
*/
public static function render_cache_miss_notice() {
if ( 'toplevel_page_' . self::OPTION_NAME !== get_current_screen()->id ) {
return;
}
if ( ! self::show_response_cache_disabled_notice() ) {
return;
}
printf(
'<div class="notice notice-warning is-dismissible"><p>%s</p></div>',
sprintf(
/* translators: %s: post-processor cache support URL */
__( 'The AMP plugin&lsquo;s post-processor cache was disabled due to the detection of highly-variable content. <a href="%s">More details</a>', 'amp' ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_url( __( 'https://github.com/ampproject/amp-wp/wiki/Post-Processor-Cache', 'amp' ) )
)
);
}
/**
* Render PHP-CSS-Parser conflict notice.
*
* @return void
*/
public static function render_php_css_parser_conflict_notice() {
if ( 'toplevel_page_' . self::OPTION_NAME !== get_current_screen()->id ) {
return;
}
if ( AMP_Style_Sanitizer::has_required_php_css_parser() ) {
return;
}
try {
$reflection = new ReflectionClass( 'Sabberworm\CSS\CSSList\CSSList' );
$source_dir = str_replace(
trailingslashit( WP_CONTENT_DIR ),
'',
preg_replace( '#/vendor/sabberworm/.+#', '', $reflection->getFileName() )
);
printf(
'<div class="notice notice-warning"><p>%s</p></div>',
sprintf(
/* translators: %s: path to the conflicting library */
__( 'A conflicting version of PHP-CSS-Parser appears to be installed by another plugin or theme (located in %s). Because of this, CSS processing will be limited, and tree shaking will not be available.', 'amp' ), // phpcs:ignore WordPress.Security.EscapeOutput
'<code>' . esc_html( $source_dir ) . '</code>'
)
);
} catch ( ReflectionException $e ) {
printf(
'<div class="notice notice-warning"><p>%s</p></div>',
esc_html__( 'PHP-CSS-Parser is not available so CSS processing will not be available.', 'amp' )
);
}
}
/**
* Show the response cache disabled notice.
*
* @since 1.0
*
* @return bool
*/
public static function show_response_cache_disabled_notice() {
return (
wp_using_ext_object_cache()
&&
! self::get_option( 'enable_response_caching' )
&&
AMP_Theme_Support::exceeded_cache_miss_threshold()
);
}
/**
* Adds a message for an update of the theme support setting.
*/
public static function handle_updated_theme_support_option() {
$template_mode = self::get_option( 'theme_support' );
// Make sure post type support has been added for sake of amp_admin_get_preview_permalink().
foreach ( AMP_Post_Type_Support::get_eligible_post_types() as $post_type ) {
remove_post_type_support( $post_type, AMP_Post_Type_Support::SLUG );
}
AMP_Post_Type_Support::add_post_type_support();
// Ensure theme support flags are set properly according to the new mode so that proper AMP URL can be generated.
$has_theme_support = ( 'native' === $template_mode || 'paired' === $template_mode );
if ( $has_theme_support ) {
$theme_support = current_theme_supports( AMP_Theme_Support::SLUG );
if ( ! is_array( $theme_support ) ) {
$theme_support = array();
}
$theme_support['paired'] = 'paired' === $template_mode;
add_theme_support( AMP_Theme_Support::SLUG, $theme_support );
} else {
remove_theme_support( AMP_Theme_Support::SLUG ); // So that the amp_get_permalink() will work for reader mode URL.
}
$url = amp_admin_get_preview_permalink();
$notice_type = 'updated';
$review_messages = array();
if ( $url && $has_theme_support ) {
$validation = AMP_Validation_Manager::validate_url( $url );
if ( is_wp_error( $validation ) ) {
$review_messages[] = esc_html(
sprintf(
/* translators: 1: error message. 2: error code. */
__( 'However, there was an error when checking the AMP validity for your site.', 'amp' ),
$validation->get_error_message(),
$validation->get_error_code()
)
);
$error_message = $validation->get_error_message();
if ( $error_message ) {
$review_messages[] = $error_message;
} else {
/* translators: %s is the error code */
$review_messages[] = esc_html( sprintf( __( 'Error code: %s.', 'amp' ), $validation->get_error_code() ) );
}
$notice_type = 'error';
} elseif ( is_array( $validation ) ) {
$new_errors = 0;
$rejected_errors = 0;
$errors = wp_list_pluck( $validation['results'], 'error' );
foreach ( $errors as $error ) {
$sanitization = AMP_Validation_Error_Taxonomy::get_validation_error_sanitization( $error );
$is_new_rejected = AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_REJECTED_STATUS === $sanitization['status'];
if ( $is_new_rejected || AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_ACCEPTED_STATUS === $sanitization['status'] ) {
$new_errors++;
}
if ( $is_new_rejected || AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_ACK_REJECTED_STATUS === $sanitization['status'] ) {
$rejected_errors++;
}
}
$invalid_url_post_id = AMP_Validated_URL_Post_Type::store_validation_errors( $errors, $url );
$invalid_url_screen_url = ! is_wp_error( $invalid_url_post_id ) ? get_edit_post_link( $invalid_url_post_id, 'raw' ) : null;
if ( $rejected_errors > 0 ) {
$notice_type = 'error';
$message = wp_kses_post(
sprintf(
/* translators: %s is count of rejected errors */
_n(
'However, AMP is not yet available due to %s validation error (for one URL at least).',
'However, AMP is not yet available due to %s validation errors (for one URL at least).',
number_format_i18n( $rejected_errors ),
'amp'
),
$rejected_errors,
esc_url( $invalid_url_screen_url )
)
);
if ( $invalid_url_screen_url ) {
$message .= ' ' . wp_kses_post(
sprintf(
/* translators: %s is URL to review issues */
_n(
'<a href="%s">Review Issue</a>.',
'<a href="%s">Review Issues</a>.',
$rejected_errors,
'amp'
),
esc_url( $invalid_url_screen_url )
)
);
}
$review_messages[] = $message;
} else {
$message = wp_kses_post(
sprintf(
/* translators: %s is an AMP URL */
__( 'View an <a href="%s">AMP version of your site</a>.', 'amp' ),
esc_url( $url )
)
);
if ( $new_errors > 0 && $invalid_url_screen_url ) {
$message .= ' ' . wp_kses_post(
sprintf(
/* translators: 1: URL to review issues. 2: count of new errors. */
_n(
'Please also <a href="%1$s">review %2$s issue</a> which may need to be fixed (for one URL at least).',
'Please also <a href="%1$s">review %2$s issues</a> which may need to be fixed (for one URL at least).',
$new_errors,
'amp'
),
esc_url( $invalid_url_screen_url ),
number_format_i18n( $new_errors )
)
);
}
$review_messages[] = $message;
}
}
}
switch ( $template_mode ) {
case 'native':
$message = esc_html__( 'Native mode activated!', 'amp' );
if ( $review_messages ) {
$message .= ' ' . join( ' ', $review_messages );
}
break;
case 'paired':
$message = esc_html__( 'Transitional mode activated!', 'amp' );
if ( $review_messages ) {
$message .= ' ' . join( ' ', $review_messages );
}
break;
case 'disabled':
$message = wp_kses_post(
sprintf(
/* translators: %s is an AMP URL */
__( 'Reader mode activated! View the <a href="%s">AMP version of a recent post</a>. It is recommended that you upgrade to Native or Transitional mode.', 'amp' ),
esc_url( $url )
)
);
break;
}
if ( isset( $message ) ) {
add_settings_error( self::OPTION_NAME, 'template_mode_updated', $message, $notice_type );
}
}
}

View File

@@ -0,0 +1,616 @@
<?php
/**
* AMP Options.
*
* @package AMP
*/
/**
* AMP_Options_Menu class.
*/
class AMP_Options_Menu {
/**
* The AMP svg menu icon.
*
* @var string
*/
const ICON_BASE64_SVG = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iNjJweCIgaGVpZ2h0PSI2MnB4IiB2aWV3Qm94PSIwIDAgNjIgNjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+QU1QLUJyYW5kLUJsYWNrLUljb248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iYW1wLWxvZ28taW50ZXJuYWwtc2l0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgICAgICA8ZyBpZD0iQU1QLUJyYW5kLUJsYWNrLUljb24iIGZpbGw9IiMwMDAwMDAiPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00MS42Mjg4NjY3LDI4LjE2MTQzMzMgTDI4LjYyNDM2NjcsNDkuODAzNTY2NyBMMjYuMjY4MzY2Nyw0OS44MDM1NjY3IEwyOC41OTc1LDM1LjcwMTY2NjcgTDIxLjM4MzgsMzUuNzEwOTY2NyBDMjEuMzgzOCwzNS43MTA5NjY3IDIxLjMxNTYsMzUuNzEzMDMzMyAyMS4yODM1NjY3LDM1LjcxMzAzMzMgQzIwLjYzMzYsMzUuNzEzMDMzMyAyMC4xMDc2MzMzLDM1LjE4NzA2NjcgMjAuMTA3NjMzMywzNC41MzcxIEMyMC4xMDc2MzMzLDM0LjI1ODEgMjAuMzY3LDMzLjc4NTg2NjcgMjAuMzY3LDMzLjc4NTg2NjcgTDMzLjMyOTEzMzMsMTIuMTY5NTY2NyBMMzUuNzI0NCwxMi4xNzk5IEwzMy4zMzYzNjY3LDI2LjMwMzUgTDQwLjU4NzI2NjcsMjYuMjk0MiBDNDAuNTg3MjY2NywyNi4yOTQyIDQwLjY2NDc2NjcsMjYuMjkzMTY2NyA0MC43MDE5NjY3LDI2LjI5MzE2NjcgQzQxLjM1MTkzMzMsMjYuMjkzMTY2NyA0MS44Nzc5LDI2LjgxOTEzMzMgNDEuODc3OSwyNy40NjkxIEM0MS44Nzc5LDI3LjczMjYgNDEuNzc0NTY2NywyNy45NjQwNjY3IDQxLjYyNzgzMzMsMjguMTYwNCBMNDEuNjI4ODY2NywyOC4xNjE0MzMzIFogTTMxLDAgQzEzLjg3ODcsMCAwLDEzLjg3OTczMzMgMCwzMSBDMCw0OC4xMjEzIDEzLjg3ODcsNjIgMzEsNjIgQzQ4LjEyMDI2NjcsNjIgNjIsNDguMTIxMyA2MiwzMSBDNjIsMTMuODc5NzMzMyA0OC4xMjAyNjY3LDAgMzEsMCBMMzEsMCBaIiBpZD0iRmlsbC0xIj48L3BhdGg+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=';
/**
* Initialize.
*/
public function init() {
add_action( 'admin_post_amp_analytics_options', 'AMP_Options_Manager::handle_analytics_submit' );
add_action( 'admin_menu', array( $this, 'add_menu_items' ), 9 );
$plugin_file = preg_replace( '#.+/(?=.+?/.+?)#', '', AMP__FILE__ );
add_filter( "plugin_action_links_{$plugin_file}", array( $this, 'add_plugin_action_links' ) );
}
/**
* Add plugin action links.
*
* @param array $links Links.
* @return array Modified links.
*/
public function add_plugin_action_links( $links ) {
return array_merge(
array(
'settings' => sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( add_query_arg( 'page', AMP_Options_Manager::OPTION_NAME, admin_url( 'admin.php' ) ) ),
__( 'Settings', 'amp' )
),
),
$links
);
}
/**
* Add menu.
*/
public function add_menu_items() {
add_menu_page(
__( 'AMP Options', 'amp' ),
__( 'AMP', 'amp' ),
'edit_posts',
AMP_Options_Manager::OPTION_NAME,
array( $this, 'render_screen' ),
self::ICON_BASE64_SVG
);
add_submenu_page(
AMP_Options_Manager::OPTION_NAME,
__( 'AMP Settings', 'amp' ),
__( 'General', 'amp' ),
'edit_posts',
AMP_Options_Manager::OPTION_NAME
);
add_settings_section(
'general',
false,
'__return_false',
AMP_Options_Manager::OPTION_NAME
);
add_settings_field(
'theme_support',
__( 'Template Mode', 'amp' ),
array( $this, 'render_theme_support' ),
AMP_Options_Manager::OPTION_NAME,
'general',
array(
'class' => 'theme_support',
)
);
add_settings_field(
'validation',
__( 'Validation Handling', 'amp' ),
array( $this, 'render_validation_handling' ),
AMP_Options_Manager::OPTION_NAME,
'general',
array(
'class' => 'amp-validation-field',
)
);
add_settings_field(
'supported_templates',
__( 'Supported Templates', 'amp' ),
array( $this, 'render_supported_templates' ),
AMP_Options_Manager::OPTION_NAME,
'general',
array(
'class' => 'amp-template-support-field',
)
);
if ( wp_using_ext_object_cache() ) {
add_settings_field(
'caching',
__( 'Caching', 'amp' ),
array( $this, 'render_caching' ),
AMP_Options_Manager::OPTION_NAME,
'general',
array(
'class' => 'amp-caching-field',
)
);
}
$submenus = array(
new AMP_Analytics_Options_Submenu( AMP_Options_Manager::OPTION_NAME ),
);
// Create submenu items and calls on the Submenu Page object to render the actual contents of the page.
foreach ( $submenus as $submenu ) {
$submenu->init();
}
}
/**
* Render theme support.
*
* @since 1.0
*/
public function render_theme_support() {
$theme_support = AMP_Options_Manager::get_option( 'theme_support' );
/* translators: %s: URL to the documentation. */
$native_description = sprintf( __( 'Integrates AMP as the framework for your site by using the actives theme templates and styles to render AMP responses. This means your site is <b>AMP-first</b> and your canonical URLs are AMP! Depending on your theme/plugins, a varying level of <a href="%s">development work</a> may be required.', 'amp' ), esc_url( 'https://amp-wp.org/documentation/developing-wordpress-amp-sites/' ) );
/* translators: %s: URL to the documentation. */
$transitional_description = sprintf( __( 'Uses the active themes templates to generate non-AMP and AMP versions of your content, allowing for each canonical URL to have a corresponding (paired) AMP URL. This mode is useful to progressively transition towards a fully AMP-first site. Depending on your theme/plugins, a varying level of <a href="%s">development work</a> may be required.', 'amp' ), esc_url( 'https://amp-wp.org/documentation/developing-wordpress-amp-sites/' ) );
$reader_description = __( 'Formerly called the <b>classic mode</b>, this mode generates paired AMP content using simplified templates which may not match the look-and-feel of your site. Only posts/pages can be served as AMP in Reader mode. No redirection is performed for mobile visitors; AMP pages are served by AMP consumption platforms.', 'amp' );
/* translators: %s: URL to the ecosystem page. */
$ecosystem_description = sprintf( __( 'For a list of themes and plugins that are known to be AMP compatible, please see the <a href="%s">ecosystem page</a>.' ), esc_url( 'https://amp-wp.org/ecosystem/' ) );
$builtin_support = in_array( get_template(), AMP_Core_Theme_Sanitizer::get_supported_themes(), true );
?>
<?php if ( current_theme_supports( AMP_Theme_Support::SLUG ) && ! AMP_Theme_Support::is_support_added_via_option() ) : ?>
<div class="notice notice-info notice-alt inline">
<p><?php esc_html_e( 'Your active theme has built-in AMP support.', 'amp' ); ?></p>
</div>
<p>
<?php echo wp_kses_post( $ecosystem_description ); ?>
</p>
<p>
<?php if ( amp_is_canonical() ) : ?>
<strong><?php esc_html_e( 'Native:', 'amp' ); ?></strong>
<?php echo wp_kses_post( $native_description ); ?>
<?php else : ?>
<strong><?php esc_html_e( 'Transitional:', 'amp' ); ?></strong>
<?php echo wp_kses_post( $transitional_description ); ?>
<?php endif; ?>
</p>
<?php else : ?>
<fieldset <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<?php if ( $builtin_support ) : ?>
<div class="notice notice-success notice-alt inline">
<p><?php esc_html_e( 'Your active theme is known to work well in transitional or native mode.', 'amp' ); ?></p>
</div>
<?php endif; ?>
<p>
<?php echo wp_kses_post( $ecosystem_description ); ?>
</p>
<dl>
<dt>
<input type="radio" id="theme_support_native" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[theme_support]' ); ?>" value="native" <?php checked( $theme_support, 'native' ); ?>>
<label for="theme_support_native">
<strong><?php esc_html_e( 'Native', 'amp' ); ?></strong>
</label>
</dt>
<dd>
<?php echo wp_kses_post( $native_description ); ?>
</dd>
<dt>
<input type="radio" id="theme_support_transitional" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[theme_support]' ); ?>" value="paired" <?php checked( $theme_support, 'paired' ); ?>>
<label for="theme_support_transitional">
<strong><?php esc_html_e( 'Transitional', 'amp' ); ?></strong>
</label>
</dt>
<dd>
<?php echo wp_kses_post( $transitional_description ); ?>
</dd>
<dt>
<input type="radio" id="theme_support_disabled" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[theme_support]' ); ?>" value="disabled" <?php checked( $theme_support, 'disabled' ); ?>>
<label for="theme_support_disabled">
<strong><?php esc_html_e( 'Reader', 'amp' ); ?></strong>
</label>
</dt>
<dd>
<?php echo wp_kses_post( $reader_description ); ?>
<?php if ( ! current_theme_supports( AMP_Theme_Support::SLUG ) && wp_count_posts( AMP_Validated_URL_Post_Type::POST_TYPE_SLUG )->publish > 0 ) : ?>
<div class="notice notice-info inline notice-alt">
<p>
<?php
echo wp_kses_post(
sprintf(
/* translators: %1: link to invalid URLs. 2: link to validation errors. */
__( 'View current site compatibility results for native and transitional modes: %1$s and %2$s.', 'amp' ),
sprintf(
'<a href="%s">%s</a>',
esc_url( add_query_arg( 'post_type', AMP_Validated_URL_Post_Type::POST_TYPE_SLUG, admin_url( 'edit.php' ) ) ),
esc_html( get_post_type_object( AMP_Validated_URL_Post_Type::POST_TYPE_SLUG )->labels->name )
),
sprintf(
'<a href="%s">%s</a>',
esc_url(
add_query_arg(
array(
'taxonomy' => AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG,
'post_type' => AMP_Validated_URL_Post_Type::POST_TYPE_SLUG,
),
admin_url( 'edit-tags.php' )
)
),
esc_html( get_taxonomy( AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG )->labels->name )
)
)
);
?>
</p>
</div>
<?php endif; ?>
</dd>
</dl>
</fieldset>
<?php endif; ?>
<?php
}
/**
* Post types support section renderer.
*
* @todo If dirty AMP is ever allowed (that is, post-processed documents which can be served with non-sanitized valdation errors), then automatically forcing sanitization in native should be able to be turned off.
*
* @since 1.0
*/
public function render_validation_handling() {
?>
<fieldset <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<?php
$auto_sanitization = AMP_Validation_Error_Taxonomy::get_validation_error_sanitization(
array(
'code' => 'non_existent',
)
);
remove_filter( 'amp_validation_error_sanitized', array( 'AMP_Validation_Manager', 'filter_tree_shaking_validation_error_as_accepted' ) );
$tree_shaking_sanitization = AMP_Validation_Error_Taxonomy::get_validation_error_sanitization(
array(
'code' => AMP_Style_Sanitizer::TREE_SHAKING_ERROR_CODE,
)
);
$forced_sanitization = 'with_filter' === $auto_sanitization['forced'];
$forced_tree_shaking = $forced_sanitization || 'with_filter' === $tree_shaking_sanitization['forced'];
?>
<?php if ( $forced_sanitization ) : ?>
<div class="notice notice-info notice-alt inline">
<p><?php esc_html_e( 'Your install is configured via a theme or plugin to automatically sanitize any AMP validation error that is encountered.', 'amp' ); ?></p>
</div>
<input type="hidden" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[auto_accept_sanitization]' ); ?>" value="<?php echo AMP_Options_Manager::get_option( 'auto_accept_sanitization' ) ? 'on' : ''; ?>">
<?php else : ?>
<div class="amp-auto-accept-sanitize-canonical notice notice-info notice-alt inline">
<p><?php esc_html_e( 'All new validation errors are automatically accepted when in native mode.', 'amp' ); ?></p>
</div>
<div class="amp-auto-accept-sanitize">
<p>
<label for="auto_accept_sanitization">
<input id="auto_accept_sanitization" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[auto_accept_sanitization]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'auto_accept_sanitization' ) ); ?>>
<?php esc_html_e( 'Automatically accept sanitization for any newly encountered AMP validation errors.', 'amp' ); ?>
</label>
</p>
<p class="description">
<?php esc_html_e( 'This will ensure your responses are always valid AMP but some important content may get stripped out (e.g. scripts).', 'amp' ); ?>
<?php
echo wp_kses_post(
sprintf(
/* translators: %s is URL to validation errors screen */
__( 'Existing validation errors which you have already rejected will not be modified (you may want to consider <a href="%s">bulk-accepting them</a>).', 'amp' ),
esc_url(
add_query_arg(
array(
'taxonomy' => AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG,
'post_type' => AMP_Validated_URL_Post_Type::POST_TYPE_SLUG,
),
admin_url( 'edit-tags.php' )
)
)
)
)
?>
</p>
</div>
<?php endif; ?>
<?php if ( $forced_tree_shaking ) : ?>
<input type="hidden" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[accept_tree_shaking]' ); ?>" value="<?php echo AMP_Options_Manager::get_option( 'accept_tree_shaking' ) ? 'on' : ''; ?>">
<?php else : ?>
<div class="amp-tree-shaking">
<p>
<label for="accept_tree_shaking">
<input id="accept_tree_shaking" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[accept_tree_shaking]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'accept_tree_shaking' ) ); ?>>
<?php esc_html_e( 'Automatically remove CSS rules that are not relevant to a given page (tree shaking).', 'amp' ); ?>
</label>
</p>
<p class="description">
<?php esc_html_e( 'AMP limits the total amount of CSS to no more than 50KB; any more than this will cause a validation error. The need to tree shake the CSS is not done by default because in some situations (in particular for dynamic content) it can result in CSS rules being removed that are needed.', 'amp' ); ?>
</p>
</div>
<?php endif; ?>
<script>
(function( $ ) {
var getThemeSupportMode = function() {
var checkedInput = $( 'input[type=radio][name="amp-options[theme_support]"]:checked' );
if ( 0 === checkedInput.length ) {
return <?php echo wp_json_encode( amp_is_canonical() ? 'native' : 'paired' ); ?>;
}
return checkedInput.val();
};
var updateTreeShakingHiddenClass = function() {
var checkbox = $( '#auto_accept_sanitization' );
$( '.amp-tree-shaking' ).toggleClass( 'hidden', checkbox.prop( 'checked' ) && 'native' !== getThemeSupportMode() );
};
var updateHiddenClasses = function() {
var themeSupportMode = getThemeSupportMode();
$( '.amp-auto-accept-sanitize' ).toggleClass( 'hidden', 'native' === themeSupportMode );
$( '.amp-validation-field' ).toggleClass( 'hidden', 'disabled' === themeSupportMode );
$( '.amp-auto-accept-sanitize-canonical' ).toggleClass( 'hidden', 'native' !== themeSupportMode );
updateTreeShakingHiddenClass();
};
$( 'input[type=radio][name="amp-options[theme_support]"]' ).change( updateHiddenClasses );
$( '#auto_accept_sanitization' ).change( updateTreeShakingHiddenClass );
updateHiddenClasses();
})( jQuery );
</script>
<p>
<label for="disable_admin_bar">
<input id="disable_admin_bar" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[disable_admin_bar]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'disable_admin_bar' ) ); ?>>
<?php esc_html_e( 'Disable admin bar on AMP pages.', 'amp' ); ?>
</label>
</p>
<p class="description">
<?php esc_html_e( 'An additional stylesheet is required to properly render the admin bar. If the additional stylesheet causes the total CSS to surpass 50KB then the admin bar should be disabled to prevent a validation error or an unstyled admin bar in AMP responses.', 'amp' ); ?>
</p>
</fieldset>
<?php
}
/**
* Supported templates section renderer.
*
* @since 1.0
*/
public function render_supported_templates() {
$theme_support_args = AMP_Theme_Support::get_theme_support_args();
?>
<?php if ( ! isset( $theme_support_args['available_callback'] ) ) : ?>
<fieldset id="all_templates_supported_fieldset" <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<?php if ( isset( $theme_support_args['templates_supported'] ) && 'all' === $theme_support_args['templates_supported'] ) : ?>
<div class="notice notice-info notice-alt inline">
<p>
<?php esc_html_e( 'The current theme requires all templates to support AMP.', 'amp' ); ?>
</p>
</div>
<?php else : ?>
<p>
<label for="all_templates_supported">
<input id="all_templates_supported" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[all_templates_supported]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'all_templates_supported' ) ); ?>>
<?php esc_html_e( 'Serve all templates as AMP regardless of what is being queried.', 'amp' ); ?>
</label>
</p>
<p class="description">
<?php esc_html_e( 'This will allow all of the URLs on your site to be served as AMP by default.', 'amp' ); ?>
</p>
<?php endif; ?>
</fieldset>
<?php else : ?>
<div class="notice notice-warning notice-alt inline">
<p>
<?php
printf(
/* translators: %s: available_callback */
esc_html__( 'Your theme is using the deprecated %s argument for AMP theme support.', 'amp' ),
'available_callback'
);
?>
</p>
</div>
<?php endif; ?>
<fieldset id="supported_post_types_fieldset" <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<?php $element_name = AMP_Options_Manager::OPTION_NAME . '[supported_post_types][]'; ?>
<h4 class="title"><?php esc_html_e( 'Content Types', 'amp' ); ?></h4>
<p>
<?php esc_html_e( 'The following content types will be available as AMP:', 'amp' ); ?>
</p>
<ul>
<?php foreach ( array_map( 'get_post_type_object', AMP_Post_Type_Support::get_eligible_post_types() ) as $post_type ) : ?>
<li>
<?php $element_id = AMP_Options_Manager::OPTION_NAME . "-supported_post_types-{$post_type->name}"; ?>
<input
type="checkbox"
id="<?php echo esc_attr( $element_id ); ?>"
name="<?php echo esc_attr( $element_name ); ?>"
value="<?php echo esc_attr( $post_type->name ); ?>"
<?php checked( post_type_supports( $post_type->name, AMP_Post_Type_Support::SLUG ) ); ?>
>
<label for="<?php echo esc_attr( $element_id ); ?>">
<?php echo esc_html( $post_type->label ); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</fieldset>
<?php if ( ! isset( $theme_support_args['available_callback'] ) ) : ?>
<fieldset id="supported_templates_fieldset" <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<style>
#supported_templates_fieldset ul ul {
margin-left: 40px;
}
</style>
<h4 class="title"><?php esc_html_e( 'Templates', 'amp' ); ?></h4>
<?php
self::list_template_conditional_options( AMP_Theme_Support::get_supportable_templates() );
?>
<script>
// Let clicks on parent items automatically cause the children checkboxes to have same checked state applied.
(function ( $ ) {
$( '#supported_templates_fieldset input[type=checkbox]' ).on( 'click', function() {
$( this ).siblings( 'ul' ).find( 'input[type=checkbox]' ).prop( 'checked', this.checked );
} );
})( jQuery );
</script>
</fieldset>
<script>
// Update the visibility of the fieldsets based on the selected template mode and then whether all templates are indicated to be supported.
(function ( $ ) {
var templateModeInputs, themeSupportDisabledInput, allTemplatesSupportedInput, supportForced;
templateModeInputs = $( 'input[type=radio][name="amp-options[theme_support]"]' );
themeSupportDisabledInput = $( '#theme_support_disabled' );
allTemplatesSupportedInput = $( '#all_templates_supported' );
supportForced = <?php echo wp_json_encode( current_theme_supports( AMP_Theme_Support::SLUG ) && ! AMP_Theme_Support::is_support_added_via_option() ); ?>;
function isThemeSupportDisabled() {
return ! supportForced && themeSupportDisabledInput.prop( 'checked' );
}
function updateFieldsetVisibility() {
var allTemplatesSupported = 0 === allTemplatesSupportedInput.length || allTemplatesSupportedInput.prop( 'checked' );
$( '#all_templates_supported_fieldset, #supported_post_types_fieldset > .title' ).toggleClass(
'hidden',
isThemeSupportDisabled()
);
$( '#supported_post_types_fieldset' ).toggleClass(
'hidden',
allTemplatesSupported && ! isThemeSupportDisabled()
);
$( '#supported_templates_fieldset' ).toggleClass(
'hidden',
allTemplatesSupported || isThemeSupportDisabled()
);
}
templateModeInputs.on( 'change', updateFieldsetVisibility );
allTemplatesSupportedInput.on( 'click', updateFieldsetVisibility );
updateFieldsetVisibility();
})( jQuery );
</script>
<?php endif; ?>
<?php
}
/**
* Render the caching settings section.
*
* @since 1.0
*
* @todo Change the messaging and description to be user-friendly and helpful.
*/
public function render_caching() {
?>
<fieldset <?php disabled( ! current_user_can( 'manage_options' ) ); ?>>
<?php if ( AMP_Options_Manager::show_response_cache_disabled_notice() ) : ?>
<div class="notice notice-info notice-alt inline">
<p><?php esc_html_e( 'The post-processor cache was disabled due to detecting randomly generated content found on', 'amp' ); ?> <a href="<?php echo esc_url( get_option( AMP_Theme_Support::CACHE_MISS_URL_OPTION, '' ) ); ?>"><?php esc_html_e( 'on this web page.', 'amp' ); ?></a></p>
<p><?php esc_html_e( 'Randomly generated content was detected on this web page. To avoid filling up the cache with unusable content, the AMP plugin\'s post-processor cache was automatically disabled.', 'amp' ); ?>
<a href="<?php echo esc_url( 'https://github.com/ampproject/amp-wp/wiki/Post-Processor-Cache' ); ?>"><?php esc_html_e( 'Read more', 'amp' ); ?></a>.</p>
</div>
<?php endif; ?>
<p>
<label for="enable_response_caching">
<input id="enable_response_caching" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[enable_response_caching]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'enable_response_caching' ) ); ?>>
<?php esc_html_e( 'Enable post-processor caching.', 'amp' ); ?>
</label>
</p>
<p class="description"><?php esc_html_e( 'This will enable post-processor caching to speed up processing an AMP response after WordPress renders a template.', 'amp' ); ?></p>
</fieldset>
<?php
}
/**
* List template conditional options.
*
* @param array $options Options.
* @param string|null $parent ID of the parent option.
*/
private function list_template_conditional_options( $options, $parent = null ) {
$element_name = AMP_Options_Manager::OPTION_NAME . '[supported_templates][]';
?>
<ul>
<?php foreach ( $options as $id => $option ) : ?>
<?php
$element_id = AMP_Options_Manager::OPTION_NAME . '-supported-templates-' . $id;
if ( $parent ? empty( $option['parent'] ) || $parent !== $option['parent'] : ! empty( $option['parent'] ) ) {
continue;
}
// Skip showing an option if it doesn't have a label.
if ( empty( $option['label'] ) ) {
continue;
}
?>
<li>
<?php if ( empty( $option['immutable'] ) ) : ?>
<input
type="checkbox"
id="<?php echo esc_attr( $element_id ); ?>"
name="<?php echo esc_attr( $element_name ); ?>"
value="<?php echo esc_attr( $id ); ?>"
<?php checked( ! empty( $option['user_supported'] ) ); ?>
>
<?php else : // Persist user selection even when checkbox disabled, when selection forced by theme/filter. ?>
<input
type="checkbox"
id="<?php echo esc_attr( $element_id ); ?>"
<?php checked( ! empty( $option['supported'] ) ); ?>
<?php disabled( true ); ?>
>
<?php if ( ! empty( $option['user_supported'] ) ) : ?>
<input type="hidden" name="<?php echo esc_attr( $element_name ); ?>" value="<?php echo esc_attr( $id ); ?>">
<?php endif; ?>
<?php endif; ?>
<label for="<?php echo esc_attr( $element_id ); ?>">
<?php echo esc_html( $option['label'] ); ?>
</label>
<?php if ( ! empty( $option['description'] ) ) : ?>
<span class="description">
&mdash; <?php echo wp_kses_post( $option['description'] ); ?>
</span>
<?php endif; ?>
<?php self::list_template_conditional_options( $options, $id ); ?>
</li>
<?php endforeach; ?>
</ul>
<?php
}
/**
* Display Settings.
*
* @since 0.6
*/
public function render_screen() {
if ( ! empty( $_GET['settings-updated'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
AMP_Options_Manager::check_supported_post_type_update_errors();
}
?>
<?php if ( ! current_user_can( 'manage_options' ) ) : ?>
<div class="notice notice-info">
<p><?php esc_html_e( 'You do not have permission to modify these settings. They are shown here for your reference. Please contact your administrator to make changes.', 'amp' ); ?></p>
</div>
<?php endif; ?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php settings_errors(); ?>
<form id="amp-settings" action="options.php" method="post">
<?php
settings_fields( AMP_Options_Manager::OPTION_NAME );
do_settings_sections( AMP_Options_Manager::OPTION_NAME );
if ( current_user_can( 'manage_options' ) ) {
submit_button();
}
?>
</form>
</div>
<?php
}
}

View File

@@ -0,0 +1,203 @@
<?php
/**
* Class AMP_Analytics_Options_Submenu_Page
*
* @package AMP
*/
/**
* Class AMP_Analytics_Options_Submenu_Page
*/
class AMP_Analytics_Options_Submenu_Page {
/**
* Render entry.
*
* @param string $id Entry ID.
* @param string $type Entry type.
* @param string $config Entry config as serialized JSON.
*/
private function render_entry( $id = '', $type = '', $config = '{}' ) {
$is_existing_entry = ! empty( $id );
if ( $is_existing_entry ) {
$entry_slug = sprintf( '%s%s', ( $type ? $type . '-' : '' ), substr( $id, - 6 ) );
/* translators: %s: the entry slug. */
$analytics_title = sprintf( __( 'Analytics: %s', 'amp' ), $entry_slug );
} else {
$analytics_title = __( 'Add new entry:', 'amp' );
$id = '__new__';
}
// Tidy-up the JSON for display.
if ( $config ) {
$options = ( 128 /* JSON_PRETTY_PRINT */ | 64 /* JSON_UNESCAPED_SLASHES */ );
$config = wp_json_encode( json_decode( $config ), $options );
}
$id_base = sprintf( '%s[analytics][%s]', AMP_Options_Manager::OPTION_NAME, $id );
?>
<div class="analytics-data-container">
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<h2>
<?php echo esc_html( $analytics_title ); ?>
</h2>
<div class="options">
<p>
<label>
<?php esc_html_e( 'Type:', 'amp' ); ?>
<input class="option-input" type="text" required name="<?php echo esc_attr( $id_base . '[type]' ); ?>" placeholder="<?php esc_attr_e( 'e.g. googleanalytics', 'amp' ); ?>" value="<?php echo esc_attr( $type ); ?>" />
</label>
<label>
<?php esc_html_e( 'ID:', 'amp' ); ?>
<input type="text" value="<?php echo esc_attr( $is_existing_entry ? $id : '' ); ?>" readonly />
</label>
<input type="hidden" name="<?php echo esc_attr( $id_base . '[id]' ); ?>" value="<?php echo esc_attr( $id ); ?>" />
</p>
<p>
<label>
<?php esc_html_e( 'JSON Configuration:', 'amp' ); ?>
<br />
<textarea
rows="10"
cols="100"
name="<?php echo esc_attr( $id_base . '[config]' ); ?>"
class="amp-analytics-input"
placeholder="{...}"
required
><?php echo esc_textarea( $config ); ?></textarea>
</label>
</p>
<input type="hidden" name="action" value="amp_analytics_options">
</div>
<p>
<?php
wp_nonce_field( 'analytics-options', 'analytics-options' );
submit_button( esc_html__( 'Save', 'amp' ), 'primary', 'save', false );
if ( $is_existing_entry ) {
submit_button( esc_html__( 'Delete', 'amp' ), 'delete button-primary', esc_attr( $id_base . '[delete]' ), false );
}
?>
</p>
</form>
</div><!-- #analytics-data-container -->
<?php
}
/**
* Render title.
*
* @param bool $has_entries Whether there are entries.
*/
public function render_title( $has_entries = false ) {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php settings_errors(); ?>
<details <?php echo ! $has_entries ? 'open' : ''; ?>>
<summary>
<?php esc_html_e( 'Learn about analytics for AMP.', 'amp' ); ?>
</summary>
<p>
<?php
echo wp_kses_post(
sprintf(
/* translators: 1: AMP Analytics docs URL. 2: AMP for WordPress analytics docs URL. 3: AMP analytics code reference. 4: amp-analytics, 5: {. 6: }. 7: <script>, 8: googleanalytics. 9: AMP analytics vendor docs URL. 10: UA-XXXXX-Y. */
__( 'For Google Analytics, please see <a href="%1$s" target="_blank">Adding Analytics to your AMP pages</a>; see also the <a href="%2$s" target="_blank">Analytics wiki page</a> and the AMP project\'s <a href="%3$s" target="_blank">%4$s documentation</a>. The analytics configuration supplied below must take the form of JSON objects, which begin with a %5$s and end with a %6$s. Do not include any HTML tags like %4$s or %7$s. A common entry would have the type %8$s (see <a href="%9$s" target="_blank">available vendors</a>) and a configuration that looks like the following (where %10$s is replaced with your own site\'s account number):', 'amp' ),
__( 'https://developers.google.com/analytics/devguides/collection/amp-analytics/', 'amp' ),
__( 'https://amp-wp.org/documentation/playbooks/analytics/', 'amp' ),
__( 'https://www.ampproject.org/docs/reference/components/amp-analytics', 'amp' ),
'<code>amp-analytics</code>',
'<code>{</code>',
'<code>}</code>',
'<code>&lt;script&gt;</code>',
'<code>googleanalytics</code>',
__( 'https://www.ampproject.org/docs/analytics/analytics-vendors', 'amp' ),
'<code>UA-XXXXX-Y</code>'
)
);
?>
<pre>{
"vars": {
"account": "UA-XXXXX-Y"
},
"triggers": {
"trackPageview": {
"on": "visible",
"request": "pageview"
}
}
}</pre>
</p>
</details>
</div><!-- .wrap -->
<?php
}
/**
* Render styles.
*/
protected function render_styles() {
?>
<style>
.amp-analytics-input {
font-family: monospace;
}
.amp-analytics-input:invalid {
border-color: red;
}
</style>
<?php
}
/**
* Render scripts.
*/
protected function render_scripts() {
?>
<script>
Array.prototype.forEach.call( document.querySelectorAll( '.amp-analytics-input' ), function( textarea ) {
textarea.addEventListener( 'input', function() {
if ( ! this.value ) {
this.setCustomValidity( '' );
return;
}
try {
var value = JSON.parse( this.value );
if ( null === value || typeof value !== 'object' || Array.isArray( value ) ) {
this.setCustomValidity( <?php echo wp_json_encode( __( 'A JSON object is required, e.g. {...}', 'amp' ) ); ?> )
} else {
this.setCustomValidity( '' );
}
} catch ( e ) {
this.setCustomValidity( e.message )
}
} );
} );
</script>
<?php
}
/**
* Render.
*/
public function render() {
$this->render_styles();
$analytics_entries = AMP_Options_Manager::get_option( 'analytics', array() );
$this->render_title( ! empty( $analytics_entries ) );
// Render entries stored in the DB.
foreach ( $analytics_entries as $entry_id => $entry ) {
$this->render_entry( $entry_id, $entry['type'], $entry['config'] );
}
// Empty form for adding more entries.
$this->render_entry();
$this->render_scripts();
}
}