Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Video block: only intercept video file uploads when VideoPress is available. On Jetpack sites without VideoPress active or without plan/free video, let core/video handle the upload.
33 changes: 33 additions & 0 deletions projects/packages/videopress/src/class-block-editor-extensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
use Automattic\Jetpack\Constants;
use Automattic\Jetpack\Current_Plan;
use Automattic\Jetpack\Status\Host;

/**
Expand Down Expand Up @@ -161,6 +162,7 @@ public static function enqueue_extensions() {
'isStandaloneActive' => Status::is_standalone_plugin_active(),
'imagesURLBase' => plugin_dir_url( __DIR__ ) . 'build/images/',
'playerBridgeUrl' => plugins_url( '../build/lib/player-bridge.js', __FILE__ ),
'canUploadToVideoPress' => self::can_upload_to_videopress( $site_type ),
);

// Expose initial state of site connection
Expand All @@ -169,4 +171,35 @@ public static function enqueue_extensions() {
// Expose initial state of videoPress editor
wp_localize_script( self::$script_handle, 'videoPressEditorState', $videopress_editor_state );
}

/**
* Check if the user can upload videos to VideoPress.
*
* On Dotcom sites, VideoPress always handles video uploads to show appropriate
* errors/upsell messages. On Jetpack sites, we check if VideoPress is active
* and if the user has plan support or hasn't used their free video yet.
*
* @param string $site_type The site type ('simple', 'atomic', or 'jetpack').
* @return bool Whether VideoPress should handle video uploads.
*/
private static function can_upload_to_videopress( $site_type ) {
// On Dotcom sites (simple/atomic), VideoPress always handles video uploads.
if ( 'jetpack' !== $site_type ) {
return true;
}

// On Jetpack sites, check if VideoPress is active (module OR standalone plugin).
if ( ! Status::is_active() ) {
return false;
}

// Check if user has a plan that supports VideoPress uploads (1TB storage feature).
if ( Current_Plan::supports( 'videopress-1tb-storage' ) ) {
return true;
}

// Check if user hasn't used their free video yet.
$video_data = Data::get_video_data();
return 0 === (int) $video_data['total'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,20 @@ const transformFromCoreEmbed = {

const transformFromFile = {
type: 'files',
// Check if the files array contains a video file.
// Check if the files array contains a video file and VideoPress can handle uploads.
isMatch: files => {
if ( ! files || ! files.length ) {
return false;
}

// Check if VideoPress should handle video uploads.
// On Jetpack sites without VideoPress active or without plan/free video,
// let core/video handle the upload instead.
const canUpload = window?.videoPressEditorState?.canUploadToVideoPress === '1';
if ( ! canUpload ) {
return false;
}

return files.some( isVideoFile );
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ describe( 'transforms', () => {
beforeEach( () => {
jest.clearAllMocks();
( findTransform as jest.Mock ).mockReturnValue( null );

// Mock window.videoPressEditorState to enable VideoPress uploads
Object.defineProperty( window, 'videoPressEditorState', {
value: { canUploadToVideoPress: '1' },
writable: true,
configurable: true,
} );
} );

afterEach( () => {
// Clean up window mock
delete ( window as Window & { videoPressEditorState?: unknown } ).videoPressEditorState;
} );

it( 'should return false when no files are provided', () => {
Expand Down Expand Up @@ -202,5 +214,37 @@ describe( 'transforms', () => {
// Ensure the unmatched file didn't cause issues
expect( result ).toHaveLength( 2 );
} );

describe( 'canUploadToVideoPress check', () => {
it( 'should return false when videoPressEditorState is undefined', () => {
// Clean up the mock set in beforeEach
delete ( window as Window & { videoPressEditorState?: unknown } ).videoPressEditorState;

const mockFile = new File( [ '' ], 'test.mp4', { type: 'video/mp4' } );
expect( transformFromFile.isMatch( [ mockFile ] ) ).toBe( false );
} );

it( 'should return false when canUploadToVideoPress is empty string', () => {
Object.defineProperty( window, 'videoPressEditorState', {
value: { canUploadToVideoPress: '' },
writable: true,
configurable: true,
} );

const mockFile = new File( [ '' ], 'test.mp4', { type: 'video/mp4' } );
expect( transformFromFile.isMatch( [ mockFile ] ) ).toBe( false );
} );

it( 'should return false when canUploadToVideoPress is missing from state', () => {
Object.defineProperty( window, 'videoPressEditorState', {
value: {},
writable: true,
configurable: true,
} );

const mockFile = new File( [ '' ], 'test.mp4', { type: 'video/mp4' } );
expect( transformFromFile.isMatch( [ mockFile ] ) ).toBe( false );
} );
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ declare global {
jetpackVideoPressSettingUrl: string;
imagesURLBase: string;
playerBridgeUrl: string;
canUploadToVideoPress: '' | '1';
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/**
* Tests for Automattic\Jetpack\VideoPress\Block_Editor_Extensions
*
* @package automattic/jetpack-videopress
*/

namespace Automattic\Jetpack\VideoPress;

use WorDBless\BaseTestCase;

/**
* Class Block_Editor_Extensions_Test
*
* Note: Tests for Jetpack site behavior are limited because the can_upload_to_videopress
* method relies on Status::is_active(), Current_Plan::supports(), and Data::get_video_data()
* which are tested separately and difficult to mock due to class autoloading.
*/
class Block_Editor_Extensions_Test extends BaseTestCase {

/**
* Test can_upload_to_videopress returns true for Dotcom simple sites.
*
* On Dotcom sites, VideoPress should always handle video uploads
* to show appropriate errors/upsell messages.
*/
public function test_can_upload_to_videopress_returns_true_for_simple_sites() {
$result = $this->invoke_can_upload_to_videopress( 'simple' );
$this->assertTrue( $result );
}

/**
* Test can_upload_to_videopress returns true for Dotcom atomic sites.
*
* On Dotcom sites, VideoPress should always handle video uploads
* to show appropriate errors/upsell messages.
*/
public function test_can_upload_to_videopress_returns_true_for_atomic_sites() {
$result = $this->invoke_can_upload_to_videopress( 'atomic' );
$this->assertTrue( $result );
}

/**
* Test can_upload_to_videopress returns false for Jetpack sites by default.
*
* Without VideoPress being active, Jetpack sites should fall back to core/video.
* This test verifies the default behavior when VideoPress is not active.
*/
public function test_can_upload_to_videopress_returns_false_for_jetpack_sites_by_default() {
// Without any plugins active, Status::is_active() returns false
// so can_upload_to_videopress should return false for jetpack sites.
$result = $this->invoke_can_upload_to_videopress( 'jetpack' );
$this->assertFalse( $result );
}

/**
* Helper method to invoke the private can_upload_to_videopress method.
*
* @param string $site_type The site type to test with.
* @return bool The result of can_upload_to_videopress.
*/
private function invoke_can_upload_to_videopress( $site_type ) {
$reflection = new \ReflectionClass( Block_Editor_Extensions::class );
$method = $reflection->getMethod( 'can_upload_to_videopress' );
if ( PHP_VERSION_ID < 80100 ) {
$method->setAccessible( true );
}
return $method->invoke( null, $site_type );
}
}
Loading