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 );
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/partials/metabox-publish.php b/admin/partials/metabox-publish.php
index 6cd9a7809..0f5df950e 100644
--- a/admin/partials/metabox-publish.php
+++ b/admin/partials/metabox-publish.php
@@ -134,8 +134,8 @@
-
-
+
+
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..50ad4be65
--- /dev/null
+++ b/assets/js/cover-media.js
@@ -0,0 +1,154 @@
+(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( '' );
+ } );
+
+ // 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( $( '