From 04699589533ddade97377437def834653a517c4d Mon Sep 17 00:00:00 2001 From: David Herrera Date: Sat, 23 Nov 2024 00:43:01 -0500 Subject: [PATCH] Allow videos in cover component --- .eslintignore | 2 +- admin/apple-actions/index/class-export.php | 156 +++++++++++++---- admin/class-admin-apple-meta-boxes.php | 26 +-- admin/class-admin-apple-news.php | 52 ++++-- admin/class-admin-apple-post-sync.php | 6 +- admin/partials/cover-image.php | 46 ----- admin/partials/cover-media.php | 142 +++++++++++++++ admin/partials/metabox-publish.php | 6 +- assets/js/cover-image.js | 60 ------- assets/js/cover-media.js | 155 +++++++++++++++++ .../js/pluginsidebar/panels/cover-image.jsx | 43 ----- .../js/pluginsidebar/panels/cover-media.jsx | 162 ++++++++++++++++++ assets/js/pluginsidebar/sidebar.jsx | 16 +- .../REST/apple-news-is-valid-cover-media.php | 60 +++++++ .../builders/class-components.php | 8 +- .../builders/class-metadata.php | 14 +- .../apple-exporter/components/class-cover.php | 51 ++++-- .../components/class-embed-web-video.php | 5 + includes/class-apple-news.php | 2 +- .../components/test-class-cover.php | 70 ++++++++ tests/data/test-video.mp4 | Bin 0 -> 468755 bytes 21 files changed, 846 insertions(+), 236 deletions(-) delete mode 100644 admin/partials/cover-image.php create mode 100644 admin/partials/cover-media.php delete mode 100644 assets/js/cover-image.js create mode 100644 assets/js/cover-media.js delete mode 100644 assets/js/pluginsidebar/panels/cover-image.jsx create mode 100644 assets/js/pluginsidebar/panels/cover-media.jsx create mode 100644 includes/REST/apple-news-is-valid-cover-media.php create mode 100644 tests/data/test-video.mp4 diff --git a/.eslintignore b/.eslintignore index 872f242f9..9b5e68df4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,5 @@ assets/js/bulk-export.js -assets/js/cover-image.js +assets/js/cover-media.js assets/js/export-table.js assets/js/json.js assets/js/meta-boxes.js diff --git a/admin/apple-actions/index/class-export.php b/admin/apple-actions/index/class-export.php index 8f490e7a6..ab5b88430 100644 --- a/admin/apple-actions/index/class-export.php +++ b/admin/apple-actions/index/class-export.php @@ -13,6 +13,7 @@ require_once dirname( __DIR__, 3 ) . '/includes/apple-exporter/autoload.php'; use Apple_Actions\Action; +use Apple_Exporter\Components\Embed_Web_Video; use Apple_Exporter\Exporter; use Apple_Exporter\Exporter_Content; use Apple_Exporter\Exporter_Content_Settings; @@ -121,34 +122,101 @@ public function fetch_exporter() { $excerpt = has_excerpt( $post ) ? wp_strip_all_tags( $post->post_excerpt ) : ''; // Get the cover configuration. - $post_thumb = null; - $cover_meta_id = get_post_meta( $this->id, 'apple_news_coverimage', true ); - $cover_caption = get_post_meta( $this->id, 'apple_news_coverimage_caption', true ); - if ( ! empty( $cover_meta_id ) ) { - if ( empty( $cover_caption ) ) { - $cover_caption = wp_get_attachment_caption( $cover_meta_id ); + $post_thumb = null; + $fall_back_to_image = false; + + $cover_provider = get_post_meta( $this->id, 'apple_news_cover_media_provider', true ); + if ( ! is_string( $cover_provider ) || ! $cover_provider ) { + $cover_provider = 'image'; + } + + if ( 'embedwebvideo' === $cover_provider ) { + $cover_url = get_post_meta( $this->id, 'apple_news_cover_embedwebvideo_url', true ); + + // Test against accepted providers. + if ( preg_match( Embed_Web_Video::YOUTUBE_MATCH, $cover_url, $cover_matches ) ) { + $post_thumb = [ + 'caption' => '', + 'url' => 'https://www.youtube.com/embed/' . end( $cover_matches ), + ]; + } elseif ( preg_match( Embed_Web_Video::VIMEO_MATCH, $cover_url, $cover_matches ) ) { + $post_thumb = [ + 'caption' => '', + 'url' => 'https://player.vimeo.com/video/' . end( $cover_matches ), + ]; + } elseif ( preg_match( Embed_Web_Video::DAILYMOTION_MATCH, $cover_url, $cover_matches ) ) { + $post_thumb = [ + 'caption' => '', + 'url' => 'https://geo.dailymotion.com/player.html?video=' . end( $cover_matches ), + ]; + } else { + $fall_back_to_image = true; } - $post_thumb = [ - 'caption' => ! empty( $cover_caption ) ? $cover_caption : '', - 'url' => wp_get_attachment_url( $cover_meta_id ), - ]; - } else { - $thumb_id = get_post_thumbnail_id( $this->id ); - $post_thumb_url = wp_get_attachment_url( $thumb_id ); - if ( empty( $cover_caption ) ) { - $cover_caption = wp_get_attachment_caption( $thumb_id ); + } + + if ( 'video_id' === $cover_provider ) { + $video_id = get_post_meta( $this->id, 'apple_news_cover_video_id', true ); + $video_url = (string) wp_get_attachment_url( $video_id ); + + if ( $video_url ) { + $post_thumb = [ + 'caption' => '', + 'url' => $video_url, + ]; + } else { + $fall_back_to_image = true; } - if ( ! empty( $post_thumb_url ) ) { - // If the post thumb URL is root-relative, convert it to fully-qualified. - if ( str_starts_with( $post_thumb_url, '/' ) ) { - $post_thumb_url = home_url( $post_thumb_url ); - } + } - // Compile the post_thumb object using the URL and caption from the featured image. + if ( 'video_url' === $cover_provider ) { + $file_url = get_post_meta( $this->id, 'apple_news_cover_video_url', true ); + $check = wp_check_filetype( $file_url ); + + if ( isset( $check['ext'] ) && 'mp4' === $check['ext'] ) { + $post_thumb = [ + 'caption' => '', + 'url' => $file_url, + ]; + } else { + $fall_back_to_image = true; + } + } + + // Provide fallback behavior so there's still a cover, e.g. if the URL becomes unsupported later. + if ( $fall_back_to_image ) { + $cover_url = ''; + $cover_provider = 'image'; + } + + if ( 'image' === $cover_provider ) { + $cover_meta_id = get_post_meta( $this->id, 'apple_news_coverimage', true ); + $cover_caption = get_post_meta( $this->id, 'apple_news_coverimage_caption', true ); + if ( ! empty( $cover_meta_id ) ) { + if ( empty( $cover_caption ) ) { + $cover_caption = wp_get_attachment_caption( $cover_meta_id ); + } $post_thumb = [ 'caption' => ! empty( $cover_caption ) ? $cover_caption : '', - 'url' => $post_thumb_url, + 'url' => wp_get_attachment_url( $cover_meta_id ), ]; + } else { + $thumb_id = get_post_thumbnail_id( $this->id ); + $post_thumb_url = wp_get_attachment_url( $thumb_id ); + if ( empty( $cover_caption ) ) { + $cover_caption = wp_get_attachment_caption( $thumb_id ); + } + if ( ! empty( $post_thumb_url ) ) { + // If the post thumb URL is root-relative, convert it to fully-qualified. + if ( str_starts_with( $post_thumb_url, '/' ) ) { + $post_thumb_url = home_url( $post_thumb_url ); + } + + // Compile the post_thumb object using the URL and caption from the featured image. + $post_thumb = [ + 'caption' => ! empty( $cover_caption ) ? $cover_caption : '', + 'url' => $post_thumb_url, + ]; + } } } @@ -160,6 +228,11 @@ public function fetch_exporter() { ]; } + // Attach the final provider slug. + if ( is_array( $post_thumb ) ) { + $post_thumb['provider'] = $cover_provider; + } + // Build the byline. $byline = $this->format_byline( $post ); @@ -208,15 +281,39 @@ public function fetch_exporter() { */ $excerpt = apply_filters( 'apple_news_exporter_excerpt', $excerpt, $post->ID ); + $cover_url = ! empty( $post_thumb['url'] ) ? $post_thumb['url'] : null; + + if ( isset( $post_thumb['provider'] ) && 'image' === $post_thumb['provider'] ) { + /** + * Filters the cover image URL of an article before it is sent to Apple News. + * + * The cover image URL is used for the Cover component, if it is active. + * + * @deprecated 2.7.0 Use the `apple_news_exporter_cover_url` filter, which includes all provider types. + * + * @param string|null $url The cover image URL for the post. + * @param int $post_id The ID of the post. + */ + $cover_url = apply_filters_deprecated( + 'apple_news_exporter_post_thumb', + [ $cover_url, $post->ID ], + '2.7.0', + 'apple_news_exporter_cover_url', + ); + } + /** - * Filters the cover image URL of an article before it is sent to Apple News. + * Filters the URL to the cover media of an article before it is sent to Apple News. * - * The cover image URL is used for the Cover component, if it is active. + * The URL may be an image URL, video file URL, or a URL to an embeddable external video, depending on the provider + * of cover media for the article. * - * @param string|null $url The cover image URL for the post. - * @param int $post_id The ID of the post. + * @param string|null $url The cover media URL for the post. + * @param string|null $provider The provider of the cover media. Possible values include 'image', 'video_url', + * 'video_id', and 'embedwebvideo'. + * @param int $post_id The ID of the post. */ - $cover_url = apply_filters( 'apple_news_exporter_post_thumb', ! empty( $post_thumb['url'] ) ? $post_thumb['url'] : null, $post->ID ); + $cover_url = apply_filters( 'apple_news_exporter_cover_url', $cover_url, $post_thumb['provider'] ?? null, $post->ID ); /** * Filters the byline of an article before it is sent to Apple News. @@ -296,8 +393,9 @@ public function fetch_exporter() { $cover_caption = apply_filters( 'apple_news_exporter_cover_caption', $cover_caption, $post->ID ); $post_thumb = [ - 'caption' => $cover_caption, - 'url' => $cover_url, + 'provider' => $cover_provider, + 'caption' => $cover_caption, + 'url' => $cover_url, ]; } else { $post_thumb = null; diff --git a/admin/class-admin-apple-meta-boxes.php b/admin/class-admin-apple-meta-boxes.php index 69c90c785..05ddc358f 100644 --- a/admin/class-admin-apple-meta-boxes.php +++ b/admin/class-admin-apple-meta-boxes.php @@ -173,17 +173,21 @@ public static function save_post_meta( $post_id ) { // Save straightforward fields. $fields = [ - 'apple_news_coverimage' => 'integer', - 'apple_news_coverimage_caption' => 'textarea', - 'apple_news_is_hidden' => 'string', - 'apple_news_is_paid' => 'string', - 'apple_news_is_preview' => 'string', - 'apple_news_is_sponsored' => 'string', - 'apple_news_pullquote' => 'string', - 'apple_news_pullquote_position' => 'string', - 'apple_news_slug' => 'string', - 'apple_news_suppress_video_url' => 'boolean', - 'apple_news_use_image_component' => 'boolean', + 'apple_news_coverimage' => 'integer', + 'apple_news_coverimage_caption' => 'textarea', + 'apple_news_cover_embedwebvideo_url' => 'string', + 'apple_news_cover_media_provider' => 'string', + 'apple_news_cover_video_id' => 'integer', + 'apple_news_cover_video_url' => 'string', + 'apple_news_is_hidden' => 'string', + 'apple_news_is_paid' => 'string', + 'apple_news_is_preview' => 'string', + 'apple_news_is_sponsored' => 'string', + 'apple_news_pullquote' => 'string', + 'apple_news_pullquote_position' => 'string', + 'apple_news_slug' => 'string', + 'apple_news_suppress_video_url' => 'boolean', + 'apple_news_use_image_component' => 'boolean', ]; foreach ( $fields as $meta_key => $type ) { switch ( $type ) { diff --git a/admin/class-admin-apple-news.php b/admin/class-admin-apple-news.php index 24cd3d419..ceb9d5846 100644 --- a/admin/class-admin-apple-news.php +++ b/admin/class-admin-apple-news.php @@ -25,6 +25,7 @@ require_once dirname( __DIR__ ) . '/includes/REST/apple-news-delete.php'; require_once dirname( __DIR__ ) . '/includes/REST/apple-news-get-published-state.php'; require_once dirname( __DIR__ ) . '/includes/REST/apple-news-get-settings.php'; +require_once dirname( __DIR__ ) . '/includes/REST/apple-news-is-valid-cover-media.php'; require_once dirname( __DIR__ ) . '/includes/REST/apple-news-modify-post.php'; require_once dirname( __DIR__ ) . '/includes/REST/apple-news-publish.php'; require_once dirname( __DIR__ ) . '/includes/REST/apple-news-sections.php'; @@ -99,44 +100,57 @@ public function __construct() { // Define custom postmeta fields to register. $postmeta = [ - 'apple_news_api_created_at' => [ + 'apple_news_api_created_at' => [ 'default' => '', ], - 'apple_news_api_id' => [ + 'apple_news_api_id' => [ 'default' => '', ], - 'apple_news_api_modified_at' => [ + 'apple_news_api_modified_at' => [ 'default' => '', ], - 'apple_news_api_revision' => [ + 'apple_news_api_revision' => [ 'default' => '', ], - 'apple_news_api_share_url' => [ + 'apple_news_api_share_url' => [ 'default' => '', ], - 'apple_news_coverimage' => [ + 'apple_news_cover_media_provider' => [ + 'default' => 'image', + ], + 'apple_news_coverimage' => [ + 'default' => 0, + 'type' => 'integer', + ], + 'apple_news_coverimage_caption' => [ + 'default' => '', + ], + 'apple_news_cover_video_id' => [ 'default' => 0, 'type' => 'integer', ], - 'apple_news_coverimage_caption' => [ + 'apple_news_cover_video_url' => [ + 'default' => '', + ], + 'apple_news_cover_embedwebvideo_url' => [ 'default' => '', ], - 'apple_news_is_hidden' => [ + 'apple_news_is_hidden' => [ 'default' => '', ], - 'apple_news_is_paid' => [ + 'apple_news_is_paid' => [ 'default' => '', ], - 'apple_news_is_preview' => [ + 'apple_news_is_preview' => [ 'default' => '', ], - 'apple_news_is_sponsored' => [ + 'apple_news_is_sponsored' => [ 'default' => '', ], - 'apple_news_maturity_rating' => [ + 'apple_news_maturity_rating' => [ 'default' => '', ], - 'apple_news_metadata' => [ + 'apple_news_metadata' => [ 'default' => '', 'sanitize_callback' => function ( $value ) { return ! empty( $value ) && is_string( $value ) ? json_decode( $value, true ) : $value; @@ -146,16 +160,16 @@ public function __construct() { ], 'type' => 'string', ], - 'apple_news_pullquote' => [ + 'apple_news_pullquote' => [ 'default' => '', ], - 'apple_news_pullquote_position' => [ + 'apple_news_pullquote_position' => [ 'default' => '', ], - 'apple_news_slug' => [ + 'apple_news_slug' => [ 'default' => '', ], - 'apple_news_sections' => [ + 'apple_news_sections' => [ 'default' => [], 'show_in_rest' => [ 'schema' => [ @@ -167,11 +181,11 @@ public function __construct() { ], 'type' => 'array', ], - 'apple_news_suppress_video_url' => [ + 'apple_news_suppress_video_url' => [ 'default' => false, 'type' => 'boolean', ], - 'apple_news_use_image_component' => [ + 'apple_news_use_image_component' => [ 'default' => false, 'type' => 'boolean', ], diff --git a/admin/class-admin-apple-post-sync.php b/admin/class-admin-apple-post-sync.php index 247c789f5..55ca802f7 100644 --- a/admin/class-admin-apple-post-sync.php +++ b/admin/class-admin-apple-post-sync.php @@ -149,8 +149,10 @@ public function do_publish( $id, $post ) { // Proceed based on the current settings for auto publish and update. $updated = get_post_meta( $id, 'apple_news_api_id', true ); - if ( ( $updated && 'yes' !== $this->settings->api_autosync_update ) - || ( ! $updated && 'yes' !== $this->settings->api_autosync ) ) { + if ( + ( $updated && 'yes' !== $this->settings->api_autosync_update ) + || ( ! $updated && 'yes' !== $this->settings->api_autosync ) + ) { return; } diff --git a/admin/partials/cover-image.php b/admin/partials/cover-image.php deleted file mode 100644 index 0dd222447..000000000 --- a/admin/partials/cover-image.php +++ /dev/null @@ -1,46 +0,0 @@ -ID, 'apple_news_coverimage', true ); -$apple_cover_image_caption = get_post_meta( $post->ID, 'apple_news_coverimage_caption', true ); - -?> -
-
- -
- - - -
-
- - -
diff --git a/admin/partials/cover-media.php b/admin/partials/cover-media.php new file mode 100644 index 000000000..3170628d4 --- /dev/null +++ b/admin/partials/cover-media.php @@ -0,0 +1,142 @@ +ID, 'apple_news_cover_media_provider', true ); +$apple_cover_image_id = get_post_meta( $post->ID, 'apple_news_coverimage', true ); +$apple_cover_image_caption = get_post_meta( $post->ID, 'apple_news_coverimage_caption', true ); +$apple_cover_video_id = get_post_meta( $post->ID, 'apple_news_cover_video_id', true ); + +?> + +

+ + + + + +
+
+ +
+ + + + +
+ + +
+
+ +
+
+ ', esc_url( wp_get_attachment_url( $apple_cover_video_id ) ) ); + $apple_add_video_hidden = 'hidden'; + $apple_remove_video_hidden = ''; + } else { + $apple_add_video_hidden = ''; + $apple_remove_video_hidden = 'hidden'; + } + ?> +
+ + + +
+ +
+ + +

+ +
+ +
+ + +

+ +
diff --git a/admin/partials/metabox-publish.php b/admin/partials/metabox-publish.php index 6cd9a7809..2aef9b0ee 100644 --- a/admin/partials/metabox-publish.php +++ b/admin/partials/metabox-publish.php @@ -133,9 +133,9 @@

-
-

- +
+

+
settings->get( 'api_autosync' ) diff --git a/assets/js/cover-image.js b/assets/js/cover-image.js deleted file mode 100644 index 5eb14d23f..000000000 --- a/assets/js/cover-image.js +++ /dev/null @@ -1,60 +0,0 @@ -(function ( $, window, undefined ) { - 'use strict'; - - // Set up add and remove image functionality. - $( '.apple-news-coverimage-image-container' ).each( function () { - var $this = $( this ), - $addImgButton = $this.find( '.apple-news-coverimage-add' ), - $delImgButton = $this.find( '.apple-news-coverimage-remove' ), - $imgContainer = $this.find( '.apple-news-coverimage-image' ), - $imgIdInput = $this.find( '.apple-news-coverimage-id' ), - frame; - - // Set up handler for remove image functionality. - $delImgButton.on( 'click', function() { - $imgContainer.empty(); - $addImgButton.removeClass( 'hidden' ); - $delImgButton.addClass( 'hidden' ); - $imgIdInput.val( '' ); - } ); - - // Set up handler for add image functionality. - $addImgButton.on( 'click', function () { - - // Open frame, if it already exists. - if ( frame ) { - frame.open(); - return; - } - - // Set configuration for media frame. - frame = wp.media( { multiple: false } ); - - // Set up handler for image selection. - frame.on( 'select', function () { - - // Get information about the attachment. - var attachment = frame.state().get( 'selection' ).first().toJSON(), - imgUrl = attachment.url; - - // Set image URL to medium size, if available. - if ( attachment.sizes.medium && attachment.sizes.medium.url ) { - imgUrl = attachment.sizes.medium.url; - } - - // Clear current values. - $imgContainer.empty(); - $imgIdInput.val( '' ); - - // Add the image and ID, swap visibility of add and remove buttons. - $imgContainer.append( '' ); // phpcs:ignore WordPressVIPMinimum.JS.StringConcat.Found, WordPressVIPMinimum.JS.HTMLExecutingFunctions.append - $imgIdInput.val( attachment.id ); - $addImgButton.addClass( 'hidden' ); - $delImgButton.removeClass( 'hidden' ); - } ); - - // Open the media frame. - frame.open(); - } ); - } ); -})( jQuery, window ); diff --git a/assets/js/cover-media.js b/assets/js/cover-media.js new file mode 100644 index 000000000..270382c82 --- /dev/null +++ b/assets/js/cover-media.js @@ -0,0 +1,155 @@ +(function ( $, window, undefined ) { + 'use strict'; + + // Set up show/hide functionality for cover media provider containers. + (function () { + var $provider = $( '[name="apple_news_cover_media_provider"]' ), + $containers = $( '.apple-news-cover-media-provider-container' ), + showSelectedContainer = function () { + $containers.hide(); + $containers.filter( '[data-provider="' + $provider.filter( ':checked' ).val() + '"]' ).show(); + }; + + showSelectedContainer(); + $provider.on( 'change', showSelectedContainer ); + })(); + + // Set up add and remove image functionality. + $( '.apple-news-coverimage-image-container' ).each( function () { + var $this = $( this ), + $addImgButton = $this.find( '.apple-news-coverimage-add' ), + $delImgButton = $this.find( '.apple-news-coverimage-remove' ), + $imgContainer = $this.find( '.apple-news-coverimage-image' ), + $imgIdInput = $this.find( '.apple-news-coverimage-id' ), + frame; + + // Set up handler for remove image functionality. + $delImgButton.on( 'click', function() { + $imgContainer.empty(); + $addImgButton.removeClass( 'hidden' ); + $delImgButton.addClass( 'hidden' ); + $imgIdInput.val( '' ); + } ); + + // Set up handler for add image functionality. + $addImgButton.on( 'click', function () { + + // Open frame, if it already exists. + if ( frame ) { + frame.open(); + return; + } + + // Set configuration for media frame. + frame = wp.media( { multiple: false } ); + + // Set up handler for image selection. + frame.on( 'select', function () { + + // Get information about the attachment. + var attachment = frame.state().get( 'selection' ).first().toJSON(), + imgUrl = attachment.url; + + // Set image URL to medium size, if available. + if ( attachment.sizes.medium && attachment.sizes.medium.url ) { + imgUrl = attachment.sizes.medium.url; + } + + // Clear current values. + $imgContainer.empty(); + $imgIdInput.val( '' ); + + // Add the image and ID, swap visibility of add and remove buttons. + $imgContainer.append( '' ); // phpcs:ignore WordPressVIPMinimum.JS.StringConcat.Found, WordPressVIPMinimum.JS.HTMLExecutingFunctions.append + $imgIdInput.val( attachment.id ); + $addImgButton.addClass( 'hidden' ); + $delImgButton.removeClass( 'hidden' ); + } ); + + // Open the media frame. + frame.open(); + } ); + } ); + + // Set up add and remove video functionality. + $( '[data-provider="video_id"]' ).each( function () { + var $this = $( this ), + $addVideoButton = $this.find( '.apple-news-cover-video-id-add' ), + $delVideoButton = $this.find( '.apple-news-cover-video-id-remove' ), + $previewContainer = $this.find( '.apple-news-cover-media-video' ), + $videoIdInput = $this.find( '[name="apple_news_cover_video_id"]' ), + frame; + + // Set up handler for remove functionality. + $delVideoButton.on( 'click', function() { + $previewContainer.empty(); + $addVideoButton.removeClass( 'hidden' ); + $delVideoButton.addClass( 'hidden' ); + $videoIdInput.val( '' ); + + console.log('remove video'); + } ); + + // Set up handler for add functionality. + $addVideoButton.on( 'click', function () { + // Open frame, if it already exists. + if ( frame ) { + frame.open(); + return; + } + + // Set configuration for media frame. + frame = wp.media( { + multiple: false, + library: { + type: 'video/mp4' + }, + } ); + + // Set up handler for image selection. + frame.on( 'select', function () { + // Get information about the attachment. + var attachment = frame.state().get( 'selection' ).first().toJSON(), + videoUrl = attachment.url; + + // Clear current values. + $previewContainer.empty(); + $videoIdInput.val( '' ); + + // Add the preview and ID, swap visibility of add and remove buttons. + $previewContainer.append( $( '