Add support for appending a unique string to the filename for the wp/v2/media endpoint (#42702)
* Extract 'downloadable product' class and include it for API endpoint calls as well * Re-add mediauploader component * Restore UploadFilesMenuItem * Provide additionalData type * Restore MediaUploader component * Lint PHP * Add changelogs * Update pnpm-lock * Revert "Update pnpm-lock" This reverts commit b61ee5813aa0b7b8b1ea8e71423bedbb6f876139. * Revert pnpm-lock.yaml * Fix unit tests * Use WC_ABSPATH
This commit is contained in:
parent
bbe2a6f2d7
commit
09bcb3fcac
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add additionalData prop to MediaUploader component
|
|
@ -39,6 +39,7 @@ type MediaUploaderProps = {
|
|||
onUpload?: ( files: MediaItem | MediaItem[] ) => void;
|
||||
onFileUploadChange?: ( files: MediaItem | MediaItem[] ) => void;
|
||||
uploadMedia?: ( options: UploadMediaOptions ) => Promise< void >;
|
||||
additionalData?: Record< string, unknown >;
|
||||
};
|
||||
|
||||
export const MediaUploader = ( {
|
||||
|
@ -56,6 +57,7 @@ export const MediaUploader = ( {
|
|||
onUpload = () => null,
|
||||
onSelect = () => null,
|
||||
uploadMedia = wpUploadMedia,
|
||||
additionalData,
|
||||
}: MediaUploaderProps ) => {
|
||||
const multiple = Boolean( multipleSelect );
|
||||
|
||||
|
@ -72,6 +74,7 @@ export const MediaUploader = ( {
|
|||
onFileChange( files ) {
|
||||
onFileUploadChange( multiple ? files : files[ 0 ] );
|
||||
},
|
||||
additionalData,
|
||||
} );
|
||||
} }
|
||||
render={ ( { openFileDialog } ) => (
|
||||
|
@ -133,6 +136,7 @@ export const MediaUploader = ( {
|
|||
multiple ? files : files[ 0 ]
|
||||
);
|
||||
},
|
||||
additionalData,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Allow uploading downloadable products by drag & drop and without the Media Library component
|
|
@ -12,9 +12,11 @@ import { chevronDown, chevronUp } from '@wordpress/icons';
|
|||
import { DownloadsMenuProps } from './types';
|
||||
import { MediaLibraryMenuItem } from '../media-library-menu-item';
|
||||
import { InsertUrlMenuItem } from '../insert-url-menu-item';
|
||||
import { UploadFilesMenuItem } from '../upload-files-menu-item';
|
||||
|
||||
export function DownloadsMenu( {
|
||||
allowedTypes,
|
||||
maxUploadFileSize,
|
||||
onUploadSuccess,
|
||||
onUploadError,
|
||||
}: DownloadsMenuProps ) {
|
||||
|
@ -39,6 +41,15 @@ export function DownloadsMenu( {
|
|||
renderContent={ ( { onClose } ) => (
|
||||
<div className="components-dropdown-menu__menu">
|
||||
<MenuGroup>
|
||||
<UploadFilesMenuItem
|
||||
allowedTypes={ allowedTypes }
|
||||
maxUploadFileSize={ maxUploadFileSize }
|
||||
onUploadSuccess={ ( files ) => {
|
||||
onUploadSuccess( files );
|
||||
onClose();
|
||||
} }
|
||||
onUploadError={ onUploadError }
|
||||
/>
|
||||
<MediaLibraryMenuItem
|
||||
allowedTypes={ allowedTypes }
|
||||
onUploadSuccess={ ( files ) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { closeSmall } from '@wordpress/icons';
|
||||
import { MediaItem } from '@wordpress/media-utils';
|
||||
import { useWooBlockProps } from '@woocommerce/block-templates';
|
||||
import { ListItem, Sortable } from '@woocommerce/components';
|
||||
import { ListItem, MediaUploader, Sortable } from '@woocommerce/components';
|
||||
import { Product, ProductDownload } from '@woocommerce/data';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this exist yet.
|
||||
|
@ -31,6 +31,7 @@ import {
|
|||
ManageDownloadLimitsModalProps,
|
||||
} from '../../../components/manage-download-limits-modal';
|
||||
import { EditDownloadsModal } from './edit-downloads-modal';
|
||||
import { UploadImage } from './upload-image';
|
||||
|
||||
function getFileName( url?: string ) {
|
||||
const [ name ] = url?.split( '/' ).reverse() ?? [];
|
||||
|
@ -249,36 +250,53 @@ export function Edit( {
|
|||
</div>
|
||||
|
||||
<div className="wp-block-woocommerce-product-downloads-field__body">
|
||||
{ ! Boolean( downloads.length ) && (
|
||||
<div className="wp-block-woocommerce-product-downloads-field__drop-zone-content">
|
||||
<p className="wp-block-woocommerce-product-downloads-field__drop-zone-label">
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'Supported file types: <Types /> and more. <link>View all</link>',
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
Types: (
|
||||
<Fragment>
|
||||
PNG, JPG, PDF, PPT, DOC, MP3, MP4
|
||||
</Fragment>
|
||||
),
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a
|
||||
href="https://codex.wordpress.org/Uploading_Files"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={ ( event ) =>
|
||||
event.stopPropagation()
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
</div>
|
||||
) }
|
||||
<MediaUploader
|
||||
label={
|
||||
! Boolean( downloads.length ) ? (
|
||||
<div className="wp-block-woocommerce-product-downloads-field__drop-zone-content">
|
||||
<UploadImage />
|
||||
<p className="wp-block-woocommerce-product-downloads-field__drop-zone-label">
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'Supported file types: <Types /> and more. <link>View all</link>',
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
Types: (
|
||||
<Fragment>
|
||||
PNG, JPG, PDF, PPT, DOC,
|
||||
MP3, MP4
|
||||
</Fragment>
|
||||
),
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a
|
||||
href="https://codex.wordpress.org/Uploading_Files"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={ ( event ) =>
|
||||
event.stopPropagation()
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
buttonText=""
|
||||
allowedMediaTypes={ allowedTypes }
|
||||
multipleSelect={ 'add' }
|
||||
onUpload={ handleFileUpload }
|
||||
onFileUploadChange={ handleFileUpload }
|
||||
onError={ handleUploadError }
|
||||
additionalData={ {
|
||||
type: 'downloadable_product',
|
||||
} }
|
||||
/>
|
||||
|
||||
{ Boolean( downloads.length ) && (
|
||||
<Sortable className="wp-block-woocommerce-product-downloads-field__table">
|
||||
|
|
|
@ -30,6 +30,9 @@ export function UploadFilesMenuItem( {
|
|||
maxUploadFileSize,
|
||||
onFileChange: onUploadSuccess,
|
||||
onError: onUploadError,
|
||||
additionalData: {
|
||||
type: 'downloadable_product',
|
||||
},
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Use downloadable products configuration for uploads through wp/v2/media endpoint
|
|
@ -54,10 +54,7 @@ class WC_Admin_Post_Types {
|
|||
add_filter( 'default_hidden_meta_boxes', array( $this, 'hidden_meta_boxes' ), 10, 2 );
|
||||
add_action( 'post_submitbox_misc_actions', array( $this, 'product_data_visibility' ) );
|
||||
|
||||
// Uploads.
|
||||
add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
|
||||
add_filter( 'wp_unique_filename', array( $this, 'update_filename' ), 10, 3 );
|
||||
add_action( 'media_upload_downloadable_product', array( $this, 'media_upload_downloadable_product' ) );
|
||||
include_once __DIR__ . '/class-wc-admin-upload-downloadable-product.php';
|
||||
|
||||
// Hide template for CPT archive.
|
||||
add_filter( 'theme_page_templates', array( $this, 'hide_cpt_archive_templates' ), 10, 3 );
|
||||
|
@ -742,100 +739,6 @@ class WC_Admin_Post_Types {
|
|||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Change upload dir for downloadable files.
|
||||
*
|
||||
* @param array $pathdata Array of paths.
|
||||
* @return array
|
||||
*/
|
||||
public function upload_dir( $pathdata ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
if ( isset( $_POST['type'] ) && 'downloadable_product' === $_POST['type'] ) {
|
||||
|
||||
if ( empty( $pathdata['subdir'] ) ) {
|
||||
$pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads';
|
||||
$pathdata['url'] = $pathdata['url'] . '/woocommerce_uploads';
|
||||
$pathdata['subdir'] = '/woocommerce_uploads';
|
||||
} else {
|
||||
$new_subdir = '/woocommerce_uploads' . $pathdata['subdir'];
|
||||
|
||||
$pathdata['path'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] );
|
||||
$pathdata['url'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] );
|
||||
$pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] );
|
||||
}
|
||||
}
|
||||
return $pathdata;
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Change filename for WooCommerce uploads and prepend unique chars for security.
|
||||
*
|
||||
* @param string $full_filename Original filename.
|
||||
* @param string $ext Extension of file.
|
||||
* @param string $dir Directory path.
|
||||
*
|
||||
* @return string New filename with unique hash.
|
||||
* @since 4.0
|
||||
*/
|
||||
public function update_filename( $full_filename, $ext, $dir ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
if ( ! isset( $_POST['type'] ) || ! 'downloadable_product' === $_POST['type'] ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
if ( ! strpos( $dir, 'woocommerce_uploads' ) ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
if ( 'no' === get_option( 'woocommerce_downloads_add_hash_to_filename' ) ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
return $this->unique_filename( $full_filename, $ext );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Change filename to append random text.
|
||||
*
|
||||
* @param string $full_filename Original filename with extension.
|
||||
* @param string $ext Extension.
|
||||
*
|
||||
* @return string Modified filename.
|
||||
*/
|
||||
public function unique_filename( $full_filename, $ext ) {
|
||||
$ideal_random_char_length = 6; // Not going with a larger length because then downloaded filename will not be pretty.
|
||||
$max_filename_length = 255; // Max file name length for most file systems.
|
||||
$length_to_prepend = min( $ideal_random_char_length, $max_filename_length - strlen( $full_filename ) - 1 );
|
||||
|
||||
if ( 1 > $length_to_prepend ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
$suffix = strtolower( wp_generate_password( $length_to_prepend, false, false ) );
|
||||
$filename = $full_filename;
|
||||
|
||||
if ( strlen( $ext ) > 0 ) {
|
||||
$filename = substr( $filename, 0, strlen( $filename ) - strlen( $ext ) );
|
||||
}
|
||||
|
||||
$full_filename = str_replace(
|
||||
$filename,
|
||||
"$filename-$suffix",
|
||||
$full_filename
|
||||
);
|
||||
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a filter when uploading a downloadable product.
|
||||
*/
|
||||
public function woocommerce_media_upload_downloadable_product() {
|
||||
do_action( 'media_upload_file' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant downloadable file access to any newly added files on any existing.
|
||||
* orders for this product that have previously been granted downloadable file access.
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
/**
|
||||
* Add hooks related to uploading downloadable products.
|
||||
*
|
||||
* @package WooCommerce\Admin
|
||||
* @version 8.5.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( class_exists( 'WC_Admin_Upload_Downloadable_Product', false ) ) {
|
||||
return new WC_Admin_Upload_Downloadable_Product();
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Admin_Upload_Downloadable_Product Class.
|
||||
*/
|
||||
class WC_Admin_Upload_Downloadable_Product {
|
||||
/**
|
||||
* Add hooks.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
|
||||
add_filter( 'wp_unique_filename', array( $this, 'update_filename' ), 10, 3 );
|
||||
add_action( 'media_upload_downloadable_product', array( $this, 'media_upload_downloadable_product' ) );
|
||||
}
|
||||
/**
|
||||
* Change upload dir for downloadable files.
|
||||
*
|
||||
* @param array $pathdata Array of paths.
|
||||
* @return array
|
||||
*/
|
||||
public function upload_dir( $pathdata ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
if ( isset( $_POST['type'] ) && 'downloadable_product' === $_POST['type'] ) {
|
||||
|
||||
if ( empty( $pathdata['subdir'] ) ) {
|
||||
$pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads';
|
||||
$pathdata['url'] = $pathdata['url'] . '/woocommerce_uploads';
|
||||
$pathdata['subdir'] = '/woocommerce_uploads';
|
||||
} else {
|
||||
$new_subdir = '/woocommerce_uploads' . $pathdata['subdir'];
|
||||
|
||||
$pathdata['path'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] );
|
||||
$pathdata['url'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] );
|
||||
$pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] );
|
||||
}
|
||||
}
|
||||
return $pathdata;
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Change filename for WooCommerce uploads and prepend unique chars for security.
|
||||
*
|
||||
* @param string $full_filename Original filename.
|
||||
* @param string $ext Extension of file.
|
||||
* @param string $dir Directory path.
|
||||
*
|
||||
* @return string New filename with unique hash.
|
||||
* @since 4.0
|
||||
*/
|
||||
public function update_filename( $full_filename, $ext, $dir ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
if ( ! isset( $_POST['type'] ) || ! 'downloadable_product' === $_POST['type'] ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
if ( ! strpos( $dir, 'woocommerce_uploads' ) ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
if ( 'no' === get_option( 'woocommerce_downloads_add_hash_to_filename' ) ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
return $this->unique_filename( $full_filename, $ext );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Change filename to append random text.
|
||||
*
|
||||
* @param string $full_filename Original filename with extension.
|
||||
* @param string $ext Extension.
|
||||
*
|
||||
* @return string Modified filename.
|
||||
*/
|
||||
public function unique_filename( $full_filename, $ext ) {
|
||||
$ideal_random_char_length = 6; // Not going with a larger length because then downloaded filename will not be pretty.
|
||||
$max_filename_length = 255; // Max file name length for most file systems.
|
||||
$length_to_prepend = min( $ideal_random_char_length, $max_filename_length - strlen( $full_filename ) - 1 );
|
||||
|
||||
if ( 1 > $length_to_prepend ) {
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
$suffix = strtolower( wp_generate_password( $length_to_prepend, false, false ) );
|
||||
$filename = $full_filename;
|
||||
|
||||
if ( strlen( $ext ) > 0 ) {
|
||||
$filename = substr( $filename, 0, strlen( $filename ) - strlen( $ext ) );
|
||||
}
|
||||
|
||||
$full_filename = str_replace(
|
||||
$filename,
|
||||
"$filename-$suffix",
|
||||
$full_filename
|
||||
);
|
||||
|
||||
return $full_filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a filter when uploading a downloadable product.
|
||||
*/
|
||||
public function woocommerce_media_upload_downloadable_product() {
|
||||
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
do_action( 'media_upload_file' );
|
||||
}
|
||||
}
|
|
@ -49,6 +49,9 @@ class Init {
|
|||
|
||||
// Add currency symbol to orders endpoint response.
|
||||
add_filter( 'woocommerce_rest_prepare_shop_order_object', array( __CLASS__, 'add_currency_symbol_to_order_response' ) );
|
||||
|
||||
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-upload-downloadable-product.php';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,18 +11,19 @@
|
|||
class WC_Test_Admin_Post_Types extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Instance of WC_Admin_Post_Types.
|
||||
* Instance of WC_Admin_Upload_Downloadable_Product.
|
||||
*
|
||||
* @var \WC_Admin_Post_Types
|
||||
* @var \WC_Admin_Upload_Downloadable_Product
|
||||
*/
|
||||
protected $wc_cpt;
|
||||
|
||||
|
||||
/**
|
||||
* Setup. Create a instance to use throughout.
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->wc_cpt = new WC_Admin_Post_Types();
|
||||
$this->wc_cpt = new WC_Admin_Upload_Downloadable_Product();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue