Plugin Directory

Changeset 1708734


Ignore:
Timestamp:
08/05/2017 03:59:01 AM (8 years ago)
Author:
batmoo
Message:

Update to 0.5

https://github.com/Automattic/amp-wp/releases/tag/0.5

Location:
amp/trunk
Files:
34 added
1 deleted
31 edited

Legend:

Unmodified
Added
Removed
  • amp/trunk/amp.php

    r1514204 r1708734  
    66 * Author: Automattic
    77 * Author URI: https://automattic.com
    8  * Version: 0.4.2
     8 * Version: 0.5
    99 * Text Domain: amp
    1010 * Domain Path: /languages/
     
    1414define( 'AMP__FILE__', __FILE__ );
    1515define( 'AMP__DIR__', dirname( __FILE__ ) );
    16 define( 'AMP__VERSION', '0.4.2' );
     16define( 'AMP__VERSION', '0.5' );
    1717
    1818require_once( AMP__DIR__ . '/back-compat/back-compat.php' );
     
    6161    add_filter( 'request', 'amp_force_query_var_value' );
    6262    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' );
    6366
    6467    if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
     
    115118    require_once( AMP__DIR__ . '/includes/amp-post-template-actions.php' );
    116119    require_once( AMP__DIR__ . '/includes/amp-post-template-functions.php' );
     120    amp_post_template_init_hooks();
    117121}
    118122
     
    122126
    123127function amp_render() {
     128    $post_id = get_queried_object_id();
     129    amp_render_post( $post_id );
     130    exit;
     131}
     132
     133function amp_render_post( $post_id ) {
     134    $post = get_post( $post_id );
     135    if ( ! $post ) {
     136        return;
     137    }
     138
    124139    amp_load_classes();
    125140
    126     $post_id = get_queried_object_id();
    127141    do_action( 'pre_amp_render_post', $post_id );
    128142
     
    130144    $template = new AMP_Post_Template( $post_id );
    131145    $template->load();
    132     exit;
    133146}
    134147
     
    158171}
    159172add_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 */
     182function 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  
    4747        // Our custom panels only need to go for AMP Customizer requests though
    4848        if ( self::is_amp_customizer() ) {
    49             if ( empty( $_GET['url'] ) ) {
     49            if ( empty( $_GET['url'] ) ) { // input var ok
    5050                $wp_customize->set_preview_url( amp_admin_get_preview_permalink() );
    5151            }
     
    157157
    158158    public function force_mobile_preview( $devices ) {
    159         if ( isset( $devices[ 'mobile' ] ) ) {
     159        if ( isset( $devices['mobile'] ) ) {
    160160            $devices['mobile']['default'] = true;
    161161            unset( $devices['desktop']['default'] );
     
    166166
    167167    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
    169169    }
    170170}
  • amp/trunk/includes/admin/functions.php

    r1510272 r1708734  
    11<?php
    22// Callbacks for adding AMP-related things to the admin.
     3
     4require_once( AMP__DIR__ . '/includes/options/class-amp-options-menu.php' );
     5require_once( AMP__DIR__ . '/includes/options/views/class-amp-options-manager.php' );
    36
    47define( 'AMP_CUSTOMIZER_QUERY_VAR', 'customize_amp' );
     
    2326}
    2427
    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_slug
    42     );
    43 }
    44 
    4528function amp_admin_get_preview_permalink() {
    4629    /**
    47      * Filter the post type to retrieve the latest of for use in the AMP template customizer.
     30     * Filter the post type to retrieve the latest for use in the AMP template customizer.
    4831     *
    4932     * @param string $post_type Post type slug. Default 'post'.
     
    7053    return amp_get_permalink( $post_id );
    7154}
     55
     56/**
     57 * Registers a submenu page to access the AMP template editor panel in the Customizer.
     58 */
     59function 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 */
     79function 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}
     92add_action( 'wp_loaded', 'amp_add_options_menu' );
     93
     94function 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}
     111add_filter( 'amp_post_template_analytics', 'amp_add_custom_analytics' );
  • amp/trunk/includes/amp-helper-functions.php

    r1510272 r1708734  
    88    }
    99
    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 {
    1114        $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 ) );
    1415    }
    1516
     
    4041 */
    4142function 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
    4247    return false !== get_query_var( AMP_QUERY_VAR, false );
    4348}
  • amp/trunk/includes/amp-post-template-actions.php

    r1510272 r1708734  
    22// Callbacks for adding content to an AMP template
    33
    4 add_action( 'amp_post_template_head', 'amp_post_template_add_title' );
     4function 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
    516function amp_post_template_add_title( $amp_template ) {
    617    ?>
     
    920}
    1021
    11 add_action( 'amp_post_template_head', 'amp_post_template_add_canonical' );
    1222function amp_post_template_add_canonical( $amp_template ) {
    1323    ?>
     
    1626}
    1727
    18 add_action( 'amp_post_template_head', 'amp_post_template_add_scripts' );
    1928function amp_post_template_add_scripts( $amp_template ) {
    2029    $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>
    2333    <?php endforeach; ?>
    2434    <script src="<?php echo esc_url( $amp_template->get( 'amp_runtime_script' ) ); ?>" async></script>
     
    2636}
    2737
    28 add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' );
    2938function amp_post_template_add_fonts( $amp_template ) {
    3039    $font_urls = $amp_template->get( 'font_urls', array() );
    31     foreach( $font_urls as $slug => $url ) : ?>
     40    foreach ( $font_urls as $slug => $url ) : ?>
    3241        <link rel="stylesheet" href="<?php echo esc_url( $url ); ?>">
    3342    <?php endforeach;
    3443}
    3544
    36 add_action( 'amp_post_template_head', 'amp_post_template_add_boilerplate_css' );
    3745function amp_post_template_add_boilerplate_css( $amp_template ) {
    3846    ?>
     
    4149}
    4250
    43 add_action( 'amp_post_template_head', 'amp_post_template_add_schemaorg_metadata' );
    4451function amp_post_template_add_schemaorg_metadata( $amp_template ) {
    4552    $metadata = $amp_template->get( 'metadata' );
     
    4855    }
    4956    ?>
    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>
    5158    <?php
    5259}
    5360
    54 add_action( 'amp_post_template_css', 'amp_post_template_add_styles', 99 );
    5561function amp_post_template_add_styles( $amp_template ) {
    5662    $styles = $amp_template->get( 'post_amp_styles' );
     
    5864        echo '/* Inline styles */' . PHP_EOL;
    5965        foreach ( $styles as $selector => $declarations ) {
    60             $declarations = implode( ";", $declarations ) . ";";
     66            $declarations = implode( ';', $declarations ) . ';';
    6167            printf( '%1$s{%2$s}', $selector, $declarations );
    6268        }
     
    6470}
    6571
    66 add_action( 'amp_post_template_data', 'amp_post_template_add_analytics_script' );
    6772function amp_post_template_add_analytics_script( $data ) {
    6873    if ( ! empty( $data['amp_analytics'] ) ) {
     
    7277}
    7378
    74 add_action( 'amp_post_template_footer', 'amp_post_template_add_analytics_data' );
    7579function amp_post_template_add_analytics_data( $amp_template ) {
    7680    $analytics_entries = $amp_template->get( 'amp_analytics' );
     
    8185    foreach ( $analytics_entries as $id => $analytics_entry ) {
    8286        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' );
    8488            continue;
    8589        }
    86 
    8790        $script_element = AMP_HTML_Utils::build_tag( 'script', array(
    8891            'type' => 'application/json',
    89         ), json_encode( $analytics_entry['config_data'] ) );
     92        ), wp_json_encode( $analytics_entry['config_data'] ) );
    9093
    9194        $amp_analytics_attr = array_merge( array(
  • amp/trunk/includes/amp-post-template-functions.php

    r1514204 r1708734  
    99
    1010        // 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 ) ) {
    1212            return $color;
    1313        }
  • amp/trunk/includes/class-amp-content.php

    r1514204 r1708734  
    6464
    6565            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' );
    6767                continue;
    6868            }
     
    100100        foreach ( $sanitizer_classes as $sanitizer_class => $args ) {
    101101            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' );
    103103                continue;
    104104            }
     
    107107
    108108            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' );
    110110                continue;
    111111            }
  • amp/trunk/includes/class-amp-post-template.php

    r1514204 r1708734  
    44require_once( AMP__DIR__ . '/includes/utils/class-amp-html-utils.php' );
    55require_once( AMP__DIR__ . '/includes/utils/class-amp-string-utils.php' );
     6require_once( AMP__DIR__ . '/includes/utils/class-amp-wp-utils.php' );
    67
    78require_once( AMP__DIR__ . '/includes/class-amp-content.php' );
     
    910require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-style-sanitizer.php' );
    1011require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-blacklist-sanitizer.php' );
     12require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php' );
    1113require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-img-sanitizer.php' );
    1214require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-video-sanitizer.php' );
    1315require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-iframe-sanitizer.php' );
    1416require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-audio-sanitizer.php' );
     17require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-playbuzz-sanitizer.php' );
    1518
    1619require_once( AMP__DIR__ . '/includes/embeds/class-amp-twitter-embed.php' );
    1720require_once( AMP__DIR__ . '/includes/embeds/class-amp-youtube-embed.php' );
     21require_once( AMP__DIR__ . '/includes/embeds/class-amp-dailymotion-embed.php' );
     22require_once( AMP__DIR__ . '/includes/embeds/class-amp-vimeo-embed.php' );
     23require_once( AMP__DIR__ . '/includes/embeds/class-amp-soundcloud-embed.php' );
    1824require_once( AMP__DIR__ . '/includes/embeds/class-amp-gallery-embed.php' );
    1925require_once( AMP__DIR__ . '/includes/embeds/class-amp-instagram-embed.php' );
    2026require_once( AMP__DIR__ . '/includes/embeds/class-amp-vine-embed.php' );
    2127require_once( AMP__DIR__ . '/includes/embeds/class-amp-facebook-embed.php' );
     28require_once( AMP__DIR__ . '/includes/embeds/class-amp-pinterest-embed.php' );
    2229
    2330class AMP_Post_Template {
     
    7077                'merriweather' => 'https://fonts.googleapis.com/css?family=Merriweather:400,400italic,700,700italic',
    7178            ),
     79
     80            'post_amp_styles' => array(),
    7281
    7382            /**
     
    8291             */
    8392            'amp_analytics' => apply_filters( 'amp_post_template_analytics', array(), $this->post ),
    84         );
     93            );
    8594
    8695        $this->build_post_content();
     
    96105            return $this->data[ $property ];
    97106        } 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' );
    99108        }
    100109
     
    224233                'AMP_Twitter_Embed_Handler' => array(),
    225234                'AMP_YouTube_Embed_Handler' => array(),
     235                'AMP_DailyMotion_Embed_Handler' => array(),
     236                'AMP_Vimeo_Embed_Handler' => array(),
     237                'AMP_SoundCloud_Embed_Handler' => array(),
    226238                'AMP_Instagram_Embed_Handler' => array(),
    227239                'AMP_Vine_Embed_Handler' => array(),
    228240                'AMP_Facebook_Embed_Handler' => array(),
     241                'AMP_Pinterest_Embed_Handler' => array(),
    229242                'AMP_Gallery_Embed_Handler' => array(),
    230243            ), $this->post ),
    231244            apply_filters( 'amp_content_sanitizers', array(
    232245                 'AMP_Style_Sanitizer' => array(),
    233                  'AMP_Blacklist_Sanitizer' => array(),
     246                 // 'AMP_Blacklist_Sanitizer' => array(),
    234247                 'AMP_Img_Sanitizer' => array(),
    235248                 'AMP_Video_Sanitizer' => array(),
    236249                 'AMP_Audio_Sanitizer' => array(),
     250                 'AMP_Playbuzz_Sanitizer' => array(),
    237251                 'AMP_Iframe_Sanitizer' => array(
    238252                     'add_placeholder' => true,
    239253                 ),
     254                 'AMP_Tag_And_Attribute_Sanitizer' => array(),
    240255            ), $this->post ),
    241256            array(
     
    246261        $this->add_data_by_key( 'post_amp_content', $amp_content->get_amp_content() );
    247262        $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() );
    249264    }
    250265
     
    275290            array( 'AMP_Img_Sanitizer' => array() ),
    276291            array(
    277                 'content_max_width' => $this->get( 'content_max_width' )
     292                'content_max_width' => $this->get( 'content_max_width' ),
    278293            )
    279294        );
     
    289304
    290305        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 );
    292307        }
    293308    }
     
    382397        $file = apply_filters( 'amp_post_template_file', $file, $template_type, $this->post );
    383398        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' );
    385400            return;
    386401        }
  • amp/trunk/includes/embeds/class-amp-gallery-embed.php

    r1364370 r1708734  
    4040            'include'    => '',
    4141            'exclude'    => '',
    42             'size'       => array( $this->args['width'], $this->args['height'] )
     42            'size'       => array( $this->args['width'], $this->args['height'] ),
    4343        ), $attr, 'gallery' );
    4444
  • amp/trunk/includes/embeds/class-amp-instagram-embed.php

    r1478266 r1708734  
    5353
    5454    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 ) ) );
    5656    }
    5757
  • amp/trunk/includes/embeds/class-amp-vine-embed.php

    r1337793 r1708734  
    2929
    3030    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 ) ) );
    3232    }
    3333
  • amp/trunk/includes/embeds/class-amp-youtube-embed.php

    r1510272 r1708734  
    4949        if ( isset( $attr[0] ) ) {
    5050            $url = ltrim( $attr[0] , '=' );
    51         } elseif ( function_exists ( 'shortcode_new_to_old_params' ) ) {
     51        } elseif ( function_exists( 'shortcode_new_to_old_params' ) ) {
    5252            $url = shortcode_new_to_old_params( $attr );
    5353        }
     
    9393    private function get_video_id_from_url( $url ) {
    9494        $video_id = false;
    95         $parsed_url = parse_url( $url );
     95        $parsed_url = AMP_WP_Utils::parse_url( $url );
    9696
    9797        if ( self::SHORT_URL_HOST === substr( $parsed_url['host'], -strlen( self::SHORT_URL_HOST ) ) ) {
     
    114114            $parts = explode( '/', $parsed_url['path'] );
    115115
    116             if ( in_array( $parts[1], array( 'v', 'e', 'embed' ) ) ) {
     116            if ( in_array( $parts[1], array( 'v', 'e', 'embed' ), true ) ) {
    117117                $video_id = $parts[2];
    118118            }
  • amp/trunk/includes/sanitizers/class-amp-base-sanitizer.php

    r1510272 r1708734  
    4343
    4444        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'] ) ) {
    4646                $percentage = absint( $value ) / 100;
    47                 return round( $percentage * $this->args[ 'content_max_width'] );
     47                return round( $percentage * $this->args['content_max_width'] );
    4848            }
    4949        }
  • amp/trunk/includes/sanitizers/class-amp-blacklist-sanitizer.php

    r1510272 r1708734  
    88 * See following for blacklist:
    99 *     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 *
    1013 */
    1114class AMP_Blacklist_Sanitizer extends AMP_Base_Sanitizer {
     
    2932
    3033    private function strip_attributes_recursive( $node, $bad_attributes, $bad_protocols ) {
    31         if ( $node->nodeType !== XML_ELEMENT_NODE ) {
     34        if ( XML_ELEMENT_NODE !== $node->nodeType ) {
    3235            return;
    3336        }
     
    3740        // Some nodes may contain valid content but are themselves invalid.
    3841        // Remove the node but preserve the children.
    39         if ( 'font' === $node_name ) {
     42        if ( 'font' === $node_name ) {
    4043            $this->replace_node_with_children( $node, $bad_attributes, $bad_protocols );
    4144            return;
     
    5053                $attribute = $node->attributes->item( $i );
    5154                $attribute_name = strtolower( $attribute->name );
    52                 if ( in_array( $attribute_name, $bad_attributes ) ) {
     55                if ( in_array( $attribute_name, $bad_attributes, true ) ) {
    5356                    $node->removeAttribute( $attribute_name );
    5457                    continue;
     
    5659
    5760                // 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 ) {
    5962                    $node->removeAttribute( $attribute_name );
    6063                    continue;
     
    125128        $href = $node->getAttribute( 'href' );
    126129
    127         // If no href is set and this isn't an anchor, it's invalid
    128130        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' );
    136134        }
    137135
     
    151149
    152150        if ( false === filter_var( $href, FILTER_VALIDATE_URL )
    153             && ! in_array( $protocol, $special_protocols ) ) {
     151            && ! in_array( $protocol, $special_protocols, true ) ) {
    154152            return false;
    155153        }
    156154
    157         if ( ! in_array( $protocol, $valid_protocols ) ) {
     155        if ( ! in_array( $protocol, $valid_protocols, true ) ) {
    158156            return false;
    159157        }
  • amp/trunk/includes/sanitizers/class-amp-iframe-sanitizer.php

    r1478266 r1708734  
    9898                    break;
    9999
    100 
    101100                case 'frameborder':
    102101                    if ( '0' !== $value && '1' !== $value ) {
     
    118117        }
    119118
    120         if ( ! isset( $out[ 'sandbox' ] ) ) {
    121             $out[ 'sandbox' ] = self::SANDBOX_DEFAULTS;
     119        if ( ! isset( $out['sandbox'] ) ) {
     120            $out['sandbox'] = self::SANDBOX_DEFAULTS;
    122121        }
    123122
  • amp/trunk/includes/sanitizers/class-amp-img-sanitizer.php

    r1510272 r1708734  
    1919
    2020    public function sanitize() {
     21
    2122        $nodes = $this->dom->getElementsByTagName( self::$tag );
     23        $need_dimensions = array();
     24
    2225        $num_nodes = $nodes->length;
     26
    2327        if ( 0 === $num_nodes ) {
    2428            return;
     
    2731        for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
    2832            $node = $nodes->item( $i );
    29             $old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
    3033
    31             if ( empty( $old_attributes['src'] ) ) {
     34            if ( ! $node->hasAttribute( 'src' ) || '' === $node->getAttribute( 'src' ) ) {
    3235                $node->parentNode->removeChild( $node );
    3336                continue;
    3437            }
    3538
    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        }
    3746
    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'] );
    4473                }
    4574            }
     75        }
     76    }
    4677
    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    }
    5196
    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 );
    53106            }
    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 );
    66107        }
    67108    }
     
    104145    private function is_gif_url( $url ) {
    105146        $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;
    108149    }
    109150}
  • amp/trunk/includes/sanitizers/class-amp-style-sanitizer.php

    r1512429 r1708734  
    1919
    2020    private function collect_styles_recursive( $node ) {
    21         if ( $node->nodeType !== XML_ELEMENT_NODE ) {
     21        if ( XML_ELEMENT_NODE !== $node->nodeType ) {
    2222            return;
    2323        }
     
    5757        }
    5858
    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
    6164        sort( $styles );
    6265
  • amp/trunk/includes/sanitizers/class-amp-video-sanitizer.php

    r1478266 r1708734  
    1010
    1111    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    }
    1223
    1324    public function sanitize() {
     
    5162                $node->parentNode->replaceChild( $new_node, $node );
    5263            }
     64
     65            $this->did_convert_elements = true;
    5366        }
    5467    }
  • amp/trunk/includes/settings/class-amp-customizer-design-settings.php

    r1510272 r1708734  
    2424            'default'           => self::DEFAULT_HEADER_COLOR,
    2525            'sanitize_callback' => 'sanitize_hex_color',
    26             'transport'         => 'postMessage'
     26            'transport'         => 'postMessage',
    2727        ) );
    2828
     
    3232            'default'           => self::DEFAULT_HEADER_BACKGROUND_COLOR,
    3333            'sanitize_callback' => 'sanitize_hex_color',
    34             'transport'         => 'postMessage'
     34            'transport'         => 'postMessage',
    3535        ) );
    3636
     
    4040            'default'           => self::DEFAULT_COLOR_SCHEME,
    4141            'sanitize_callback' => array( __CLASS__ , 'sanitize_color_scheme' ),
    42             'transport'         => 'postMessage'
     42            'transport'         => 'postMessage',
    4343        ) );
    4444    }
    4545
    46     public function register_customizer_ui( $wp_customize ) {
     46    public static function register_customizer_ui( $wp_customize ) {
    4747        $wp_customize->add_section( 'amp_design', array(
    4848            'title' => __( 'Design', 'amp' ),
     
    5656                'label'    => __( 'Header Text Color', 'amp' ),
    5757                'section'  => 'amp_design',
    58                 'priority' => 10
     58                'priority' => 10,
    5959            ) )
    6060        );
     
    6666                'label'    => __( 'Header Background & Link Color', 'amp' ),
    6767                'section'  => 'amp_design',
    68                 'priority' => 20
     68                'priority' => 20,
    6969            ) )
    7070        );
     
    110110    protected static function get_color_scheme_names() {
    111111        return array(
    112             'light'   => __( 'Light', 'amp'),
     112            'light'   => __( 'Light', 'amp' ),
    113113            'dark'    => __( 'Dark', 'amp' ),
    114114        );
     
    130130                'muted_text_color' => '#b1b1b1',
    131131                'border_color'     => '#707070',
    132             )
     132            ),
    133133        );
    134134    }
     
    148148        $scheme_slugs = array_keys( $schemes );
    149149
    150         if ( ! in_array( $value, $scheme_slugs ) ) {
     150        if ( ! in_array( $value, $scheme_slugs, true ) ) {
    151151            $value = self::DEFAULT_COLOR_SCHEME;
    152152        }
  • amp/trunk/includes/utils/class-amp-dom-utils.php

    r1514204 r1708734  
    9696            // Not all are valid AMP, but we include them for completeness.
    9797            $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',
    99116            );
    100117        }
    101118
    102         return in_array( $tag, $self_closing_tags );
     119        return in_array( $tag, $self_closing_tags, true );
    103120    }
    104121}
  • amp/trunk/includes/utils/class-amp-html-utils.php

    r1337793 r1708734  
    1818        return implode( ' ', $string );
    1919    }
     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    }
    2032}
  • amp/trunk/includes/utils/class-amp-image-dimension-extractor.php

    r1357059 r1708734  
    33class AMP_Image_Dimension_Extractor {
    44    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 ) {
    79        if ( ! self::$callbacks_registered ) {
    810            self::register_callbacks();
    911        }
    1012
    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;
    1725    }
    1826
     
    3038        }
    3139
    32         $parsed = parse_url( $url );
     40        $parsed = AMP_WP_Utils::parse_url( $url );
    3341        if ( ! isset( $parsed['host'] ) ) {
    3442            $path = '';
     
    4856        self::$callbacks_registered = true;
    4957
    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 ) {
    101161        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();
    118238    }
    119239}
  • amp/trunk/includes/utils/class-amp-string-utils.php

    r1478266 r1708734  
    55        return '' !== $haystack
    66            && '' !== $needle
    7             && $needle === substr( $haystack, -strlen( $needle ) );
     7            && substr( $haystack, -strlen( $needle ) ) === $needle;
    88    }
    99}
  • amp/trunk/jetpack-helper.php

    r1364370 r1708734  
    5757        $tz = get_option( 'gmt_offset' );
    5858        $v = 'ext';
    59         $blog_url = parse_url( site_url() );
     59        $blog_url = AMP_WP_Utils::parse_url( site_url() );
    6060        $srv = $blog_url['host'];
    6161        $j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION );
     
    6464    }
    6565
    66     $data['host'] = rawurlencode( $_SERVER['HTTP_HOST'] );
     66    $data['host'] = isset( $_SERVER['HTTP_HOST'] ) ? rawurlencode( $_SERVER['HTTP_HOST'] ) : ''; // input var ok
    6767    $data['rand'] = 'RANDOM'; // amp placeholder
    6868    $data['ref'] = 'DOCUMENT_REFERRER'; // amp placeholder
  • amp/trunk/readme.md

    r1514204 r1708734  
    55This 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.
    66
    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`
     7With 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&amp=1`
    88
    99Note #1: that Pages and archives are not currently supported.
     
    5454#### Logo Only
    5555
    56 If you want to hide the site text and just show a logo, use the `amp_post_template_css` action. The following colours the title bar black, hides the site title, and replaces it with a centered logo:
    57 
    58 ```
     56If 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
    5959add_action( 'amp_post_template_css', 'xyz_amp_additional_css_styles' );
    6060
     
    6262    // only CSS here please...
    6363    ?>
    64     nav.amp-wp-title-bar {
     64    header.amp-wp-header {
    6565        padding: 12px 0;
    6666        background: #000;
    6767    }
    68     nav.amp-wp-title-bar a {
     68    header.amp-wp-header a {
    6969        background-image: url( 'https://example.com/path/to/logo.png' );
    7070        background-repeat: no-repeat;
     
    8080```
    8181
    82 Note: you will need to adjust the colours and sizes based on your brand.
     82Note: you will need to adjust the colors and sizes based on your brand.
    8383
    8484### Template Tweaks
     
    8888#### Featured Image
    8989
    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;
     90The 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
     93add_filter( 'amp_post_template_data', 'xyz_amp_remove_featured_image' );
     94
     95function xyz_amp_remove_featured_image( $data ) {
     96    $data['featured_image'] = false;
     97    return $data;
    10598}
    10699```
     
    137130The 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.
    138131
    139 ```
     132```php
    140133add_filter( 'amp_post_template_metadata', 'xyz_amp_modify_json_metadata', 10, 2 );
    141134
     
    315308* You must trigger the `amp_post_template_head` action in the `<head>` section:
    316309
    317 ```
     310```php
    318311do_action( 'amp_post_template_head', $this );
    319312```
     
    321314* You must trigger the `amp_post_template_footer` action right before the `</body>` tag:
    322315
    323 ```
     316```php
    324317do_action( 'amp_post_template_footer', $this );
    325318```
     
    396389
    397390    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        );
    399395    }
    400396
     
    491487```
    492488
     489## Extracting Image Dimensions
     490
     491AMP requires images to have width and height attributes. When these attributes aren't present in an image tag, AMP-WP will
     492attempt to determine them for the image.
     493
     494### Extraction Methods
     495
     496#### Concurrent Dimension Extraction - PHP 5.3+ and cURL
     497If you're using PHP 5.3+ and have the cURL extension installed, AMP-WP will attempt to determine dimensions for all images
     498that need them concurrently. Only the minimum number of bytes required to determine the dimensions for a given image type
     499are 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
     501If you're using PHP 5.2 or do not have the cURL extension installed, AMP-WP will attempt to determine image dimensions
     502sequentially. Only the minimum number of bytes required to determine the dimensions for a given image type are retrieved,
     503but 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
     505You can implement your own image dimension extraction method by adding a callback to the **amp_extract_image_dimensions_batch** filter.
     506
     507amp_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
     508dimensions 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
     511array(
     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```
     519Your custom dimension extraction callback would iterate through the mappings contained in this single argument, determining
     520dimensions via your custom method for all image url keys whose values are not arrays of dimensions, e.g.
     521```php
     522function 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```
     538Your 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
     539returned to the apply_filter() call (if there are no more unprocessed callbacks).
     540
     541The default callback provided by WP-AMP described above, *extract_by_downloading_images*, will fire unless explicitly removed, so be sure
     542to 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,
     549you need to update it to hook into the *amp_extract_image_dimensions_batch* filter instead and iterate over the key value
     550pairs in the single argument as per the example above.**
     551
    493552## Analytics
    494553
    495 To output proper analytics tags, you can use the `amp_post_template_analytics` filter:
    496 
    497 ```
     554There are two options you can follow to include analytics tags in your posts.
     555
     556### Plugin Analytics Options
     557
     558The 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 ![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/amp-options-analytics.png)
     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 ![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/analytics-option-entries.png)
     569
     570Notice 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![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/invalid-input.png)
     580   
     581And, if the configuration provided is actually a valid JSON string, a success message (in green) is displayed at the
     582top of the window below the title, and again the entry form is reloaded.
     583
     584![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/options-saved.png)
     585
     586### Manually
     587
     588Alaternatively, you can use the `amp_post_template_analytics` filter:
     589
     590```php
    498591add_filter( 'amp_post_template_analytics', 'xyz_amp_add_custom_analytics' );
    499592function xyz_amp_add_custom_analytics( $analytics ) {
     
    557650If you want a custom template for your post type:
    558651
    559 ```
     652```php
    560653add_filter( 'amp_post_template_file', 'xyz_amp_set_review_template', 10, 3 );
    561654
  • amp/trunk/readme.txt

    r1673399 r1708734  
    22Contributors: batmoo, joen, automattic, potatomaster
    33Tags: amp, mobile
    4 Requires at least: 4.4
     4Requires at least: 4.7
    55Tested up to: 4.8
    6 Stable tag: 0.4.2
     6Stable tag: 0.5
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    1414This 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.
    1515
    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`
     16With 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&amp=1`
    1717
    1818Note #1: that Pages and archives are not currently supported. Pages support is being worked on.
     
    3232= How do I customize the AMP output for my site? =
    3333
    34 You can tweak a few things like colours from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`.
     34You can tweak a few things like colors from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`.
    3535
    3636For deeper level customizations, please see the readme at https://github.com/Automattic/amp-wp/blob/master/readme.md
     
    5454== Changelog ==
    5555
     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
    5672= 0.4.2 (2016-10-13) =
    5773
     
    7389- New template: spiffy, shiny, and has the fresh theme smell (props allancole and the Automattic Theme Team).
    7490- *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 colours 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)
    7692- Fix: support for inline styles (props coreymckrill).
    7793- Fix: no more fatal errors when tags not supported by post type (props david-binda)
  • amp/trunk/templates/footer.php

    r1510272 r1708734  
    33        <h2><?php echo esc_html( $this->get( 'blog_name' ) ); ?></h2>
    44        <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>
    66        </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>
    88    </div>
    99</footer>
  • amp/trunk/templates/header-bar.php

    r1510272 r1708734  
    33        <a href="<?php echo esc_url( $this->get( 'home_url' ) ); ?>">
    44            <?php $site_icon_url = $this->get( 'site_icon_url' );
    5                 if ( $site_icon_url ) : ?>
     5            if ( $site_icon_url ) : ?>
    66                <amp-img src="<?php echo esc_url( $site_icon_url ); ?>" width="32" height="32" class="amp-wp-site-icon"></amp-img>
    77            <?php endif; ?>
  • amp/trunk/templates/meta-author.php

    r1510272 r1708734  
    11<?php $post_author = $this->get( 'post_author' ); ?>
    22<?php if ( $post_author ) : ?>
    3     <?php $author_avatar_url = get_avatar_url( $post_author->user_email, array( 'size' => 24 ) ); ?>
    43    <div class="amp-wp-meta amp-wp-byline">
    54        <?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>
    76        <?php endif; ?>
    87        <span class="amp-wp-author author vcard"><?php echo esc_html( $post_author->display_name ); ?></span>
  • amp/trunk/templates/style.php

    r1510272 r1708734  
    135135    /** site icon is 32px **/
    136136    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 ); ?>;
    138138    border-radius: 50%;
    139139    position: absolute;
Note: See TracChangeset for help on using the changeset viewer.