Skip to content

Commit d8040f5

Browse files
fusharmatticbot
authored andcommitted
Media Library: Implement upload media from URL (#41089)
Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/13170764545 Upstream-Ref: Automattic/jetpack@d6f2cb4
1 parent 00fe491 commit d8040f5

File tree

12 files changed

+292
-63
lines changed

12 files changed

+292
-63
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<?php return array('dependencies' => array('react', 'react-dom', 'wp-i18n', 'wp-polyfill'), 'version' => '10268ed3f972a83340f1');

jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/wpcom-media-url-upload/wpcom-media-url-upload.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/wpcom-media-url-upload/wpcom-media-url-upload.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/wpcom-media-url-upload/wpcom-media-url-upload.rtl.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jetpack_vendor/automattic/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public static function load_wpcom_user_features() {
152152
require_once __DIR__ . '/features/wpcom-command-palette/wpcom-command-palette.php';
153153
require_once __DIR__ . '/features/wpcom-dashboard-widgets/wpcom-dashboard-widgets.php';
154154
require_once __DIR__ . '/features/wpcom-locale/sync-locale-from-calypso-to-atomic.php';
155+
require_once __DIR__ . '/features/wpcom-media/wpcom-media-url-upload.php';
155156
require_once __DIR__ . '/features/wpcom-options-general/options-general.php';
156157
require_once __DIR__ . '/features/wpcom-plugins/wpcom-plugins.php';
157158
require_once __DIR__ . '/features/wpcom-profile-settings/profile-settings-link-to-wpcom.php';
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { __ } from '@wordpress/i18n';
2+
import clsx from 'clsx';
3+
import { useState } from 'react';
4+
5+
import './style.scss';
6+
7+
const WpcomMediaUrlUploadForm = ( { ajaxUrl, action, nonce, isEditor } ) => {
8+
const [ url, setUrl ] = useState( '' );
9+
10+
const [ show, setShow ] = useState( false );
11+
const [ isUploading, setIsUploading ] = useState( false );
12+
13+
const handleUrlChange = e => {
14+
setUrl( e.target.value );
15+
};
16+
17+
const handleSubmit = async e => {
18+
if ( isUploading ) {
19+
return false;
20+
}
21+
try {
22+
new URL( url ); // eslint-disable-line no-new
23+
} catch {
24+
return false;
25+
}
26+
e.preventDefault();
27+
28+
const formData = new FormData();
29+
formData.append( 'action', action );
30+
formData.append( 'url', url );
31+
formData.append( '_ajax_nonce', nonce );
32+
33+
setIsUploading( true );
34+
35+
const response = await fetch( ajaxUrl, {
36+
method: 'POST',
37+
body: formData,
38+
} );
39+
40+
const { success, data } = await response.json();
41+
42+
if ( success ) {
43+
window.wp.media.model.Attachment.get( data.attachment_id ).fetch( {
44+
success: function ( attachment ) {
45+
const addAttachment = attachmentToAdd => {
46+
( window.wp.media.frame.controller || window.wp.media.frame ).content
47+
.get()
48+
.collection.add( attachmentToAdd );
49+
};
50+
51+
if ( isEditor ) {
52+
const mediaLibraryTab = window.wp.media.frame.state( 'library' );
53+
mediaLibraryTab.trigger( 'open' );
54+
55+
addAttachment( attachment );
56+
57+
const selection = mediaLibraryTab.get( 'selection' );
58+
selection.reset();
59+
selection.add( [ attachment ] );
60+
} else {
61+
addAttachment( attachment );
62+
}
63+
64+
setIsUploading( false );
65+
setUrl( '' );
66+
},
67+
} );
68+
} else {
69+
setIsUploading( false );
70+
window.wp.Uploader.errors.add( { file: { name: url }, message: data[ 0 ].message } );
71+
}
72+
73+
return false;
74+
};
75+
76+
const renderLink = () => {
77+
return (
78+
<button
79+
className="button wpcom-media-url-upload-form__pre-upload-button"
80+
onClick={ () => setShow( true ) }
81+
>
82+
{ __( 'Upload from URL', 'jetpack-mu-wpcom' ) }
83+
</button>
84+
);
85+
};
86+
87+
const renderForm = () => {
88+
let buttonText = __( 'Upload', 'jetpack-mu-wpcom' );
89+
if ( isUploading ) {
90+
buttonText = __( 'Uploading…', 'jetpack-mu-wpcom' );
91+
}
92+
return (
93+
<form onSubmit={ handleSubmit }>
94+
<input
95+
type="url"
96+
value={ url }
97+
onChange={ handleUrlChange }
98+
placeholder={ __( 'Enter media URL', 'jetpack-mu-wpcom' ) }
99+
required
100+
readOnly={ isUploading }
101+
/>
102+
<button
103+
type="submit"
104+
className={ clsx( 'button', 'button-primary', {
105+
'updating-message': isUploading,
106+
} ) }
107+
readOnly={ isUploading }
108+
>
109+
{ buttonText }
110+
</button>
111+
</form>
112+
);
113+
};
114+
115+
return <div className="wpcom-media-url-upload-form">{ show ? renderForm() : renderLink() }</div>;
116+
};
117+
118+
export default WpcomMediaUrlUploadForm;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom/client';
3+
import WpcomMediaUrlUploadForm from './wpcom-media-url-upload-form';
4+
5+
const props = typeof window === 'object' ? window.JETPACK_MU_WPCOM_MEDIA_URL_UPLOAD : {};
6+
7+
document.addEventListener( 'DOMContentLoaded', function () {
8+
if ( window.wp?.media?.view?.UploaderInline ) {
9+
const originalUploaderInline = window.wp.media.view.UploaderInline;
10+
11+
window.wp.media.view.UploaderInline = originalUploaderInline.extend( {
12+
ready: function () {
13+
originalUploaderInline.prototype.ready.apply( this, arguments );
14+
15+
const container = document.getElementById( 'wpcom-media-url-upload' );
16+
if ( container ) {
17+
const root = ReactDOM.createRoot( container );
18+
root.render( <WpcomMediaUrlUploadForm { ...props } /> );
19+
}
20+
},
21+
} );
22+
}
23+
} );
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Allows uploading media from URL in Media Library.
4+
*
5+
* @package automattic/jetpack-mu-wpcom
6+
*/
7+
8+
/**
9+
* Appends the wpcom media URL upload form.
10+
*/
11+
function wpcom_media_url_upload() {
12+
global $pagenow;
13+
14+
if ( empty( $_GET['untangling-media'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended
15+
return;
16+
}
17+
18+
?>
19+
<div id="wpcom-media-url-upload"></div>
20+
<?php
21+
22+
$handle = jetpack_mu_wpcom_enqueue_assets( 'wpcom-media-url-upload', array( 'js', 'css' ) );
23+
24+
$data = wp_json_encode(
25+
array(
26+
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
27+
'action' => 'wpcom_media_url_upload',
28+
'nonce' => wp_create_nonce( 'wpcom_media_url_upload' ),
29+
'isEditor' => $pagenow !== 'upload.php',
30+
)
31+
);
32+
33+
wp_add_inline_script(
34+
$handle,
35+
"window.JETPACK_MU_WPCOM_MEDIA_URL_UPLOAD = $data;",
36+
'before'
37+
);
38+
}
39+
40+
/**
41+
* AJAX handler for the wpcom media URL upload.
42+
*/
43+
function wpcom_handle_media_url_upload() {
44+
check_ajax_referer( 'wpcom_media_url_upload' );
45+
46+
if ( ! isset( $_POST['url'] ) ) {
47+
return;
48+
}
49+
50+
$url = esc_url_raw( wp_unslash( $_POST['url'] ) );
51+
52+
$tmp_file = download_url( $url );
53+
if ( is_wp_error( $tmp_file ) ) {
54+
return wp_send_json_error( $tmp_file );
55+
}
56+
57+
if ( is_multisite() ) {
58+
add_filter( 'wp_handle_sideload_prefilter', 'check_upload_size' );
59+
}
60+
61+
$attachment_id = media_handle_sideload(
62+
array(
63+
'name' => basename( wp_parse_url( $url, PHP_URL_PATH ) ),
64+
'tmp_name' => $tmp_file,
65+
)
66+
);
67+
68+
if ( file_exists( $tmp_file ) ) {
69+
wp_delete_file( $tmp_file );
70+
}
71+
72+
if ( is_wp_error( $attachment_id ) ) {
73+
return wp_send_json_error( $attachment_id );
74+
} else {
75+
return wp_send_json_success( array( 'attachment_id' => $attachment_id ) );
76+
}
77+
}
78+
79+
if ( current_user_can( 'upload_files' ) ) {
80+
add_action( 'pre-upload-ui', 'wpcom_media_url_upload', 9 );
81+
add_action( 'wp_ajax_wpcom_media_url_upload', 'wpcom_handle_media_url_upload' );
82+
}

jetpack_vendor/automattic/jetpack-mu-wpcom/webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ module.exports = [
4545
'./src/features/wpcom-global-styles/wpcom-global-styles-view.js',
4646
'wpcom-documentation-links':
4747
'./src/features/wpcom-documentation-links/wpcom-documentation-links.ts',
48+
'wpcom-media-url-upload': './src/features/wpcom-media/wpcom-media-url-upload.js',
4849
'wpcom-options-general': [
4950
'./src/features/wpcom-options-general/options-general.js',
5051
'./src/features/wpcom-options-general/options-general.scss',

jetpack_vendor/i18n-map.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
),
5555
'jetpack-mu-wpcom' => array(
5656
'path' => 'jetpack_vendor/automattic/jetpack-mu-wpcom',
57-
'ver' => '6.2.0-alpha1738744373',
57+
'ver' => '6.2.0-alpha1738809817',
5858
),
5959
'jetpack-password-checker' => array(
6060
'path' => 'jetpack_vendor/automattic/jetpack-password-checker',

0 commit comments

Comments
 (0)