Changeset 1708734
- Timestamp:
- 08/05/2017 03:59:01 AM (8 years ago)
- Location:
- amp/trunk
- Files:
-
- 34 added
- 1 deleted
- 31 edited
-
amp.php (modified) (7 diffs)
-
assets/images/placeholder-icon.png (modified) (previous)
-
includes/admin/class-amp-customizer.php (modified) (3 diffs)
-
includes/admin/functions.php (modified) (3 diffs)
-
includes/amp-helper-functions.php (modified) (2 diffs)
-
includes/amp-post-template-actions.php (modified) (10 diffs)
-
includes/amp-post-template-functions.php (modified) (1 diff)
-
includes/class-amp-content.php (modified) (3 diffs)
-
includes/class-amp-post-template.php (modified) (10 diffs)
-
includes/embeds/class-amp-dailymotion-embed.php (added)
-
includes/embeds/class-amp-gallery-embed.php (modified) (1 diff)
-
includes/embeds/class-amp-instagram-embed.php (modified) (1 diff)
-
includes/embeds/class-amp-pinterest-embed.php (added)
-
includes/embeds/class-amp-soundcloud-embed.php (added)
-
includes/embeds/class-amp-vimeo-embed.php (added)
-
includes/embeds/class-amp-vine-embed.php (modified) (1 diff)
-
includes/embeds/class-amp-youtube-embed.php (modified) (3 diffs)
-
includes/lib/class-fastimage.php (deleted)
-
includes/lib/fasterimage (added)
-
includes/lib/fasterimage/Exception (added)
-
includes/lib/fasterimage/Exception/InvalidImageException.php (added)
-
includes/lib/fasterimage/ExifParser.php (added)
-
includes/lib/fasterimage/FasterImage.php (added)
-
includes/lib/fasterimage/ImageParser.php (added)
-
includes/lib/fasterimage/Stream (added)
-
includes/lib/fasterimage/Stream/Exception (added)
-
includes/lib/fasterimage/Stream/Exception/StreamBufferTooSmallException.php (added)
-
includes/lib/fasterimage/Stream/Stream.php (added)
-
includes/lib/fasterimage/Stream/StreamableInterface.php (added)
-
includes/lib/fasterimage/amp-fasterimage.php (added)
-
includes/lib/fastimage (added)
-
includes/lib/fastimage/class-fastimage.php (added)
-
includes/options (added)
-
includes/options/class-amp-analytics-options-submenu.php (added)
-
includes/options/class-amp-options-menu.php (added)
-
includes/options/views (added)
-
includes/options/views/class-amp-analytics-options-submenu-page.php (added)
-
includes/options/views/class-amp-options-manager.php (added)
-
includes/options/views/class-amp-options-menu-page.php (added)
-
includes/sanitizers/class-amp-allowed-tags-generated.php (added)
-
includes/sanitizers/class-amp-base-sanitizer.php (modified) (1 diff)
-
includes/sanitizers/class-amp-blacklist-sanitizer.php (modified) (7 diffs)
-
includes/sanitizers/class-amp-iframe-sanitizer.php (modified) (2 diffs)
-
includes/sanitizers/class-amp-img-sanitizer.php (modified) (3 diffs)
-
includes/sanitizers/class-amp-playbuzz-sanitizer.php (added)
-
includes/sanitizers/class-amp-style-sanitizer.php (modified) (2 diffs)
-
includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php (added)
-
includes/sanitizers/class-amp-video-sanitizer.php (modified) (2 diffs)
-
includes/settings/class-amp-customizer-design-settings.php (modified) (8 diffs)
-
includes/utils/class-amp-dom-utils.php (modified) (1 diff)
-
includes/utils/class-amp-html-utils.php (modified) (1 diff)
-
includes/utils/class-amp-image-dimension-extractor.php (modified) (3 diffs)
-
includes/utils/class-amp-string-utils.php (modified) (1 diff)
-
includes/utils/class-amp-wp-utils.php (added)
-
jetpack-helper.php (modified) (2 diffs)
-
readme-assets (added)
-
readme-assets/amp-options-analytics.png (added)
-
readme-assets/analytics-option-entries.png (added)
-
readme-assets/invalid-input.png (added)
-
readme-assets/options-saved.png (added)
-
readme.md (modified) (11 diffs)
-
readme.txt (modified) (5 diffs)
-
templates/footer.php (modified) (1 diff)
-
templates/header-bar.php (modified) (1 diff)
-
templates/meta-author.php (modified) (1 diff)
-
templates/style.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
amp/trunk/amp.php
r1514204 r1708734 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com 8 * Version: 0. 4.28 * Version: 0.5 9 9 * Text Domain: amp 10 10 * Domain Path: /languages/ … … 14 14 define( 'AMP__FILE__', __FILE__ ); 15 15 define( 'AMP__DIR__', dirname( __FILE__ ) ); 16 define( 'AMP__VERSION', '0. 4.2' );16 define( 'AMP__VERSION', '0.5' ); 17 17 18 18 require_once( AMP__DIR__ . '/back-compat/back-compat.php' ); … … 61 61 add_filter( 'request', 'amp_force_query_var_value' ); 62 62 add_action( 'wp', 'amp_maybe_add_actions' ); 63 64 // Redirect the old url of amp page to the updated url. 65 add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' ); 63 66 64 67 if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) { … … 115 118 require_once( AMP__DIR__ . '/includes/amp-post-template-actions.php' ); 116 119 require_once( AMP__DIR__ . '/includes/amp-post-template-functions.php' ); 120 amp_post_template_init_hooks(); 117 121 } 118 122 … … 122 126 123 127 function amp_render() { 128 $post_id = get_queried_object_id(); 129 amp_render_post( $post_id ); 130 exit; 131 } 132 133 function amp_render_post( $post_id ) { 134 $post = get_post( $post_id ); 135 if ( ! $post ) { 136 return; 137 } 138 124 139 amp_load_classes(); 125 140 126 $post_id = get_queried_object_id();127 141 do_action( 'pre_amp_render_post', $post_id ); 128 142 … … 130 144 $template = new AMP_Post_Template( $post_id ); 131 145 $template->load(); 132 exit;133 146 } 134 147 … … 158 171 } 159 172 add_action( 'plugins_loaded', '_amp_bootstrap_customizer', 9 ); 173 174 /** 175 * Redirects the old AMP URL to the new AMP URL. 176 * If post slug is updated the amp page with old post slug will be redirected to the updated url. 177 * 178 * @param string $link New URL of the post. 179 * 180 * @return string $link URL to be redirected. 181 */ 182 function amp_redirect_old_slug_to_new_url( $link ) { 183 184 if ( is_amp_endpoint() ) { 185 $link = trailingslashit( trailingslashit( $link ) . AMP_QUERY_VAR ); 186 } 187 188 return $link; 189 } -
amp/trunk/includes/admin/class-amp-customizer.php
r1510272 r1708734 47 47 // Our custom panels only need to go for AMP Customizer requests though 48 48 if ( self::is_amp_customizer() ) { 49 if ( empty( $_GET['url'] ) ) { 49 if ( empty( $_GET['url'] ) ) { // input var ok 50 50 $wp_customize->set_preview_url( amp_admin_get_preview_permalink() ); 51 51 } … … 157 157 158 158 public function force_mobile_preview( $devices ) { 159 if ( isset( $devices[ 'mobile'] ) ) {159 if ( isset( $devices['mobile'] ) ) { 160 160 $devices['mobile']['default'] = true; 161 161 unset( $devices['desktop']['default'] ); … … 166 166 167 167 public static function is_amp_customizer() { 168 return ! empty( $_REQUEST[ AMP_CUSTOMIZER_QUERY_VAR ] ); 168 return ! empty( $_REQUEST[ AMP_CUSTOMIZER_QUERY_VAR ] ); // input var ok 169 169 } 170 170 } -
amp/trunk/includes/admin/functions.php
r1510272 r1708734 1 1 <?php 2 2 // Callbacks for adding AMP-related things to the admin. 3 4 require_once( AMP__DIR__ . '/includes/options/class-amp-options-menu.php' ); 5 require_once( AMP__DIR__ . '/includes/options/views/class-amp-options-manager.php' ); 3 6 4 7 define( 'AMP_CUSTOMIZER_QUERY_VAR', 'customize_amp' ); … … 23 26 } 24 27 25 /**26 * Registers a submenu page to access the AMP template editor panel in the Customizer.27 */28 function amp_add_customizer_link() {29 // Teensy little hack on menu_slug, but it works. No redirect!30 $menu_slug = add_query_arg( array(31 'autofocus[panel]' => AMP_Template_Customizer::PANEL_ID,32 'return' => rawurlencode( admin_url() ),33 AMP_CUSTOMIZER_QUERY_VAR => true,34 ), 'customize.php' );35 36 // Add the theme page.37 $page = add_theme_page(38 __( 'AMP', 'amp' ),39 __( 'AMP', 'amp' ),40 'edit_theme_options',41 $menu_slug42 );43 }44 45 28 function amp_admin_get_preview_permalink() { 46 29 /** 47 * Filter the post type to retrieve the latest offor use in the AMP template customizer.30 * Filter the post type to retrieve the latest for use in the AMP template customizer. 48 31 * 49 32 * @param string $post_type Post type slug. Default 'post'. … … 70 53 return amp_get_permalink( $post_id ); 71 54 } 55 56 /** 57 * Registers a submenu page to access the AMP template editor panel in the Customizer. 58 */ 59 function amp_add_customizer_link() { 60 // Teensy little hack on menu_slug, but it works. No redirect! 61 $menu_slug = add_query_arg( array( 62 'autofocus[panel]' => AMP_Template_Customizer::PANEL_ID, 63 'return' => rawurlencode( admin_url() ), 64 AMP_CUSTOMIZER_QUERY_VAR => true, 65 ), 'customize.php' ); 66 67 // Add the theme page. 68 add_theme_page( 69 __( 'AMP', 'amp' ), 70 __( 'AMP', 'amp' ), 71 'edit_theme_options', 72 $menu_slug 73 ); 74 } 75 76 /** 77 * Registers a top-level menu for AMP configuration options 78 */ 79 function amp_add_options_menu() { 80 if ( ! is_admin() ) { 81 return; 82 } 83 84 $show_options_menu = apply_filters( 'amp_options_menu_is_enabled', true ); 85 if ( true !== $show_options_menu ) { 86 return; 87 } 88 89 $amp_options = new AMP_Options_Menu(); 90 $amp_options->init(); 91 } 92 add_action( 'wp_loaded', 'amp_add_options_menu' ); 93 94 function amp_add_custom_analytics( $analytics ) { 95 $analytics_entries = AMP_Options_Manager::get_option( 'analytics', array() ); 96 97 if ( ! $analytics_entries ) { 98 return $analytics; 99 } 100 101 foreach ( $analytics_entries as $entry_id => $entry ) { 102 $analytics[ $entry_id ] = array( 103 'type' => $entry['type'], 104 'attributes' => array(), 105 'config_data' => json_decode( $entry['config'] ), 106 ); 107 } 108 109 return $analytics; 110 } 111 add_filter( 'amp_post_template_analytics', 'amp_add_custom_analytics' ); -
amp/trunk/includes/amp-helper-functions.php
r1510272 r1708734 8 8 } 9 9 10 if ( '' != get_option( 'permalink_structure' ) ) { 10 $structure = get_option( 'permalink_structure' ); 11 if ( empty( $structure ) ) { 12 $amp_url = add_query_arg( AMP_QUERY_VAR, 1, get_permalink( $post_id ) ); 13 } else { 11 14 $amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( AMP_QUERY_VAR, 'single_amp' ); 12 } else {13 $amp_url = add_query_arg( AMP_QUERY_VAR, 1, get_permalink( $post_id ) );14 15 } 15 16 … … 40 41 */ 41 42 function is_amp_endpoint() { 43 if ( 0 === did_action( 'parse_query' ) ) { 44 _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( "is_amp_endpoint() was called before the 'parse_query' hook was called. This function will always return 'false' before the 'parse_query' hook is called.", 'amp' ) ), '0.4.2' ); 45 } 46 42 47 return false !== get_query_var( AMP_QUERY_VAR, false ); 43 48 } -
amp/trunk/includes/amp-post-template-actions.php
r1510272 r1708734 2 2 // Callbacks for adding content to an AMP template 3 3 4 add_action( 'amp_post_template_head', 'amp_post_template_add_title' ); 4 function amp_post_template_init_hooks() { 5 add_action( 'amp_post_template_head', 'amp_post_template_add_title' ); 6 add_action( 'amp_post_template_head', 'amp_post_template_add_canonical' ); 7 add_action( 'amp_post_template_head', 'amp_post_template_add_scripts' ); 8 add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' ); 9 add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' ); 10 add_action( 'amp_post_template_head', 'amp_post_template_add_schemaorg_metadata' ); 11 add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 ); 12 add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' ); 13 add_action( 'amp_post_template_footer', 'amp_post_template_add_analytics_data' ); 14 } 15 5 16 function amp_post_template_add_title( $amp_template ) { 6 17 ?> … … 9 20 } 10 21 11 add_action( 'amp_post_template_head', 'amp_post_template_add_canonical' );12 22 function amp_post_template_add_canonical( $amp_template ) { 13 23 ?> … … 16 26 } 17 27 18 add_action( 'amp_post_template_head', 'amp_post_template_add_scripts' );19 28 function amp_post_template_add_scripts( $amp_template ) { 20 29 $scripts = $amp_template->get( 'amp_component_scripts', array() ); 21 foreach ( $scripts as $element => $script ) : ?> 22 <script custom-element="<?php echo esc_attr( $element ); ?>" src="<?php echo esc_url( $script ); ?>" async></script> 30 foreach ( $scripts as $element => $script ) : 31 $custom_type = ($element == 'amp-mustache') ? 'template' : 'element'; ?> 32 <script custom-<?php echo esc_attr( $custom_type ); ?>="<?php echo esc_attr( $element ); ?>" src="<?php echo esc_url( $script ); ?>" async></script> 23 33 <?php endforeach; ?> 24 34 <script src="<?php echo esc_url( $amp_template->get( 'amp_runtime_script' ) ); ?>" async></script> … … 26 36 } 27 37 28 add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' );29 38 function amp_post_template_add_fonts( $amp_template ) { 30 39 $font_urls = $amp_template->get( 'font_urls', array() ); 31 foreach ( $font_urls as $slug => $url ) : ?>40 foreach ( $font_urls as $slug => $url ) : ?> 32 41 <link rel="stylesheet" href="<?php echo esc_url( $url ); ?>"> 33 42 <?php endforeach; 34 43 } 35 44 36 add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );37 45 function amp_post_template_add_boilerplate_css( $amp_template ) { 38 46 ?> … … 41 49 } 42 50 43 add_action( 'amp_post_template_head', 'amp_post_template_add_schemaorg_metadata' );44 51 function amp_post_template_add_schemaorg_metadata( $amp_template ) { 45 52 $metadata = $amp_template->get( 'metadata' ); … … 48 55 } 49 56 ?> 50 <script type="application/ld+json"><?php echo json_encode( $metadata ); ?></script>57 <script type="application/ld+json"><?php echo wp_json_encode( $metadata ); ?></script> 51 58 <?php 52 59 } 53 60 54 add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 );55 61 function amp_post_template_add_styles( $amp_template ) { 56 62 $styles = $amp_template->get( 'post_amp_styles' ); … … 58 64 echo '/* Inline styles */' . PHP_EOL; 59 65 foreach ( $styles as $selector => $declarations ) { 60 $declarations = implode( ";", $declarations ) . ";";66 $declarations = implode( ';', $declarations ) . ';'; 61 67 printf( '%1$s{%2$s}', $selector, $declarations ); 62 68 } … … 64 70 } 65 71 66 add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );67 72 function amp_post_template_add_analytics_script( $data ) { 68 73 if ( ! empty( $data['amp_analytics'] ) ) { … … 72 77 } 73 78 74 add_action( 'amp_post_template_footer', 'amp_post_template_add_analytics_data' );75 79 function amp_post_template_add_analytics_data( $amp_template ) { 76 80 $analytics_entries = $amp_template->get( 'amp_analytics' ); … … 81 85 foreach ( $analytics_entries as $id => $analytics_entry ) { 82 86 if ( ! isset( $analytics_entry['type'], $analytics_entry['attributes'], $analytics_entry['config_data'] ) ) { 83 _doing_it_wrong( __FUNCTION__, sprintf( __( 'Analytics entry for %s is missing one of the following keys: `type`, `attributes`, or `config_data` (array keys: %s)', 'amp' ), esc_html( $id ), esc_html( implode( ', ', array_keys( $analytics_entry ) ) ) ), '0.3.2' );87 _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'Analytics entry for %s is missing one of the following keys: `type`, `attributes`, or `config_data` (array keys: %s)', 'amp' ), esc_html( $id ), esc_html( implode( ', ', array_keys( $analytics_entry ) ) ) ), '0.3.2' ); 84 88 continue; 85 89 } 86 87 90 $script_element = AMP_HTML_Utils::build_tag( 'script', array( 88 91 'type' => 'application/json', 89 ), json_encode( $analytics_entry['config_data'] ) );92 ), wp_json_encode( $analytics_entry['config_data'] ) ); 90 93 91 94 $amp_analytics_attr = array_merge( array( -
amp/trunk/includes/amp-post-template-functions.php
r1514204 r1708734 9 9 10 10 // 3 or 6 hex digits, or the empty string. 11 if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {11 if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) { 12 12 return $color; 13 13 } -
amp/trunk/includes/class-amp-content.php
r1514204 r1708734 64 64 65 65 if ( ! is_subclass_of( $embed_handler, 'AMP_Base_Embed_Handler' ) ) { 66 _doing_it_wrong( __METHOD__, sprintf( __( 'Embed Handler (%s) must extend `AMP_Embed_Handler`', 'amp' ), $embed_handler_class ), '0.1' );66 _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Embed Handler (%s) must extend `AMP_Embed_Handler`', 'amp' ), $embed_handler_class ), '0.1' ); 67 67 continue; 68 68 } … … 100 100 foreach ( $sanitizer_classes as $sanitizer_class => $args ) { 101 101 if ( ! class_exists( $sanitizer_class ) ) { 102 _doing_it_wrong( __METHOD__, sprintf( __( 'Sanitizer (%s) class does not exist', 'amp' ), esc_html( $sanitizer_class ) ), '0.4.1' );102 _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) class does not exist', 'amp' ), esc_html( $sanitizer_class ) ), '0.4.1' ); 103 103 continue; 104 104 } … … 107 107 108 108 if ( ! is_subclass_of( $sanitizer, 'AMP_Base_Sanitizer' ) ) { 109 _doing_it_wrong( __METHOD__, sprintf( __( 'Sanitizer (%s) must extend `AMP_Base_Sanitizer`', 'amp' ), esc_html( $sanitizer_class ) ), '0.1' );109 _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Sanitizer (%s) must extend `AMP_Base_Sanitizer`', 'amp' ), esc_html( $sanitizer_class ) ), '0.1' ); 110 110 continue; 111 111 } -
amp/trunk/includes/class-amp-post-template.php
r1514204 r1708734 4 4 require_once( AMP__DIR__ . '/includes/utils/class-amp-html-utils.php' ); 5 5 require_once( AMP__DIR__ . '/includes/utils/class-amp-string-utils.php' ); 6 require_once( AMP__DIR__ . '/includes/utils/class-amp-wp-utils.php' ); 6 7 7 8 require_once( AMP__DIR__ . '/includes/class-amp-content.php' ); … … 9 10 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-style-sanitizer.php' ); 10 11 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-blacklist-sanitizer.php' ); 12 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php' ); 11 13 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-img-sanitizer.php' ); 12 14 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-video-sanitizer.php' ); 13 15 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-iframe-sanitizer.php' ); 14 16 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-audio-sanitizer.php' ); 17 require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-playbuzz-sanitizer.php' ); 15 18 16 19 require_once( AMP__DIR__ . '/includes/embeds/class-amp-twitter-embed.php' ); 17 20 require_once( AMP__DIR__ . '/includes/embeds/class-amp-youtube-embed.php' ); 21 require_once( AMP__DIR__ . '/includes/embeds/class-amp-dailymotion-embed.php' ); 22 require_once( AMP__DIR__ . '/includes/embeds/class-amp-vimeo-embed.php' ); 23 require_once( AMP__DIR__ . '/includes/embeds/class-amp-soundcloud-embed.php' ); 18 24 require_once( AMP__DIR__ . '/includes/embeds/class-amp-gallery-embed.php' ); 19 25 require_once( AMP__DIR__ . '/includes/embeds/class-amp-instagram-embed.php' ); 20 26 require_once( AMP__DIR__ . '/includes/embeds/class-amp-vine-embed.php' ); 21 27 require_once( AMP__DIR__ . '/includes/embeds/class-amp-facebook-embed.php' ); 28 require_once( AMP__DIR__ . '/includes/embeds/class-amp-pinterest-embed.php' ); 22 29 23 30 class AMP_Post_Template { … … 70 77 'merriweather' => 'https://fonts.googleapis.com/css?family=Merriweather:400,400italic,700,700italic', 71 78 ), 79 80 'post_amp_styles' => array(), 72 81 73 82 /** … … 82 91 */ 83 92 'amp_analytics' => apply_filters( 'amp_post_template_analytics', array(), $this->post ), 84 );93 ); 85 94 86 95 $this->build_post_content(); … … 96 105 return $this->data[ $property ]; 97 106 } else { 98 _doing_it_wrong( __METHOD__, sprintf( __( 'Called for non-existant key ("%s").', 'amp' ), esc_html( $property ) ), '0.1' );107 _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Called for non-existant key ("%s").', 'amp' ), esc_html( $property ) ), '0.1' ); 99 108 } 100 109 … … 224 233 'AMP_Twitter_Embed_Handler' => array(), 225 234 'AMP_YouTube_Embed_Handler' => array(), 235 'AMP_DailyMotion_Embed_Handler' => array(), 236 'AMP_Vimeo_Embed_Handler' => array(), 237 'AMP_SoundCloud_Embed_Handler' => array(), 226 238 'AMP_Instagram_Embed_Handler' => array(), 227 239 'AMP_Vine_Embed_Handler' => array(), 228 240 'AMP_Facebook_Embed_Handler' => array(), 241 'AMP_Pinterest_Embed_Handler' => array(), 229 242 'AMP_Gallery_Embed_Handler' => array(), 230 243 ), $this->post ), 231 244 apply_filters( 'amp_content_sanitizers', array( 232 245 'AMP_Style_Sanitizer' => array(), 233 'AMP_Blacklist_Sanitizer' => array(),246 // 'AMP_Blacklist_Sanitizer' => array(), 234 247 'AMP_Img_Sanitizer' => array(), 235 248 'AMP_Video_Sanitizer' => array(), 236 249 'AMP_Audio_Sanitizer' => array(), 250 'AMP_Playbuzz_Sanitizer' => array(), 237 251 'AMP_Iframe_Sanitizer' => array( 238 252 'add_placeholder' => true, 239 253 ), 254 'AMP_Tag_And_Attribute_Sanitizer' => array(), 240 255 ), $this->post ), 241 256 array( … … 246 261 $this->add_data_by_key( 'post_amp_content', $amp_content->get_amp_content() ); 247 262 $this->merge_data_for_key( 'amp_component_scripts', $amp_content->get_amp_scripts() ); 248 $this-> add_data_by_key( 'post_amp_styles', $amp_content->get_amp_styles() );263 $this->merge_data_for_key( 'post_amp_styles', $amp_content->get_amp_styles() ); 249 264 } 250 265 … … 275 290 array( 'AMP_Img_Sanitizer' => array() ), 276 291 array( 277 'content_max_width' => $this->get( 'content_max_width' ) 292 'content_max_width' => $this->get( 'content_max_width' ), 278 293 ) 279 294 ); … … 289 304 290 305 if ( $featured_styles ) { 291 $this-> add_data_by_key( 'post_amp_styles', $featured_styles );306 $this->merge_data_for_key( 'post_amp_styles', $featured_styles ); 292 307 } 293 308 } … … 382 397 $file = apply_filters( 'amp_post_template_file', $file, $template_type, $this->post ); 383 398 if ( ! $this->is_valid_template( $file ) ) { 384 _doing_it_wrong( __METHOD__, sprintf( __( 'Path validation for template (%s) failed. Path cannot traverse and must be located in `%s`.', 'amp' ), esc_html( $file ), 'WP_CONTENT_DIR' ), '0.1' );399 _doing_it_wrong( __METHOD__, sprintf( esc_html__( 'Path validation for template (%s) failed. Path cannot traverse and must be located in `%s`.', 'amp' ), esc_html( $file ), 'WP_CONTENT_DIR' ), '0.1' ); 385 400 return; 386 401 } -
amp/trunk/includes/embeds/class-amp-gallery-embed.php
r1364370 r1708734 40 40 'include' => '', 41 41 'exclude' => '', 42 'size' => array( $this->args['width'], $this->args['height'] ) 42 'size' => array( $this->args['width'], $this->args['height'] ), 43 43 ), $attr, 'gallery' ); 44 44 -
amp/trunk/includes/embeds/class-amp-instagram-embed.php
r1478266 r1708734 53 53 54 54 public function oembed( $matches, $attr, $url, $rawattr ) { 55 return $this->render( array( 'url' => $url, 'instagram_id' => end( $matches ) ) );55 return $this->render( array( 'url' => $url, 'instagram_id' => end( $matches ) ) ); 56 56 } 57 57 -
amp/trunk/includes/embeds/class-amp-vine-embed.php
r1337793 r1708734 29 29 30 30 public function oembed( $matches, $attr, $url, $rawattr ) { 31 return $this->render( array( 'url' => $url, 'vine_id' => end( $matches ) ) );31 return $this->render( array( 'url' => $url, 'vine_id' => end( $matches ) ) ); 32 32 } 33 33 -
amp/trunk/includes/embeds/class-amp-youtube-embed.php
r1510272 r1708734 49 49 if ( isset( $attr[0] ) ) { 50 50 $url = ltrim( $attr[0] , '=' ); 51 } elseif ( function_exists ( 'shortcode_new_to_old_params' ) ) {51 } elseif ( function_exists( 'shortcode_new_to_old_params' ) ) { 52 52 $url = shortcode_new_to_old_params( $attr ); 53 53 } … … 93 93 private function get_video_id_from_url( $url ) { 94 94 $video_id = false; 95 $parsed_url = parse_url( $url );95 $parsed_url = AMP_WP_Utils::parse_url( $url ); 96 96 97 97 if ( self::SHORT_URL_HOST === substr( $parsed_url['host'], -strlen( self::SHORT_URL_HOST ) ) ) { … … 114 114 $parts = explode( '/', $parsed_url['path'] ); 115 115 116 if ( in_array( $parts[1], array( 'v', 'e', 'embed' ) ) ) {116 if ( in_array( $parts[1], array( 'v', 'e', 'embed' ), true ) ) { 117 117 $video_id = $parts[2]; 118 118 } -
amp/trunk/includes/sanitizers/class-amp-base-sanitizer.php
r1510272 r1708734 43 43 44 44 if ( AMP_String_Utils::endswith( $value, '%' ) ) { 45 if ( 'width' === $dimension && isset( $this->args[ 'content_max_width'] ) ) {45 if ( 'width' === $dimension && isset( $this->args['content_max_width'] ) ) { 46 46 $percentage = absint( $value ) / 100; 47 return round( $percentage * $this->args[ 'content_max_width'] );47 return round( $percentage * $this->args['content_max_width'] ); 48 48 } 49 49 } -
amp/trunk/includes/sanitizers/class-amp-blacklist-sanitizer.php
r1510272 r1708734 8 8 * See following for blacklist: 9 9 * https://github.com/ampproject/amphtml/blob/master/spec/amp-html-format.md#html-tags 10 * 11 * As of AMP 0.5 this has been replaced by AMP_Tag_And_Attribute_Sanitizer but is kept around for back-compat. 12 * 10 13 */ 11 14 class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer { … … 29 32 30 33 private function strip_attributes_recursive( $node, $bad_attributes, $bad_protocols ) { 31 if ( $node->nodeType !== XML_ELEMENT_NODE) {34 if ( XML_ELEMENT_NODE !== $node->nodeType ) { 32 35 return; 33 36 } … … 37 40 // Some nodes may contain valid content but are themselves invalid. 38 41 // Remove the node but preserve the children. 39 if ( 'font' === $node_name ) {42 if ( 'font' === $node_name ) { 40 43 $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols ); 41 44 return; … … 50 53 $attribute = $node->attributes->item( $i ); 51 54 $attribute_name = strtolower( $attribute->name ); 52 if ( in_array( $attribute_name, $bad_attributes ) ) {55 if ( in_array( $attribute_name, $bad_attributes, true ) ) { 53 56 $node->removeAttribute( $attribute_name ); 54 57 continue; … … 56 59 57 60 // on* attributes (like onclick) are a special case 58 if ( 0 === stripos( $attribute_name, 'on' ) && $attribute_name != 'on') {61 if ( 0 === stripos( $attribute_name, 'on' ) && 'on' !== $attribute_name ) { 59 62 $node->removeAttribute( $attribute_name ); 60 63 continue; … … 125 128 $href = $node->getAttribute( 'href' ); 126 129 127 // If no href is set and this isn't an anchor, it's invalid128 130 if ( empty( $href ) ) { 129 $name_attr = $node->getAttribute( 'name' ); 130 if ( ! empty( $name_attr ) ) { 131 // No further validation is required 132 return true; 133 } else { 134 return false; 135 } 131 // If no href, check that a is an anchor or not. 132 // We don't need to validate anchors any further. 133 return $node->hasAttribute( 'name' ) || $node->hasAttribute( 'id' ); 136 134 } 137 135 … … 151 149 152 150 if ( false === filter_var( $href, FILTER_VALIDATE_URL ) 153 && ! in_array( $protocol, $special_protocols ) ) {151 && ! in_array( $protocol, $special_protocols, true ) ) { 154 152 return false; 155 153 } 156 154 157 if ( ! in_array( $protocol, $valid_protocols ) ) {155 if ( ! in_array( $protocol, $valid_protocols, true ) ) { 158 156 return false; 159 157 } -
amp/trunk/includes/sanitizers/class-amp-iframe-sanitizer.php
r1478266 r1708734 98 98 break; 99 99 100 101 100 case 'frameborder': 102 101 if ( '0' !== $value && '1' !== $value ) { … … 118 117 } 119 118 120 if ( ! isset( $out[ 'sandbox'] ) ) {121 $out[ 'sandbox'] = self::SANDBOX_DEFAULTS;119 if ( ! isset( $out['sandbox'] ) ) { 120 $out['sandbox'] = self::SANDBOX_DEFAULTS; 122 121 } 123 122 -
amp/trunk/includes/sanitizers/class-amp-img-sanitizer.php
r1510272 r1708734 19 19 20 20 public function sanitize() { 21 21 22 $nodes = $this->dom->getElementsByTagName( self::$tag ); 23 $need_dimensions = array(); 24 22 25 $num_nodes = $nodes->length; 26 23 27 if ( 0 === $num_nodes ) { 24 28 return; … … 27 31 for ( $i = $num_nodes - 1; $i >= 0; $i-- ) { 28 32 $node = $nodes->item( $i ); 29 $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );30 33 31 if ( empty( $old_attributes['src']) ) {34 if ( ! $node->hasAttribute( 'src' ) || '' === $node->getAttribute( 'src' ) ) { 32 35 $node->parentNode->removeChild( $node ); 33 36 continue; 34 37 } 35 38 36 $new_attributes = $this->filter_attributes( $old_attributes ); 39 // Determine which images need their dimensions determined/extracted. 40 if ( '' === $node->getAttribute( 'width' ) || '' === $node->getAttribute( 'height' ) ) { 41 $need_dimensions[ $node->getAttribute( 'src' ) ][] = $node; 42 } else { 43 $this->adjust_and_replace_node( $node ); 44 } 45 } 37 46 38 // Try to extract dimensions for the image, if not set. 39 if ( ! isset( $new_attributes['width'] ) || ! isset( $new_attributes['height'] ) ) { 40 $dimensions = AMP_Image_Dimension_Extractor::extract( $new_attributes['src'] ); 41 if ( is_array( $dimensions ) ) { 42 $new_attributes['width'] = $dimensions[0]; 43 $new_attributes['height'] = $dimensions[1]; 47 $this->determine_dimensions( $need_dimensions ); 48 $this->adjust_and_replace_nodes_in_array_map( $need_dimensions ); 49 } 50 51 /** 52 * Figure out width and height attribute values for images that don't have them by 53 * attempting to determine actual dimensions and setting reasonable defaults otherwise. 54 * 55 * @param array $need_dimensions List of Img src url to node mappings corresponding to images that need dimensions. 56 */ 57 private function determine_dimensions( $need_dimensions ) { 58 $dimensions_by_url = AMP_Image_Dimension_Extractor::extract( array_keys( $need_dimensions ) ); 59 60 foreach ( $dimensions_by_url as $url => $dimensions ) { 61 foreach ( $need_dimensions[ $url ] as $node ) { 62 // Provide default dimensions for images whose dimensions we couldn't fetch. 63 if ( false === $dimensions ) { 64 $width = isset( $this->args['content_max_width'] ) ? $this->args['content_max_width'] : self::FALLBACK_WIDTH; 65 $height = self::FALLBACK_HEIGHT; 66 $node->setAttribute( 'width', $width ); 67 $node->setAttribute( 'height', $height ); 68 $class = $node->hasAttribute( 'class' ) ? $node->getAttribute( 'class' ) . ' amp-wp-unknown-size' : 'amp-wp-unknown-size'; 69 $node->setAttribute( 'class', $class ); 70 } else { 71 $node->setAttribute( 'width', $dimensions['width'] ); 72 $node->setAttribute( 'height', $dimensions['height'] ); 44 73 } 45 74 } 75 } 76 } 46 77 47 // Final fallback when we have no dimensions. 48 if ( ! isset( $new_attributes['width'] ) || ! isset( $new_attributes['height'] ) ) { 49 $new_attributes['width'] = isset( $this->args['content_max_width'] ) ? $this->args['content_max_width'] : self::FALLBACK_WIDTH; 50 $new_attributes['height'] = self::FALLBACK_HEIGHT; 78 /** 79 * Make final modifications to DOMNode 80 * 81 * @param DOMNode $node The DOMNode to adjust and replace 82 */ 83 private function adjust_and_replace_node( $node ) { 84 $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node ); 85 $new_attributes = $this->filter_attributes( $old_attributes ); 86 $new_attributes = $this->enforce_sizes_attribute( $new_attributes ); 87 if ( $this->is_gif_url( $new_attributes['src'] ) ) { 88 $this->did_convert_elements = true; 89 $new_tag = 'amp-anim'; 90 } else { 91 $new_tag = 'amp-img'; 92 } 93 $new_node = AMP_DOM_Utils::create_node( $this->dom, $new_tag, $new_attributes ); 94 $node->parentNode->replaceChild( $new_node, $node ); 95 } 51 96 52 $this->add_or_append_attribute( $new_attributes, 'class', 'amp-wp-unknown-size' ); 97 /** 98 * Now that all images have width and height attributes, make final tweaks and replace original image nodes 99 * 100 * @param array $node_lists Img DOM nodes (now with width and height attributes). 101 */ 102 private function adjust_and_replace_nodes_in_array_map( $node_lists ) { 103 foreach ( $node_lists as $node_list ) { 104 foreach ( $node_list as $node ) { 105 $this->adjust_and_replace_node( $node ); 53 106 } 54 55 $new_attributes = $this->enforce_sizes_attribute( $new_attributes );56 57 if ( $this->is_gif_url( $new_attributes['src'] ) ) {58 $this->did_convert_elements = true;59 $new_tag = 'amp-anim';60 } else {61 $new_tag = 'amp-img';62 }63 64 $new_node = AMP_DOM_Utils::create_node( $this->dom, $new_tag, $new_attributes );65 $node->parentNode->replaceChild( $new_node, $node );66 107 } 67 108 } … … 104 145 private function is_gif_url( $url ) { 105 146 $ext = self::$anim_extension; 106 $path = parse_url( $url, PHP_URL_PATH );107 return $ext === substr( $path, -strlen( $ext ) );147 $path = AMP_WP_Utils::parse_url( $url, PHP_URL_PATH ); 148 return substr( $path, -strlen( $ext ) ) === $ext; 108 149 } 109 150 } -
amp/trunk/includes/sanitizers/class-amp-style-sanitizer.php
r1512429 r1708734 19 19 20 20 private function collect_styles_recursive( $node ) { 21 if ( $node->nodeType !== XML_ELEMENT_NODE) {21 if ( XML_ELEMENT_NODE !== $node->nodeType ) { 22 22 return; 23 23 } … … 57 57 } 58 58 59 // Normalize order 60 $styles = array_map( 'trim', explode( ';', $string ) ); 59 // safecss returns a string but we want individual rules. 60 // Using preg_split to break up rules by `;` but only if the semi-colon is not inside parens (like a data-encoded image). 61 $styles = array_map( 'trim', preg_split( "/;(?![^(]*\))/", $string ) ); 62 63 // Normalize the order of the styles 61 64 sort( $styles ); 62 65 -
amp/trunk/includes/sanitizers/class-amp-video-sanitizer.php
r1478266 r1708734 10 10 11 11 public static $tag = 'video'; 12 13 private static $script_slug = 'amp-video'; 14 private static $script_src = 'https://cdn.ampproject.org/v0/amp-video-0.1.js'; 15 16 public function get_scripts() { 17 if ( ! $this->did_convert_elements ) { 18 return array(); 19 } 20 21 return array( self::$script_slug => self::$script_src ); 22 } 12 23 13 24 public function sanitize() { … … 51 62 $node->parentNode->replaceChild( $new_node, $node ); 52 63 } 64 65 $this->did_convert_elements = true; 53 66 } 54 67 } -
amp/trunk/includes/settings/class-amp-customizer-design-settings.php
r1510272 r1708734 24 24 'default' => self::DEFAULT_HEADER_COLOR, 25 25 'sanitize_callback' => 'sanitize_hex_color', 26 'transport' => 'postMessage' 26 'transport' => 'postMessage', 27 27 ) ); 28 28 … … 32 32 'default' => self::DEFAULT_HEADER_BACKGROUND_COLOR, 33 33 'sanitize_callback' => 'sanitize_hex_color', 34 'transport' => 'postMessage' 34 'transport' => 'postMessage', 35 35 ) ); 36 36 … … 40 40 'default' => self::DEFAULT_COLOR_SCHEME, 41 41 'sanitize_callback' => array( __CLASS__ , 'sanitize_color_scheme' ), 42 'transport' => 'postMessage' 42 'transport' => 'postMessage', 43 43 ) ); 44 44 } 45 45 46 public function register_customizer_ui( $wp_customize ) {46 public static function register_customizer_ui( $wp_customize ) { 47 47 $wp_customize->add_section( 'amp_design', array( 48 48 'title' => __( 'Design', 'amp' ), … … 56 56 'label' => __( 'Header Text Color', 'amp' ), 57 57 'section' => 'amp_design', 58 'priority' => 10 58 'priority' => 10, 59 59 ) ) 60 60 ); … … 66 66 'label' => __( 'Header Background & Link Color', 'amp' ), 67 67 'section' => 'amp_design', 68 'priority' => 20 68 'priority' => 20, 69 69 ) ) 70 70 ); … … 110 110 protected static function get_color_scheme_names() { 111 111 return array( 112 'light' => __( 'Light', 'amp' ),112 'light' => __( 'Light', 'amp' ), 113 113 'dark' => __( 'Dark', 'amp' ), 114 114 ); … … 130 130 'muted_text_color' => '#b1b1b1', 131 131 'border_color' => '#707070', 132 ) 132 ), 133 133 ); 134 134 } … … 148 148 $scheme_slugs = array_keys( $schemes ); 149 149 150 if ( ! in_array( $value, $scheme_slugs ) ) {150 if ( ! in_array( $value, $scheme_slugs, true ) ) { 151 151 $value = self::DEFAULT_COLOR_SCHEME; 152 152 } -
amp/trunk/includes/utils/class-amp-dom-utils.php
r1514204 r1708734 96 96 // Not all are valid AMP, but we include them for completeness. 97 97 $self_closing_tags = array( 98 'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'embed', 'frame', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr', 98 'area', 99 'base', 100 'basefont', 101 'bgsound', 102 'br', 103 'col', 104 'embed', 105 'frame', 106 'hr', 107 'img', 108 'input', 109 'keygen', 110 'link', 111 'meta', 112 'param', 113 'source', 114 'track', 115 'wbr', 99 116 ); 100 117 } 101 118 102 return in_array( $tag, $self_closing_tags );119 return in_array( $tag, $self_closing_tags, true ); 103 120 } 104 121 } -
amp/trunk/includes/utils/class-amp-html-utils.php
r1337793 r1708734 18 18 return implode( ' ', $string ); 19 19 } 20 21 public static function is_valid_json( $data ) { 22 if ( ! empty( $data ) ) { 23 $decoded = json_decode( $data ); 24 if ( function_exists( 'json_last_error' ) ) { 25 return ( json_last_error() === JSON_ERROR_NONE ); 26 } else { // PHP 5.2 back-compatibility 27 return null !== $decoded; 28 } 29 } 30 return false; 31 } 20 32 } -
amp/trunk/includes/utils/class-amp-image-dimension-extractor.php
r1357059 r1708734 3 3 class AMP_Image_Dimension_Extractor { 4 4 static $callbacks_registered = false; 5 6 static public function extract( $url ) { 5 const STATUS_FAILED_LAST_ATTEMPT = 'failed'; 6 const STATUS_IMAGE_EXTRACTION_FAILED = 'failed'; 7 8 static public function extract( $urls ) { 7 9 if ( ! self::$callbacks_registered ) { 8 10 self::register_callbacks(); 9 11 } 10 12 11 $url = self::normalize_url( $url ); 12 if ( false === $url ) { 13 return false; 14 } 15 16 return apply_filters( 'amp_extract_image_dimensions', false, $url ); 13 $valid_urls = array(); 14 foreach ( $urls as $url ) { 15 $url = self::normalize_url( $url ); 16 if ( false !== $url ) { 17 $valid_urls[] = $url; 18 } 19 } 20 21 $dimensions = array_fill_keys( $valid_urls, false ); 22 $dimensions = apply_filters( 'amp_extract_image_dimensions_batch', $dimensions ); 23 24 return $dimensions; 17 25 } 18 26 … … 30 38 } 31 39 32 $parsed = parse_url( $url );40 $parsed = AMP_WP_Utils::parse_url( $url ); 33 41 if ( ! isset( $parsed['host'] ) ) { 34 42 $path = ''; … … 48 56 self::$callbacks_registered = true; 49 57 50 add_filter( 'amp_extract_image_dimensions', array( __CLASS__, 'extract_from_attachment_metadata' ), 10, 2 ); 51 add_filter( 'amp_extract_image_dimensions', array( __CLASS__, 'extract_by_downloading_image' ), 999, 2 ); // Run really late since this is our last resort 52 53 do_action( 'amp_extract_image_dimensions_callbacks_registered' ); 54 } 55 56 public static function extract_from_attachment_metadata( $dimensions, $url ) { 57 if ( is_array( $dimensions ) ) { 58 return $dimensions; 59 } 60 61 $url = strtok( $url, '?' ); 62 $attachment_id = attachment_url_to_postid( $url ); 63 if ( empty( $attachment_id ) ) { 64 return false; 65 } 66 67 $metadata = wp_get_attachment_metadata( $attachment_id ); 68 if ( ! $metadata ) { 69 return false; 70 } 71 72 return array( $metadata['width'], $metadata['height'] ); 73 } 74 75 public static function extract_by_downloading_image( $dimensions, $url ) { 76 if ( is_array( $dimensions ) ) { 77 return $dimensions; 78 } 79 80 $url_hash = md5( $url ); 81 $transient_name = sprintf( 'amp_img_%s', $url_hash ); 82 $transient_expiry = 30 * DAY_IN_SECONDS; 83 $transient_fail = 'fail'; 84 85 $dimensions = get_transient( $transient_name ); 86 87 if ( is_array( $dimensions ) ) { 88 return $dimensions; 89 } elseif ( $transient_fail === $dimensions ) { 90 return false; 91 } 92 93 // Very simple lock to prevent stampedes 94 $transient_lock_name = sprintf( 'amp_lock_%s', $url_hash ); 95 if ( false !== get_transient( $transient_lock_name ) ) { 96 return false; 97 } 98 set_transient( $transient_lock_name, 1, MINUTE_IN_SECONDS ); 99 100 // Note to other developers: please don't use this class directly as it may not stick around forever... 58 add_filter( 'amp_extract_image_dimensions_batch', array( __CLASS__, 'extract_by_downloading_images' ), 999, 1 ); 59 60 do_action( 'amp_extract_image_dimensions_batch_callbacks_registered' ); 61 } 62 63 /** 64 * Extract dimensions from downloaded images (or transient/cached dimensions from downloaded images) 65 * 66 * @param array $dimensions Image urls mapped to dimensions. 67 * @param string $mode Whether image dimensions should be extracted concurrently or synchronously. 68 * @return array Dimensions mapped to image urls, or false if they could not be retrieved 69 */ 70 public static function extract_by_downloading_images( $dimensions, $mode = 'concurrent' ) { 71 $transient_expiration = 30 * DAY_IN_SECONDS; 72 73 $urls_to_fetch = array(); 74 $images = array(); 75 76 self::determine_which_images_to_fetch( $dimensions, $urls_to_fetch ); 77 self::fetch_images( $urls_to_fetch, $images, $mode ); 78 self::process_fetched_images( $urls_to_fetch, $images, $dimensions, $transient_expiration ); 79 80 return $dimensions; 81 } 82 83 /** 84 * Determine which images to fetch by checking for dimensions in transient/cache. 85 * Creates a short lived transient that acts as a semaphore so that another visitor 86 * doesn't trigger a remote fetch for the same image at the same time. 87 * 88 * @param array $dimensions Image urls mapped to dimensions. 89 * @param array $urls_to_fetch Urls of images to fetch because dimensions are not in transient/cache. 90 */ 91 private static function determine_which_images_to_fetch( &$dimensions, &$urls_to_fetch ) { 92 foreach ( $dimensions as $url => $value ) { 93 94 // Check whether some other callback attached to the filter already provided dimensions for this image. 95 if ( is_array( $value ) ) { 96 continue; 97 } 98 99 $url_hash = md5( $url ); 100 $transient_name = sprintf( 'amp_img_%s', $url_hash ); 101 $cached_dimensions = get_transient( $transient_name ); 102 103 // If we're able to retrieve the dimensions from a transient, set them and move on. 104 if ( is_array( $cached_dimensions ) ) { 105 $dimensions[ $url ] = array( 106 'width' => $cached_dimensions[0], 107 'height' => $cached_dimensions[1], 108 ); 109 continue; 110 } 111 112 // If the value in the transient reflects we couldn't get dimensions for this image the last time we tried, move on. 113 if ( self::STATUS_FAILED_LAST_ATTEMPT === $cached_dimensions ) { 114 $dimensions[ $url ] = false; 115 continue; 116 } 117 118 $transient_lock_name = sprintf( 'amp_lock_%s', $url_hash ); 119 120 // If somebody is already trying to extract dimensions for this transient right now, move on. 121 if ( false !== get_transient( $transient_lock_name ) ) { 122 $dimensions[ $url ] = false; 123 continue; 124 } 125 126 // Include the image as a url to fetch. 127 $urls_to_fetch[ $url ] = array(); 128 $urls_to_fetch[ $url ]['url'] = $url; 129 $urls_to_fetch[ $url ]['transient_name'] = $transient_name; 130 $urls_to_fetch[ $url ]['transient_lock_name'] = $transient_lock_name; 131 set_transient( $transient_lock_name, 1, MINUTE_IN_SECONDS ); 132 } 133 } 134 135 /** 136 * Fetch dimensions of remote images 137 * 138 * @param array $urls_to_fetch Image src urls to fetch. 139 * @param array $images Array to populate with results of image/dimension inspection. 140 * @param string $mode Whether image dimensions should be extracted concurrently or synchronously. 141 */ 142 private static function fetch_images( $urls_to_fetch, &$images, $mode ) { 143 // Use FasterImage when for compatible PHP versions 144 if ( 'synchronous' === $mode || 145 false === function_exists( 'curl_multi_exec' ) || 146 version_compare( PHP_VERSION, '5.4.0' ) < 0 147 ) { 148 self::fetch_images_via_fast_image( $urls_to_fetch, $images ); 149 } else { 150 self::fetch_images_via_faster_image( $urls_to_fetch, $images ); 151 } 152 } 153 154 /** 155 * Fetch images via FastImage library 156 * 157 * @param array $urls_to_fetch Image src urls to fetch. 158 * @param array $images Array to populate with results of image/dimension inspection. 159 */ 160 private static function fetch_images_via_fast_image( $urls_to_fetch, &$images ) { 101 161 if ( ! class_exists( 'FastImage' ) ) { 102 require_once( AMP__DIR__ . '/includes/lib/class-fastimage.php' ); 103 } 104 105 // TODO: look into using curl+stream (https://github.com/willwashburn/FasterImage) 106 $image = new FastImage( $url ); 107 $dimensions = $image->getSize(); 108 109 if ( ! is_array( $dimensions ) ) { 110 set_transient( $transient_name, $transient_fail, $transient_expiry ); 111 delete_transient( $transient_lock_name ); 112 return false; 113 } 114 115 set_transient( $transient_name, $dimensions, $transient_expiry ); 116 delete_transient( $transient_lock_name ); 117 return $dimensions; 162 require_once( AMP__DIR__ . '/includes/lib/fastimage/class-fastimage.php' ); 163 } 164 165 $image = new FastImage(); 166 $urls = array_keys( $urls_to_fetch ); 167 168 foreach ( $urls as $url ) { 169 $result = $image->load( $url ); 170 if ( false === $result ) { 171 $images[ $url ]['size'] = self::STATUS_IMAGE_EXTRACTION_FAILED; 172 } else { 173 $size = $image->getSize(); 174 $images[ $url ]['size'] = $size; 175 } 176 } 177 } 178 179 /** 180 * Fetch images via FasterImage library 181 * 182 * @param array $urls_to_fetch Image src urls to fetch. 183 * @param array $images Array to populate with results of image/dimension inspection. 184 */ 185 private static function fetch_images_via_faster_image( $urls_to_fetch, &$images ) { 186 $urls = array_keys( $urls_to_fetch ); 187 188 if ( ! function_exists( 'amp_get_fasterimage_client' ) ) { 189 require_once( AMP__DIR__ . '/includes/lib/fasterimage/amp-fasterimage.php' ); 190 } 191 192 $user_agent = apply_filters( 'amp_extract_image_dimensions_get_user_agent', self::get_default_user_agent() ); 193 $client = amp_get_fasterimage_client( $user_agent ); 194 $images = $client->batch( $urls ); 195 } 196 197 /** 198 * Determine success or failure of remote fetch, integrate fetched dimensions into url to dimension mapping, 199 * cache fetched dimensions via transient and release/delete semaphore transient 200 * 201 * @param array $urls_to_fetch List of image urls that were fetched and transient names corresponding to each (for unlocking semaphore, setting "real" transient). 202 * @param array $images Results of remote fetch mapping fetched image url to dimensions. 203 * @param array $dimensions Map of image url to dimensions to be updated with results of remote fetch. 204 * @param int $transient_expiration Duration image dimensions should exist in transient/cache. 205 */ 206 private static function process_fetched_images( $urls_to_fetch, $images, &$dimensions, $transient_expiration ) { 207 foreach ( $urls_to_fetch as $url_data ) { 208 $image_data = $images[ $url_data['url'] ]; 209 if ( self::STATUS_IMAGE_EXTRACTION_FAILED === $image_data['size'] ) { 210 $dimensions[ $url_data['url'] ] = false; 211 set_transient( $url_data['transient_name'], self::STATUS_FAILED_LAST_ATTEMPT, $transient_expiration ); 212 } else { 213 $dimensions[ $url_data['url'] ] = array( 214 'width' => $image_data['size'][0], 215 'height' => $image_data['size'][1], 216 ); 217 set_transient( 218 $url_data['transient_name'], 219 array( 220 $image_data['size'][0], 221 $image_data['size'][1], 222 ), 223 $transient_expiration 224 ); 225 } 226 delete_transient( $url_data['transient_lock_name'] ); 227 } 228 } 229 230 231 /** 232 * Get default user agent 233 * 234 * @return string 235 */ 236 public static function get_default_user_agent() { 237 return 'amp-wp, v' . AMP__VERSION . ', ' . get_site_url(); 118 238 } 119 239 } -
amp/trunk/includes/utils/class-amp-string-utils.php
r1478266 r1708734 5 5 return '' !== $haystack 6 6 && '' !== $needle 7 && $needle === substr( $haystack, -strlen( $needle ) );7 && substr( $haystack, -strlen( $needle ) ) === $needle; 8 8 } 9 9 } -
amp/trunk/jetpack-helper.php
r1364370 r1708734 57 57 $tz = get_option( 'gmt_offset' ); 58 58 $v = 'ext'; 59 $blog_url = parse_url( site_url() );59 $blog_url = AMP_WP_Utils::parse_url( site_url() ); 60 60 $srv = $blog_url['host']; 61 61 $j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ); … … 64 64 } 65 65 66 $data['host'] = rawurlencode( $_SERVER['HTTP_HOST'] );66 $data['host'] = isset( $_SERVER['HTTP_HOST'] ) ? rawurlencode( $_SERVER['HTTP_HOST'] ) : ''; // input var ok 67 67 $data['rand'] = 'RANDOM'; // amp placeholder 68 68 $data['ref'] = 'DOCUMENT_REFERRER'; // amp placeholder -
amp/trunk/readme.md
r1514204 r1708734 5 5 This plugin adds support for the [Accelerated Mobile Pages](https://www.ampproject.org) (AMP) Project, which is an open source initiative that aims to provide mobile optimized content that can load instantly everywhere. 6 6 7 With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/ 2016/01/01/amp-on/?amp=1`7 With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/?p=123&=1` 8 8 9 9 Note #1: that Pages and archives are not currently supported. … … 54 54 #### Logo Only 55 55 56 If you want to hide the site text and just show a logo, use the `amp_post_template_css` action. The following colo urs the title bar black, hides the site title, and replaces it with a centered logo:57 58 ``` 56 If you want to hide the site text and just show a logo, use the `amp_post_template_css` action. The following colors the title bar black, hides the site title, and replaces it with a centered logo: 57 58 ```php 59 59 add_action( 'amp_post_template_css', 'xyz_amp_additional_css_styles' ); 60 60 … … 62 62 // only CSS here please... 63 63 ?> 64 nav.amp-wp-title-bar {64 header.amp-wp-header { 65 65 padding: 12px 0; 66 66 background: #000; 67 67 } 68 nav.amp-wp-title-bar a {68 header.amp-wp-header a { 69 69 background-image: url( 'https://example.com/path/to/logo.png' ); 70 70 background-repeat: no-repeat; … … 80 80 ``` 81 81 82 Note: you will need to adjust the colo urs and sizes based on your brand.82 Note: you will need to adjust the colors and sizes based on your brand. 83 83 84 84 ### Template Tweaks … … 88 88 #### Featured Image 89 89 90 The default template does not display the featured image currently. There are many ways to add it, such as the snippet below: 91 92 ```php 93 add_action( 'pre_amp_render_post', 'xyz_amp_add_custom_actions' ); 94 function xyz_amp_add_custom_actions() { 95 add_filter( 'the_content', 'xyz_amp_add_featured_image' ); 96 } 97 98 function xyz_amp_add_featured_image( $content ) { 99 if ( has_post_thumbnail() ) { 100 // Just add the raw <img /> tag; our sanitizer will take care of it later. 101 $image = sprintf( '<p class="xyz-featured-image">%s</p>', get_the_post_thumbnail() ); 102 $content = $image . $content; 103 } 104 return $content; 90 The default template displays the featured image. If you don't want to display the featured image in your amp page, use the following code: 91 92 ```php 93 add_filter( 'amp_post_template_data', 'xyz_amp_remove_featured_image' ); 94 95 function xyz_amp_remove_featured_image( $data ) { 96 $data['featured_image'] = false; 97 return $data; 105 98 } 106 99 ``` … … 137 130 The plugin adds some default metadata to enable ["Rich Snippet" support](https://developers.google.com/structured-data/rich-snippets/articles). You can modify this using the `amp_post_template_metadata` filter. The following changes the type annotation to `NewsArticle` (from the default `BlogPosting`) and overrides the default Publisher Logo. 138 131 139 ``` 132 ```php 140 133 add_filter( 'amp_post_template_metadata', 'xyz_amp_modify_json_metadata', 10, 2 ); 141 134 … … 315 308 * You must trigger the `amp_post_template_head` action in the `<head>` section: 316 309 317 ``` 310 ```php 318 311 do_action( 'amp_post_template_head', $this ); 319 312 ``` … … 321 314 * You must trigger the `amp_post_template_footer` action right before the `</body>` tag: 322 315 323 ``` 316 ```php 324 317 do_action( 'amp_post_template_footer', $this ); 325 318 ``` … … 396 389 397 390 public function get_scripts() { 398 return array( 'amp-mustache' => 'https://cdn.ampproject.org/v0/amp-mustache-0.1.js' ); 391 return array( 392 'amp-mustache' => 'https://cdn.ampproject.org/v0/amp-mustache-0.1.js', 393 'amp-list' => 'https://cdn.ampproject.org/v0/amp-list-0.1.js', 394 ); 399 395 } 400 396 … … 491 487 ``` 492 488 489 ## Extracting Image Dimensions 490 491 AMP requires images to have width and height attributes. When these attributes aren't present in an image tag, AMP-WP will 492 attempt to determine them for the image. 493 494 ### Extraction Methods 495 496 #### Concurrent Dimension Extraction - PHP 5.3+ and cURL 497 If you're using PHP 5.3+ and have the cURL extension installed, AMP-WP will attempt to determine dimensions for all images 498 that need them concurrently. Only the minimum number of bytes required to determine the dimensions for a given image type 499 are retrieved. Dimensions are then cached via transients for subsequent requests. This is the fastest and therefore recommended method. 500 #### Sequential Dimension Extraction - PHP 5.2 or no cURL 501 If you're using PHP 5.2 or do not have the cURL extension installed, AMP-WP will attempt to determine image dimensions 502 sequentially. Only the minimum number of bytes required to determine the dimensions for a given image type are retrieved, 503 but the time it takes to retrieve each image's dimensions sequentially can still add up. Dimensions are then cached via transients for subsequent requests. 504 #### Custom Dimension Extraction 505 You can implement your own image dimension extraction method by adding a callback to the **amp_extract_image_dimensions_batch** filter. 506 507 amp_extract_image_dimensions_batch callback functions take a single argument, *$dimensions* by convention, which is a map/array of image urls to either an array containing the 508 dimensions of the image at the url (if another callback for the filter was able to determine them), or false if the dimensions have yet to be determined, e.g. 509 510 ```php 511 array( 512 'http://i0.wp.com/placehold.it/350x150.png' => array( 513 'width' => 350, 514 'height' => 150, 515 ), 516 'http://i0.wp.com/placehold.it/1024x768.png' => false, 517 ); 518 ``` 519 Your custom dimension extraction callback would iterate through the mappings contained in this single argument, determining 520 dimensions via your custom method for all image url keys whose values are not arrays of dimensions, e.g. 521 ```php 522 function my_custom_dimension_extraction_callback( $dimensions ) { 523 foreach ( $dimensions as $url => $value ) { 524 // Skip if dimensions have already been determined for this image. 525 if ( is_array( $value ) ) { 526 continue; 527 } 528 $width = <YOUR CUSTOM CODE TO DETERMINE WIDTH> 529 $height = <YOUR CUSTOM CODE TO DETERMINE HEIGHT> 530 $dimensions[ $url ] = array( 531 'width' => $width, 532 'height' => $height, 533 ); 534 } 535 536 return $dimensions; 537 ``` 538 Your callback needs to return $dimensions so that the value either cascades to the next callback that was added to the *amp_extract_image_dimensions_batch* filter or is 539 returned to the apply_filter() call (if there are no more unprocessed callbacks). 540 541 The default callback provided by WP-AMP described above, *extract_by_downloading_images*, will fire unless explicitly removed, so be sure 542 to remove it from the callback chain if you don't want it to, e.g. 543 544 ```php 545 remove_filter( 'amp_extract_image_dimensions_batch', array( 'AMP_Image_Dimension_Extractor', 'extract_by_downloading_images' ), 999, 1 ); 546 ```` 547 548 **Note that if you previously added a custom dimension extraction callback to the *amp_extract_image_dimensions* filter, 549 you need to update it to hook into the *amp_extract_image_dimensions_batch* filter instead and iterate over the key value 550 pairs in the single argument as per the example above.** 551 493 552 ## Analytics 494 553 495 To output proper analytics tags, you can use the `amp_post_template_analytics` filter: 496 497 ``` 554 There are two options you can follow to include analytics tags in your posts. 555 556 ### Plugin Analytics Options 557 558 The plugin defines an analytics option to enable the addition of 559 [amp-analytics](https://www.ampproject.org/docs/reference/components/amp-analytics) in your posts. When the plugin is 560 active, an AMP top-level menu appears in the Dashboard with one inner sub-menu called 'Analytics': 561 562  563 564 Selecting the `Analytics` sub-menu in the AMP options menu takes us to an Analytics Options entry page, where we can 565 define the analytics tags we want to have, by specifying the vendor type (e.g. Parsely), and the associated JSON 566 configuration. 567 568  569 570 Notice that the goal of this option of the plugin is to provide a simple mechanism to insert analytics tags; 571 it provides very simple validation based solely on the validity of the JSON string provided. It is the users 572 responsibility to make sure that the values in the configuration string and the vendor type used are coherent with 573 the analytics requirements of their site . Please review the documentation in the [AMP project ](https://github.com/ampproject/amphtml/blob/master/extensions/amp-analytics/amp-analytics.md) and in [AMPByExample](https://ampbyexample.com/components/amp-analytics/). 574 575 The AMP Analytics options entry form provides a very simple validation feedback mechanism: if the JSON configuration 576 string entered is invalid (i.e. not valid JSON), an error message (in red) is displayed below the title of the 577 options window and the entry form is reloaded: 578 579  580 581 And, if the configuration provided is actually a valid JSON string, a success message (in green) is displayed at the 582 top of the window below the title, and again the entry form is reloaded. 583 584  585 586 ### Manually 587 588 Alaternatively, you can use the `amp_post_template_analytics` filter: 589 590 ```php 498 591 add_filter( 'amp_post_template_analytics', 'xyz_amp_add_custom_analytics' ); 499 592 function xyz_amp_add_custom_analytics( $analytics ) { … … 557 650 If you want a custom template for your post type: 558 651 559 ``` 652 ```php 560 653 add_filter( 'amp_post_template_file', 'xyz_amp_set_review_template', 10, 3 ); 561 654 -
amp/trunk/readme.txt
r1673399 r1708734 2 2 Contributors: batmoo, joen, automattic, potatomaster 3 3 Tags: amp, mobile 4 Requires at least: 4. 44 Requires at least: 4.7 5 5 Tested up to: 4.8 6 Stable tag: 0. 4.26 Stable tag: 0.5 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 14 14 This plugin adds support for the [Accelerated Mobile Pages](https://www.ampproject.org) (AMP) Project, which is an an open source initiative that aims to provide mobile optimized content that can load instantly everywhere. 15 15 16 With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/ 2016/01/01/amp-on/?amp=1`16 With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/?p=123&=1` 17 17 18 18 Note #1: that Pages and archives are not currently supported. Pages support is being worked on. … … 32 32 = How do I customize the AMP output for my site? = 33 33 34 You can tweak a few things like colo urs from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`.34 You can tweak a few things like colors from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`. 35 35 36 36 For deeper level customizations, please see the readme at https://github.com/Automattic/amp-wp/blob/master/readme.md … … 54 54 == Changelog == 55 55 56 = 0.5 (2017-08-04) = 57 58 - Whitelist Sanitizer: Replace Blacklist Sanitizer with a whitelist-based approach using the AMP spec (props delputnam) 59 - Image Dimensions: Replace fastimage with fasterimage for PHP 5.4+. Enables faster downloads and wider support (props gititon) 60 - Embed Handlers: Added support for Vimeo, SoundCloud, Pinterest (props amedina) and PlayBuzz (props lysk88) 61 - Analytics: UI for easier addition of analytics tags (props amedina) 62 - Fix: parse query strings properly (props amyevans) 63 - Fix: Old slug redirect for AMP URLs (props rahulsprajapati) 64 - Fix: Handle issues with data uri images in CSS (props trepmal) 65 - Fix: Add amp-video js for amp-video tags (props ptbello) 66 - Fix: Output CSS for feature image (props mjangda) 67 - Fix: Fix attribute when adding AMP Mustache lib (props luigitec) 68 - Fix: Various documentation updates (props piersb, bhhaskin) 69 - Fix: PHP Warnings from `register_customizer_ui` (props jahvi) 70 - Fix: Coding Standards (props paulschreiber) 71 56 72 = 0.4.2 (2016-10-13) = 57 73 … … 73 89 - New template: spiffy, shiny, and has the fresh theme smell (props allancole and the Automattic Theme Team). 74 90 - *Warning*: The template update has potential breaking changes. Please see https://wordpress.org/support/topic/v0-4-whats-new-and-possible-breaking-changes/ 75 - AMP Customizer: Pick your colo urs and make the template your own (props DrewAPicture and 10up)91 - AMP Customizer: Pick your colors and make the template your own (props DrewAPicture and 10up) 76 92 - Fix: support for inline styles (props coreymckrill). 77 93 - Fix: no more fatal errors when tags not supported by post type (props david-binda) -
amp/trunk/templates/footer.php
r1510272 r1708734 3 3 <h2><?php echo esc_html( $this->get( 'blog_name' ) ); ?></h2> 4 4 <p> 5 <a href="<?php echo esc_url( __( 'https://wordpress.org/', 'amp' ) ); ?>"><?php printf( __( 'Powered by %s', 'amp' ), 'WordPress'); ?></a>5 <a href="<?php echo esc_url( esc_html__( 'https://wordpress.org/', 'amp' ) ); ?>"><?php echo esc_html( sprintf( __( 'Powered by %s', 'amp' ), 'WordPress' ) ); ?></a> 6 6 </p> 7 <a href="#top" class="back-to-top"><?php _e( 'Back to top', 'amp' ); ?></a>7 <a href="#top" class="back-to-top"><?php esc_html_e( 'Back to top', 'amp' ); ?></a> 8 8 </div> 9 9 </footer> -
amp/trunk/templates/header-bar.php
r1510272 r1708734 3 3 <a href="<?php echo esc_url( $this->get( 'home_url' ) ); ?>"> 4 4 <?php $site_icon_url = $this->get( 'site_icon_url' ); 5 if ( $site_icon_url ) : ?>5 if ( $site_icon_url ) : ?> 6 6 <amp-img src="<?php echo esc_url( $site_icon_url ); ?>" width="32" height="32" class="amp-wp-site-icon"></amp-img> 7 7 <?php endif; ?> -
amp/trunk/templates/meta-author.php
r1510272 r1708734 1 1 <?php $post_author = $this->get( 'post_author' ); ?> 2 2 <?php if ( $post_author ) : ?> 3 <?php $author_avatar_url = get_avatar_url( $post_author->user_email, array( 'size' => 24 ) ); ?>4 3 <div class="amp-wp-meta amp-wp-byline"> 5 4 <?php if ( function_exists( 'get_avatar_url' ) ) : ?> 6 <amp-img src="<?php echo esc_url( $author_avatar_url); ?>" width="24" height="24" layout="fixed"></amp-img>5 <amp-img src="<?php echo esc_url( get_avatar_url( $post_author->user_email, array( 'size' => 24 ) ) ); ?>" width="24" height="24" layout="fixed"></amp-img> 7 6 <?php endif; ?> 8 7 <span class="amp-wp-author author vcard"><?php echo esc_html( $post_author->display_name ); ?></span> -
amp/trunk/templates/style.php
r1510272 r1708734 135 135 /** site icon is 32px **/ 136 136 background-color: <?php echo sanitize_hex_color( $header_color ); ?>; 137 border: 1px solid <?php echo sanitize_hex_color( $header_color ); ?>;137 border: 1px solid <?php echo sanitize_hex_color( $header_color ); ?>; 138 138 border-radius: 50%; 139 139 position: absolute;
Note: See TracChangeset
for help on using the changeset viewer.