WordPress.org

Make WordPress Core

Changeset 23729


Ignore:
Timestamp:
03/16/13 05:25:44 (3 years ago)
Author:
markjaquith
Message:

Introduce [audio] and [video] shortcodes, and use MediaElement.js to play them.

props wonderboymusic. see #23282.

Location:
trunk
Files:
15 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/includes/ajax-actions.php

    r23699 r23729  
    20202020        $title = ''; // We no longer insert title tags into <img> tags, as they are redundant. 
    20212021        $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt ); 
     2022    } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 )  ) { 
     2023        $html = stripslashes_deep( $_POST['html'] ); 
    20222024    } 
    20232025 
  • trunk/wp-includes/functions.php

    r23637 r23729  
    40214021</div>' ) ); 
    40224022} 
     4023 
     4024/** 
     4025 * Return RegEx body to liberally match an opening HTML tag that: 
     4026 * 1. Is self-closing or 
     4027 * 2. Has no body but has a closing tag of the same name or 
     4028 * 3. Contains a body and a closing tag of the same name 
     4029 * 
     4030 * Note: this RegEx does not balance inner tags and does not attempt to produce valid HTML 
     4031 * 
     4032 * @since 3.6.0 
     4033 * 
     4034 * @param string $tag An HTML tag name. Example: 'video' 
     4035 * @return string 
     4036 */ 
     4037function get_tag_regex( $tag ) { 
     4038    if ( empty( $tag ) ) 
     4039        return; 
     4040 
     4041    return sprintf( '(<%1$s[^>]*(?:/?>$|>[\s\S]*?</%1$s>))', tag_escape( $tag ) ); 
     4042} 
  • trunk/wp-includes/js/media-editor.js

    r23298 r23729  
    6767                    captionId: 'attachment_' + attachment.id 
    6868                }); 
    69  
     69            } else if ( 'video' === attachment.type || 'audio' === attachment.type ) { 
     70                _.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) ); 
    7071            // Format properties for non-images. 
    7172            } else { 
     
    9495 
    9596            return wp.html.string( options ); 
     97        }, 
     98 
     99        audio: function( props, attachment ) { 
     100            var shortcode, html; 
     101 
     102            props = wp.media.string.props( props, attachment ); 
     103 
     104            shortcode = {}; 
     105 
     106            if ( props.mime ) { 
     107                switch ( props.mime ) { 
     108                case 'audio/mpeg': 
     109                    if ( props.linkUrl.indexOf( 'mp3' ) ) 
     110                        shortcode.mp3 = props.linkUrl; 
     111                    else if ( props.linkUrl.indexOf( 'm4a' ) ) 
     112                        shortcode.m4a = props.linkUrl; 
     113                    break; 
     114                case 'audio/mp3': 
     115                    shortcode.mp3 = props.linkUrl; 
     116                    break; 
     117                case 'audio/m4a': 
     118                    shortcode.m4a = props.linkUrl; 
     119                    break; 
     120                case 'audio/wav': 
     121                    shortcode.wav = props.linkUrl; 
     122                    break; 
     123                case 'audio/ogg': 
     124                    shortcode.ogg = props.linkUrl; 
     125                    break; 
     126                case 'audio/x-ms-wma': 
     127                case 'audio/wma': 
     128                    shortcode.wma = props.linkUrl; 
     129                    break; 
     130                } 
     131            } 
     132 
     133            html = wp.shortcode.string({ 
     134                tag:     'audio', 
     135                attrs:   shortcode 
     136            }); 
     137 
     138            return html; 
     139        }, 
     140 
     141        video: function( props, attachment ) { 
     142            var shortcode, html; 
     143 
     144            props = wp.media.string.props( props, attachment ); 
     145 
     146            shortcode = {}; 
     147 
     148            if ( props.mime ) { 
     149                switch ( props.mime ) { 
     150                case 'video/mp4': 
     151                    shortcode.mp4 = props.linkUrl; 
     152                    break; 
     153                case 'video/m4v': 
     154                    shortcode.m4v = props.linkUrl; 
     155                    break; 
     156                case 'video/webm': 
     157                    shortcode.webm = props.linkUrl; 
     158                    break; 
     159                case 'video/ogg': 
     160                    shortcode.ogv = props.linkUrl; 
     161                    break; 
     162                case 'video/x-ms-wmv': 
     163                case 'video/wmv': 
     164                case 'video/asf': 
     165                    shortcode.wmv = props.linkUrl; 
     166                    break; 
     167                case 'video/flv': 
     168                case 'video/x-flv': 
     169                    shortcode.flv = props.linkUrl; 
     170                    break; 
     171                } 
     172            } 
     173 
     174            html = wp.shortcode.string({ 
     175                tag:     'video', 
     176                attrs:   shortcode 
     177            }); 
     178 
     179            return html; 
    96180        }, 
    97181 
     
    576660                            options[ option ] = props[ prop ]; 
    577661                    }); 
    578  
     662                } else if ( 'video' === attachment.type ) { 
     663                    html = wp.media.string.video( props ); 
     664                } else if ( 'audio' === attachment.type ) { 
     665                    html = wp.media.string.audio( props ); 
    579666                } else { 
    580667                    html = wp.media.string.link( props ); 
  • trunk/wp-includes/media.php

    r23714 r23729  
    805805    return $output; 
    806806} 
     807 
     808/** 
     809 * Provide a No-JS Flash fallback as a last resort for audio / video 
     810 * 
     811 * @since 3.6.0 
     812 * 
     813 * @param string $url 
     814 * @return string Fallback HTML 
     815 */ 
     816function wp_mediaelement_fallback( $url ) { 
     817    return apply_filters( 'wp_mediaelement_fallback', sprintf( '<a href="%1$s">%1$s</a>', esc_url( $url ) ), $url ); 
     818} 
     819 
     820/** 
     821 * Return a filtered list of WP-supported audio formats 
     822 * 
     823 * @since 3.6.0 
     824 * @return array 
     825 */ 
     826function wp_get_audio_extensions() { 
     827    return apply_filters( 'wp_audio_extensions', array( 'mp3', 'ogg', 'wma', 'm4a', 'wav' ) ); 
     828} 
     829 
     830/** 
     831 * The Audio shortcode. 
     832 * 
     833 * This implements the functionality of the Audio Shortcode for displaying 
     834 * WordPress mp3s in a post. 
     835 * 
     836 * @since 3.6.0 
     837 * 
     838 * @param array $attr Attributes of the shortcode. 
     839 * @return string HTML content to display audio. 
     840 */ 
     841function wp_audio_shortcode( $attr ) { 
     842    $post_id = get_post() ? get_the_ID() : 0; 
     843 
     844    static $instances = 0; 
     845    $instances++; 
     846 
     847    $audio = null; 
     848 
     849    $default_types = wp_get_audio_extensions(); 
     850    $defaults_atts = array( 'src' => '' ); 
     851    foreach ( $default_types as $type  ) 
     852        $defaults_atts[$type] = ''; 
     853 
     854    $atts = shortcode_atts( $defaults_atts, $attr ); 
     855    extract( $atts ); 
     856 
     857    $primary = false; 
     858    if ( ! empty( $src ) ) { 
     859        $type = wp_check_filetype( $src ); 
     860        if ( ! in_array( $type['ext'], $default_types ) ) { 
     861            printf( '<a class="wp-post-format-link-audio" href="%1$s">%1$s</a>', $src ); 
     862            return; 
     863        } 
     864        $primary = true; 
     865        array_unshift( $default_types, 'src' ); 
     866    } else { 
     867        foreach ( $default_types as $ext ) { 
     868            if ( ! empty( $$ext ) ) { 
     869                $type = wp_check_filetype( $$ext ); 
     870                if ( $type['ext'] === $ext ) 
     871                    $primary = true; 
     872            } 
     873        } 
     874    } 
     875 
     876    if ( ! $primary ) { 
     877        $audios = get_post_audio( $post_id ); 
     878        if ( empty( $audios ) ) 
     879            return; 
     880 
     881        $audio = reset( $audios ); 
     882        $src = wp_get_attachment_url( $audio->ID ); 
     883        if ( empty( $src ) ) 
     884            return; 
     885 
     886        array_unshift( $default_types, 'src' ); 
     887    } 
     888 
     889    $library = apply_filters( 'wp_audio_shortcode_library', 'mediaelement' ); 
     890    if ( 'mediaelement' === $library ) { 
     891        wp_enqueue_style( 'wp-mediaelement' ); 
     892        wp_enqueue_script( 'wp-mediaelement' ); 
     893    } 
     894 
     895    $atts = array( 
     896        sprintf( 'class="%s"', apply_filters( 'wp_audio_shortcode_class', 'wp-audio-shortcode' ) ), 
     897        sprintf( 'id="audio-%d-%d"', $post_id, $instances ), 
     898    ); 
     899 
     900    $html = sprintf( '<audio %s controls="controls" preload="none">', join( ' ', $atts ) ); 
     901 
     902    $fileurl = ''; 
     903    $source = '<source type="%s" src="%s" />'; 
     904    foreach ( $default_types as $fallback ) { 
     905        if ( ! empty( $$fallback ) ) { 
     906            if ( empty( $fileurl ) ) 
     907                $fileurl = $$fallback; 
     908            $type = wp_check_filetype( $$fallback ); 
     909            $html .= sprintf( $source, $type['type'], $$fallback ); 
     910        } 
     911    } 
     912 
     913    if ( 'mediaelement' === $library ) 
     914        $html .= wp_mediaelement_fallback( $fileurl ); 
     915    $html .= '</audio>'; 
     916 
     917    return apply_filters( 'wp_audio_shortcode', $html, $atts, $audio, $post_id ); 
     918} 
     919add_shortcode( 'audio', apply_filters( 'wp_audio_shortcode_handler', 'wp_audio_shortcode' ) ); 
     920 
     921/** 
     922 * Return a filtered list of WP-supported video formats 
     923 * 
     924 * @since 3.6.0 
     925 * @return array 
     926 */ 
     927function wp_get_video_extensions() { 
     928    return apply_filters( 'wp_video_extensions', array( 'mp4', 'm4v', 'webm', 'ogv', 'wmv', 'flv' ) ); 
     929} 
     930 
     931/** 
     932 * The Video shortcode. 
     933 * 
     934 * This implements the functionality of the Video Shortcode for displaying 
     935 * WordPress mp4s in a post. 
     936 * 
     937 * @since 3.6.0 
     938 * 
     939 * @param array $attr Attributes of the shortcode. 
     940 * @return string HTML content to display video. 
     941 */ 
     942function wp_video_shortcode( $attr ) { 
     943    global $content_width; 
     944    $post_id = get_post() ? get_the_ID() : 0; 
     945 
     946    static $instances = 0; 
     947    $instances++; 
     948 
     949    $video = null; 
     950 
     951    $default_types = wp_get_video_extensions(); 
     952    $defaults_atts = array( 
     953        'src' => '', 
     954        'poster' => '', 
     955        'height' => 360, 
     956        'width' => empty( $content_width ) ? 640 : $content_width, 
     957    ); 
     958    foreach ( $default_types as $type  ) 
     959        $defaults_atts[$type] = ''; 
     960 
     961    $atts = shortcode_atts( $defaults_atts, $attr ); 
     962    extract( $atts ); 
     963 
     964    $primary = false; 
     965    if ( ! empty( $src ) ) { 
     966        $type = wp_check_filetype( $src ); 
     967        if ( ! in_array( $type['ext'], $default_types ) ) { 
     968            printf( '<a class="wp-post-format-link-video" href="%1$s">%1$s</a>', $src ); 
     969            return; 
     970        } 
     971        $primary = true; 
     972        array_unshift( $default_types, 'src' ); 
     973    } else { 
     974        foreach ( $default_types as $ext ) { 
     975            if ( ! empty( $$ext ) ) { 
     976                $type = wp_check_filetype( $$ext ); 
     977                if ( $type['ext'] === $ext ) 
     978                    $primary = true; 
     979            } 
     980        } 
     981    } 
     982 
     983    if ( ! $primary ) { 
     984        $videos = get_post_video( $post_id ); 
     985        if ( empty( $videos ) ) 
     986            return; 
     987 
     988        $video = reset( $videos ); 
     989        $src = wp_get_attachment_url( $video->ID ); 
     990        if ( empty( $src ) ) 
     991            return; 
     992 
     993        array_unshift( $default_types, 'src' ); 
     994    } 
     995 
     996    $library = apply_filters( 'wp_video_shortcode_library', 'mediaelement' ); 
     997    if ( 'mediaelement' === $library ) { 
     998        wp_enqueue_style( 'wp-mediaelement' ); 
     999        wp_enqueue_script( 'wp-mediaelement' ); 
     1000    } 
     1001 
     1002    $atts = array( 
     1003        sprintf( 'class="%s"', apply_filters( 'wp_video_shortcode_class', 'wp-video-shortcode' ) ), 
     1004        sprintf( 'id="video-%d-%d"', $post_id, $instances ), 
     1005        sprintf( 'width="%d"', $width ), 
     1006        sprintf( 'height="%d"', $height ), 
     1007    ); 
     1008 
     1009    if ( ! empty( $poster ) ) 
     1010        $atts[] = sprintf( 'poster="%s"', esc_url( $poster ) ); 
     1011 
     1012    $html = sprintf( '<video %s controls="controls" preload="none">', join( ' ', $atts ) ); 
     1013 
     1014    $fileurl = ''; 
     1015    $source = '<source type="%s" src="%s" />'; 
     1016    foreach ( $default_types as $fallback ) { 
     1017        if ( ! empty( $$fallback ) ) { 
     1018            if ( empty( $fileurl ) ) 
     1019                $fileurl = $$fallback; 
     1020            $type = wp_check_filetype( $$fallback ); 
     1021            // m4v sometimes shows up as video/mpeg which collides with mp4 
     1022            if ( 'm4v' === $type['ext'] ) 
     1023                $type['type'] = 'video/m4v'; 
     1024            $html .= sprintf( $source, $type['type'], $$fallback ); 
     1025        } 
     1026    } 
     1027    if ( 'mediaelement' === $library ) 
     1028        $html .= wp_mediaelement_fallback( $fileurl, $width, $height ); 
     1029    $html .= '</video>'; 
     1030 
     1031    return apply_filters( 'wp_video_shortcode', $html, $atts, $video, $post_id ); 
     1032} 
     1033add_shortcode( 'video', apply_filters( 'wp_video_shortcode_handler', 'wp_video_shortcode' ) ); 
    8071034 
    8081035/** 
     
    15461773    do_action( 'wp_enqueue_media' ); 
    15471774} 
     1775 
     1776/** 
     1777 * Retrieve audio attached to the passed post 
     1778 * 
     1779 * @since 3.6.0 
     1780 * 
     1781 * @param int $post_id  Post ID 
     1782 * @return array Found audio attachments 
     1783 */ 
     1784function get_post_audio( $post_id = 0 ) { 
     1785    $post = empty( $post_id ) ? get_post() : get_post( $post_id ); 
     1786    if ( empty( $post ) ) 
     1787        return; 
     1788 
     1789    $children = get_children( array( 
     1790        'post_parent' => $post->ID, 
     1791        'post_type' => 'attachment', 
     1792        'post_mime_type' => 'audio', 
     1793        'posts_per_page' => -1 
     1794    ) ); 
     1795 
     1796    if ( ! empty( $children ) ) 
     1797        return $children; 
     1798} 
     1799 
     1800/** 
     1801 * Retrieve video attached to the passed post 
     1802 * 
     1803 * @since 3.6.0 
     1804 * 
     1805 * @param int $post_id  Post ID 
     1806 * @return array Found video attachments 
     1807 */ 
     1808function get_post_video( $post_id = 0 ) { 
     1809    $post = empty( $post_id ) ? get_post() : get_post( $post_id ); 
     1810    if ( empty( $post ) ) 
     1811        return; 
     1812 
     1813    $children = get_children( array( 
     1814        'post_parent' => $post->ID, 
     1815        'post_type' => 'attachment', 
     1816        'post_mime_type' => 'video', 
     1817        'posts_per_page' => -1 
     1818    ) ); 
     1819 
     1820    if ( ! empty( $children ) ) 
     1821        return $children; 
     1822} 
     1823 
     1824/** 
     1825 * Audio embed handler callback. 
     1826 * 
     1827 * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}. 
     1828 * @param array $attr Embed attributes. 
     1829 * @param string $url The original URL that was matched by the regex. 
     1830 * @param array $rawattr The original unmodified attributes. 
     1831 * @return string The embed HTML. 
     1832 */ 
     1833function wp_audio_embed( $matches, $attr, $url, $rawattr ) { 
     1834    $audio = $url; 
     1835    if ( shortcode_exists( 'audio' ) ) 
     1836        $audio = do_shortcode( '[audio src="' . $url . '" /]' ); 
     1837    return apply_filters( 'wp_audio_embed', $audio, $attr, $url, $rawattr ); 
     1838} 
     1839wp_embed_register_handler( 'wp_audio_embed', '#https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')#i', apply_filters( 'wp_audio_embed_handler', 'wp_audio_embed' ), 9999 ); 
     1840 
     1841/** 
     1842 * Video embed handler callback. 
     1843 * 
     1844 * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}. 
     1845 * @param array $attr Embed attributes. 
     1846 * @param string $url The original URL that was matched by the regex. 
     1847 * @param array $rawattr The original unmodified attributes. 
     1848 * @return string The embed HTML. 
     1849 */ 
     1850function wp_video_embed( $matches, $attr, $url, $rawattr ) { 
     1851    $dimensions = ''; 
     1852    $video = $url; 
     1853    if ( shortcode_exists( 'video' ) ) { 
     1854        if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) { 
     1855            $dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] ); 
     1856            $dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] ); 
     1857        } 
     1858        $video = do_shortcode( '[video ' . $dimensions . 'src="' . $url . '" /]' ); 
     1859    } 
     1860    return apply_filters( 'wp_video_embed', $video, $attr, $url, $rawattr ); 
     1861} 
     1862wp_embed_register_handler( 'wp_video_embed', '#https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')#i', apply_filters( 'wp_video_embed_handler', 'wp_video_embed' ), 9999 ); 
  • trunk/wp-includes/post-formats.php

    r23655 r23729  
    309309    $format_output = ''; 
    310310    $meta = get_post_format_meta( $post->ID ); 
     311    // passed by ref in preg_match() 
     312    $matches = array(); 
    311313 
    312314    switch ( $format ) { 
     
    366368 
    367369        case 'gallery': 
    368             preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches ); 
    369             if ( ! empty( $matches ) && isset( $matches[2] ) ) { 
    370                 foreach ( (array) $matches[2] as $match ) { 
    371                     if ( 'gallery' === $match ) 
    372                         break 2; // foreach + case 
    373                 } 
    374             } 
    375  
    376             if ( ! empty( $meta['gallery'] ) ) { 
     370            if ( ! has_shortcode( $post->post_content, $format ) && ! empty( $meta['gallery'] ) ) 
    377371                $format_output .= $meta['gallery']; 
    378             } 
    379372            break; 
    380373 
    381374        case 'video': 
    382375        case 'audio': 
    383             $shortcode_regex = '/' . get_shortcode_regex() . '/s'; 
    384             $matches = preg_match( $shortcode_regex, $content ); 
    385             if ( ! $matches || $format !== $matches[2] ) { 
    386                 if ( ! empty( $meta['media'] ) ) { 
    387                     // the metadata is a shortcode or an embed code 
    388                     if ( preg_match( $shortcode_regex, $meta['media'] ) || preg_match( '#<[^>]+>#', $meta['media'] ) ) { 
    389                         $format_output .= $meta['media']; 
    390                     } elseif ( ! stristr( $content, $meta['media'] ) ) { 
    391                         // attempt to embed the URL 
    392                         $format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] ); 
    393                     } 
     376            if ( ! has_shortcode( $post->post_content, $format ) && ! empty( $meta['media'] ) ) { 
     377                // the metadata is a shortcode or an embed code 
     378                if ( preg_match( '/' . get_shortcode_regex() . '/s', $meta['media'] ) || preg_match( '#<[^>]+>#', $meta['media'] ) ) { 
     379                    $format_output .= $meta['media']; 
     380                } elseif ( ! stristr( $content, $meta['media'] ) ) { 
     381                    // attempt to embed the URL 
     382                    $format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] ); 
    394383                } 
    395384            } 
  • trunk/wp-includes/script-loader.php

    r23683 r23729  
    278278    $scripts->add( 'imgareaselect', "/wp-includes/js/imgareaselect/jquery.imgareaselect$suffix.js", array('jquery'), '0.9.8', 1 ); 
    279279 
     280    $scripts->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelement-and-player$suffix.js", array('jquery'), '2.10.1', 1 ); 
     281    $scripts->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.js", array('mediaelement'), false, 1 ); 
     282 
    280283    $scripts->add( 'password-strength-meter', "/wp-admin/js/password-strength-meter$suffix.js", array('jquery'), false, 1 ); 
    281284    did_action( 'init' ) && $scripts->localize( 'password-strength-meter', 'pwsL10n', array( 
     
    542545    $styles->add( 'buttons', "/wp-includes/css/buttons$suffix.css" ); 
    543546 
     547    $styles->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelementplayer$suffix.css" ); 
     548    $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.css", array( 'mediaelement' ) ); 
     549 
    544550    foreach ( $rtl_styles as $rtl_style ) { 
    545551        $styles->add_data( $rtl_style, 'rtl', true ); 
  • trunk/wp-includes/shortcodes.php

    r23626 r23729  
    126126 
    127127    $shortcode_tags = array(); 
     128} 
     129 
     130/** 
     131 * Whether a registered shortcode exists named $tag 
     132 * 
     133 * @since 3.6.0 
     134 * 
     135 * @global array $shortcode_tags 
     136 * @param string $tag 
     137 * @return boolean 
     138 */ 
     139function shortcode_exists( $tag ) { 
     140    global $shortcode_tags; 
     141    return array_key_exists( $tag, $shortcode_tags ); 
     142} 
     143 
     144/** 
     145 * Whether the passed content contains the specified shortcode 
     146 * 
     147 * @since 3.6.0 
     148 * 
     149 * @global array $shortcode_tags 
     150 * @param string $tag 
     151 * @return boolean 
     152 */ 
     153function has_shortcode( $content, $tag ) { 
     154    if ( shortcode_exists( $tag ) ) { 
     155        $matches = array(); 
     156        preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ); 
     157        if ( empty( $matches ) ) 
     158            return false; 
     159 
     160        foreach ( $matches as $shortcode ) { 
     161            if ( $tag === $shortcode[2] ) 
     162                return true; 
     163        } 
     164    } 
     165    return false; 
    128166} 
    129167 
Note: See TracChangeset for help on using the changeset viewer.