Make WordPress Core

Changeset 23729


Ignore:
Timestamp:
03/16/2013 05:25:44 AM (12 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.