Merged in feature/280-dev-dev01 (pull request #21)

auto-patch  280-dev-dev01-2024-01-19T16_41_58

* auto-patch  280-dev-dev01-2024-01-19T16_41_58
This commit is contained in:
Tony Volpe
2024-01-19 16:44:43 +00:00
parent 2699b5437a
commit be83910651
2125 changed files with 179300 additions and 35639 deletions

View File

@@ -8,6 +8,7 @@
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Internal\Admin\Analytics;
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
if ( ! defined( 'ABSPATH' ) ) {
@@ -27,6 +28,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'admin_styles' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
}
/**
@@ -554,6 +556,73 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
}
}
/**
* Enqueue block editor assets.
*
* @return void
*/
public function enqueue_block_editor_assets() {
$settings_tabs = apply_filters('woocommerce_settings_tabs_array', []);
if ( is_array( $settings_tabs ) && count( $settings_tabs ) > 0 ) {
$formatted_settings_tabs = array();
foreach ($settings_tabs as $key => $label) {
if (
is_string( $key ) && $key !== "" &&
is_string( $label ) && $label !== ""
) {
$formatted_settings_tabs[] = array(
'key' => $key,
'label' => wp_strip_all_tags( $label ),
);
}
}
WCAdminAssets::register_script( 'wp-admin-scripts', 'command-palette' );
wp_localize_script(
'wc-admin-command-palette',
'wcCommandPaletteSettings',
array(
'settingsTabs' => $formatted_settings_tabs,
)
);
}
$admin_features_disabled = apply_filters( 'woocommerce_admin_disabled', false );
if ( ! $admin_features_disabled ) {
$analytics_reports = Analytics::get_report_pages();
if ( is_array( $analytics_reports ) && count( $analytics_reports ) > 0 ) {
$formatted_analytics_reports = array_map( function( $report ) {
if ( ! is_array( $report ) ) {
return null;
}
$title = array_key_exists( 'title', $report ) ? $report['title'] : '';
$path = array_key_exists( 'path', $report ) ? $report['path'] : '';
if (
is_string( $title ) && $title !== "" &&
is_string( $path ) && $path !== ""
) {
return array(
'title' => wp_strip_all_tags( $title ),
'path' => $path,
);
}
return null;
}, $analytics_reports );
$formatted_analytics_reports = array_filter( $formatted_analytics_reports, 'is_array' );
WCAdminAssets::register_script( 'wp-admin-scripts', 'command-palette-analytics' );
wp_localize_script(
'wc-admin-command-palette-analytics',
'wcCommandPaletteAnalytics',
array(
'reports' => $formatted_analytics_reports,
)
);
}
}
}
/**
* Helper function to determine whether the current screen is an order edit screen.
*

View File

@@ -17,6 +17,12 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
}
class WC_Admin_Log_Table_List extends WP_List_Table {
/**
* The key for the user option of how many list table items to display per page.
*
* @const string
*/
public const PER_PAGE_USER_OPTION_KEY = 'woocommerce_status_log_items_per_page';
/**
* Initialize the log table list.
@@ -92,6 +98,40 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
<?php
}
/**
* Generates the table rows.
*
* @return void
*/
public function display_rows() {
foreach ( $this->items as $log ) {
$this->single_row( $log );
if ( ! empty( $log['context'] ) ) {
$this->context_row( $log );
}
}
}
/**
* Render the additional table row that contains extra log context data.
*
* @param array $log Log entry data.
*
* @return void
*/
protected function context_row( $log ) {
// Maintains alternating row background colors.
?>
<tr style="display: none"><td></td></tr>
<tr id="log-context-<?php echo esc_attr( $log['log_id'] ); ?>" class="log-context">
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>">
<p><strong><?php esc_html_e( 'Additional context', 'woocommerce' ); ?></strong></p>
<pre><?php echo esc_html( $log['context'] ); ?></pre>
</td>
</tr>
<?php
}
/**
* Get list columns.
*
@@ -104,6 +144,7 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
'level' => __( 'Level', 'woocommerce' ),
'message' => __( 'Message', 'woocommerce' ),
'source' => __( 'Source', 'woocommerce' ),
'context' => __( 'Context', 'woocommerce' ),
);
}
@@ -167,7 +208,10 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
* @return string
*/
public function column_message( $log ) {
return esc_html( $log['message'] );
return sprintf(
'<pre>%s</pre>',
esc_html( $log['message'] )
);
}
/**
@@ -180,6 +224,36 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
return esc_html( $log['source'] );
}
/**
* Context column.
*
* @param array $log Log entry data.
*
* @return string
*/
public function column_context( $log ) {
$content = '';
if ( ! empty( $log['context'] ) ) {
ob_start();
?>
<button
class="log-toggle button button-secondary button-small"
data-log-id="<?php echo esc_attr( $log['log_id'] ); ?>"
data-toggle-status="off"
data-label-show="<?php esc_attr_e( 'Show context', 'woocommerce' ); ?>"
data-label-hide="<?php esc_attr_e( 'Hide context', 'woocommerce' ); ?>"
>
<span class="dashicons dashicons-arrow-down-alt2"></span>
<span class="log-toggle-label screen-reader-text"><?php esc_html_e( 'Show context', 'woocommerce' ); ?></span>
</button>
<?php
$content = ob_get_clean();
}
return $content;
}
/**
* Get bulk actions.
*
@@ -265,7 +339,10 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
$this->prepare_column_headers();
$per_page = $this->get_items_per_page( 'woocommerce_status_log_items_per_page', 10 );
$per_page = $this->get_items_per_page(
self::PER_PAGE_USER_OPTION_KEY,
$this->get_per_page_default()
);
$where = $this->get_items_query_where();
$order = $this->get_items_query_order();
@@ -273,7 +350,7 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
$offset = $this->get_items_query_offset();
$query_items = "
SELECT log_id, timestamp, level, message, source
SELECT log_id, timestamp, level, message, source, context
FROM {$wpdb->prefix}woocommerce_log
{$where} {$order} {$limit} {$offset}
";
@@ -302,7 +379,11 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
protected function get_items_query_limit() {
global $wpdb;
$per_page = $this->get_items_per_page( 'woocommerce_status_log_items_per_page', 10 );
$per_page = $this->get_items_per_page(
self::PER_PAGE_USER_OPTION_KEY,
$this->get_per_page_default()
);
return $wpdb->prepare( 'LIMIT %d', $per_page );
}
@@ -316,7 +397,10 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
protected function get_items_query_offset() {
global $wpdb;
$per_page = $this->get_items_per_page( 'woocommerce_status_log_items_per_page', 10 );
$per_page = $this->get_items_per_page(
self::PER_PAGE_USER_OPTION_KEY,
$this->get_per_page_default()
);
$current_page = $this->get_pagenum();
if ( 1 < $current_page ) {
$offset = $per_page * ( $current_page - 1 );
@@ -392,4 +476,13 @@ class WC_Admin_Log_Table_List extends WP_List_Table {
$this->get_sortable_columns(),
);
}
/**
* Helper to get the default value for the per_page arg.
*
* @return int
*/
public function get_per_page_default(): int {
return 20;
}
}

View File

@@ -12,7 +12,7 @@ use Automattic\WooCommerce\Internal\Admin\Marketplace;
use Automattic\WooCommerce\Internal\Admin\Orders\COTRedirectionController;
use Automattic\WooCommerce\Internal\Admin\Orders\PageController as Custom_Orders_PageController;
use Automattic\WooCommerce\Internal\Admin\Logging\PageController as LoggingPageController;
use Automattic\WooCommerce\Internal\Admin\Logging\FileV2\ListTable as LoggingListTable;
use Automattic\WooCommerce\Internal\Admin\Logging\FileV2\{ FileListTable, SearchListTable };
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -324,7 +324,9 @@ class WC_Admin_Menus {
$screen_options = array(
'woocommerce_keys_per_page',
'woocommerce_webhooks_per_page',
LoggingListTable::PER_PAGE_USER_OPTION_KEY,
FileListTable::PER_PAGE_USER_OPTION_KEY,
SearchListTable::PER_PAGE_USER_OPTION_KEY,
WC_Admin_Log_Table_List::PER_PAGE_USER_OPTION_KEY,
);
if ( in_array( $option, $screen_options, true ) ) {

View File

@@ -280,6 +280,19 @@ class WC_Admin_Notices {
do_action( 'woocommerce_hide_' . $name . '_notice' );
}
/**
* Check if a given user has dismissed a given admin notice.
*
* @since 8.5.0
*
* @param string $name The name of the admin notice to check.
* @param int|null $user_id User id, or null for the current user.
* @return bool True if the user has dismissed the notice.
*/
public static function user_has_dismissed_notice( string $name, ?int $user_id = null ): bool {
return (bool) get_user_meta( $user_id ?? get_current_user_id(), "dismissed_{$name}_notice", true );
}
/**
* Add notices + styles if needed.
*/

View File

@@ -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.

View File

@@ -212,7 +212,7 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
$save_fields = $this->get_customer_meta_fields();
foreach ( $save_fields as $fieldset ) {
foreach ( $save_fields as $fieldset_type => $fieldset ) {
foreach ( $fieldset['fields'] as $key => $field ) {
@@ -222,6 +222,25 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) :
update_user_meta( $user_id, $key, wc_clean( $_POST[ $key ] ) );
}
}
// Skip firing the action for any non-internal fieldset types.
if ( ! in_array( $fieldset_type, array( 'billing', 'shipping' ), true ) ) {
continue;
}
// Fieldset type is an internal address type.
$address_type = $fieldset_type;
/**
* Hook: woocommerce_customer_save_address.
*
* Fires after a customer address has been saved on the user profile admin screen.
*
* @since 8.5.0
* @param int $user_id User ID being saved.
* @param string $address_type Type of address; 'billing' or 'shipping'.
*/
do_action( 'woocommerce_customer_save_address', $user_id, $address_type );
}
}

View File

@@ -15,6 +15,12 @@ defined( 'ABSPATH' ) || exit;
* WC_Admin_Status Class.
*/
class WC_Admin_Status {
/**
* An instance of the DB log handler list table.
*
* @var WC_Admin_Log_Table_List
*/
private static $db_log_list_table;
/**
* Handles output of the reports page in admin.
@@ -133,15 +139,17 @@ class WC_Admin_Status {
* Show the log page contents for db log handler.
*/
public static function status_logs_db() {
if ( ! empty( $_REQUEST['flush-logs'] ) ) { // WPCS: input var ok, CSRF ok.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handled in flush_db_logs().
if ( isset( $_REQUEST['flush-logs'] ) ) {
self::flush_db_logs();
}
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['log'] ) ) { // WPCS: input var ok, CSRF ok.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce handled in log_table_bulk_actions().
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['log'] ) ) {
self::log_table_bulk_actions();
}
$log_table_list = new WC_Admin_Log_Table_List();
$log_table_list = self::get_db_log_list_table();
$log_table_list->prepare_items();
include_once __DIR__ . '/views/html-admin-page-status-logs-db.php';
@@ -306,20 +314,38 @@ class WC_Admin_Status {
exit();
}
/**
* Return a stored instance of the DB log list table class.
*
* @return WC_Admin_Log_Table_List
*/
public static function get_db_log_list_table() {
if ( is_null( self::$db_log_list_table ) ) {
self::$db_log_list_table = new WC_Admin_Log_Table_List();
}
return self::$db_log_list_table;
}
/**
* Clear DB log table.
*
* @since 3.0.0
*/
private static function flush_db_logs() {
if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'woocommerce-status-logs' ) ) { // WPCS: input var ok, sanitization ok.
wp_die( esc_html__( 'Action failed. Please refresh the page and retry.', 'woocommerce' ) );
check_admin_referer( 'bulk-logs' );
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'You do not have permission to manage log entries.', 'woocommerce' ) );
}
WC_Log_Handler_DB::flush();
wp_safe_redirect( esc_url_raw( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) );
exit();
$sendback = wp_sanitize_redirect( admin_url( 'admin.php?page=wc-status&tab=logs' ) );
wp_safe_redirect( $sendback );
exit;
}
/**
@@ -328,15 +354,22 @@ class WC_Admin_Status {
* @since 3.0.0
*/
private static function log_table_bulk_actions() {
if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'woocommerce-status-logs' ) ) { // WPCS: input var ok, sanitization ok.
wp_die( esc_html__( 'Action failed. Please refresh the page and retry.', 'woocommerce' ) );
check_admin_referer( 'bulk-logs' );
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'You do not have permission to manage log entries.', 'woocommerce' ) );
}
$log_ids = array_map( 'absint', (array) isset( $_REQUEST['log'] ) ? wp_unslash( $_REQUEST['log'] ) : array() ); // WPCS: input var ok, sanitization ok.
$log_ids = (array) filter_input( INPUT_GET, 'log', FILTER_CALLBACK, array( 'options' => 'absint' ) );
if ( ( isset( $_REQUEST['action'] ) && 'delete' === $_REQUEST['action'] ) || ( isset( $_REQUEST['action2'] ) && 'delete' === $_REQUEST['action2'] ) ) { // WPCS: input var ok, sanitization ok.
$action = self::get_db_log_list_table()->current_action();
if ( 'delete' === $action ) {
WC_Log_Handler_DB::delete( $log_ids );
wp_safe_redirect( esc_url_raw( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) );
$sendback = remove_query_arg( array( 'action', 'action2', 'log', '_wpnonce', '_wp_http_referer' ), wp_get_referer() );
wp_safe_redirect( $sendback );
exit();
}
}

View File

@@ -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' );
}
}

View File

@@ -99,11 +99,6 @@ class WC_Helper_Plugin_Info {
$results = json_decode( wp_remote_retrieve_body( $request ), true );
if ( ! empty( $results ) ) {
$response = (object) $results;
$product = array_shift( $products );
if ( isset( $product['package'] ) ) {
$response->download_link = $product['package'];
}
}
return $response;

View File

@@ -94,6 +94,22 @@ class WC_Helper_Subscriptions_API {
),
)
);
register_rest_route(
'wc/v3',
'/marketplace/subscriptions/install-url',
array(
'methods' => 'GET',
'callback' => array( __CLASS__, 'install_url' ),
'permission_callback' => array( __CLASS__, 'get_permission' ),
'args' => array(
'product_key' => array(
'required' => true,
'type' => 'string',
),
),
)
);
}
/**
@@ -257,6 +273,50 @@ class WC_Helper_Subscriptions_API {
),
);
}
/**
* Get the install URL for a WooCommerce.com product.
*
* @param WP_REST_Request $request Request object.
*/
public static function install_url( $request ) {
$product_key = $request->get_param( 'product_key' );
$subscription = WC_Helper::get_subscription( $product_key );
if ( ! $subscription ) {
wp_send_json_error(
array(
'message' => __( 'We couldn\'t find a subscription for this product.', 'woocommerce' ),
),
400
);
}
if ( true === $subscription['local']['installed'] ) {
wp_send_json_success(
array(
'message' => __( 'This product is already installed.', 'woocommerce' ),
),
);
}
$install_url = WC_Helper::get_subscription_install_url( $subscription['product_key'] );
if ( ! $install_url ) {
wp_send_json_error(
array(
'message' => __( 'There was an error getting the install URL for this product.', 'woocommerce' ),
),
400
);
}
wp_send_json_success(
array(
'url' => $install_url,
),
);
}
}
WC_Helper_Subscriptions_API::load();

View File

@@ -6,6 +6,7 @@
*/
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
if ( ! defined( 'ABSPATH' ) ) {
exit;
@@ -648,6 +649,8 @@ class WC_Helper {
return;
}
self::maybe_redirect_to_new_marketplace_installer();
if ( empty( $_GET['section'] ) || 'helper' !== $_GET['section'] ) {
return;
}
@@ -681,17 +684,48 @@ class WC_Helper {
}
}
/**
* Maybe redirect to the new Marketplace installer.
*/
private static function maybe_redirect_to_new_marketplace_installer() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Redirect requires the "install" URL parameter to be passed.
if ( empty( $_GET['install'] ) ) {
return;
}
wp_safe_redirect(
self::get_helper_redirect_url(
array(
'page' => 'wc-addons',
'section' => 'helper',
),
isset( $_GET['redirect-to-wc-admin'] ),
sanitize_text_field( wp_unslash( $_GET['install'] ) )
)
);
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Get helper redirect URL.
*
* @param array $args Query args.
* @param bool $redirect_to_wc_admin Whether to redirect to WC Admin.
* @param array $args Query args.
* @param bool $redirect_to_wc_admin Whether to redirect to WC Admin.
* @param string $install_product_key Optional Product key to install.
* @return string
*/
private static function get_helper_redirect_url( $args = array(), $redirect_to_wc_admin = false ) {
private static function get_helper_redirect_url( $args = array(), $redirect_to_wc_admin = false, $install_product_key = '' ) {
global $current_screen;
if ( true === $redirect_to_wc_admin && 'woocommerce_page_wc-addons' === $current_screen->id ) {
return add_query_arg(
if (
'woocommerce_page_wc-addons' === $current_screen->id &&
FeaturesUtil::feature_is_enabled( 'marketplace' ) &&
(
true === $redirect_to_wc_admin ||
false === empty( $install_product_key )
)
) {
$new_url = add_query_arg(
array(
'page' => 'wc-admin',
'tab' => 'my-subscriptions',
@@ -699,6 +733,15 @@ class WC_Helper {
),
admin_url( 'admin.php' )
);
if ( ! empty( $install_product_key ) ) {
$new_url = add_query_arg(
array(
'install' => $install_product_key,
),
$new_url
);
}
return $new_url;
}
return add_query_arg(
@@ -727,6 +770,10 @@ class WC_Helper {
$redirect_url_args['redirect-to-wc-admin'] = 1;
}
if ( isset( $_GET['install'] ) ) {
$redirect_url_args['install'] = sanitize_text_field( wp_unslash( $_GET['install'] ) );
}
$redirect_uri = add_query_arg(
$redirect_url_args,
admin_url( 'admin.php' )
@@ -795,7 +842,8 @@ class WC_Helper {
'page' => 'wc-addons',
'section' => 'helper',
),
isset( $_GET['redirect-to-wc-admin'] )
isset( $_GET['redirect-to-wc-admin'] ),
isset( $_GET['install'] ) ? sanitize_text_field( wp_unslash( $_GET['install'] ) ) : ''
)
);
die();
@@ -858,7 +906,8 @@ class WC_Helper {
'section' => 'helper',
'wc-helper-status' => 'helper-connected',
),
isset( $_GET['redirect-to-wc-admin'] )
isset( $_GET['redirect-to-wc-admin'] ),
isset( $_GET['install'] ) ? sanitize_text_field( wp_unslash( $_GET['install'] ) ) : ''
)
);
die();
@@ -884,7 +933,8 @@ class WC_Helper {
'section' => 'helper',
'wc-helper-status' => 'helper-disconnected',
),
isset( $_GET['redirect-to-wc-admin'] )
isset( $_GET['redirect-to-wc-admin'] ),
isset( $_GET['install'] ) ? sanitize_text_field( wp_unslash( $_GET['install'] ) ) : ''
);
self::disconnect();
@@ -911,7 +961,8 @@ class WC_Helper {
'filter' => self::get_current_filter(),
'wc-helper-status' => 'helper-refreshed',
),
isset( $_GET['redirect-to-wc-admin'] )
isset( $_GET['redirect-to-wc-admin'] ),
isset( $_GET['install'] ) ? sanitize_text_field( wp_unslash( $_GET['install'] ) ) : ''
);
wp_safe_redirect( $redirect_uri );
@@ -1126,6 +1177,40 @@ class WC_Helper {
return $deactivated;
}
/**
* Get a subscriptions install URL.
*
* @param string $product_key Subscription product key.
* @return string
*/
public static function get_subscription_install_url( $product_key ) {
$install_url_response = WC_Helper_API::post(
'install-url',
array(
'authenticated' => true,
'body' => wp_json_encode(
array(
'product_key' => $product_key,
'wc_version' => WC()->version,
)
),
)
);
$code = wp_remote_retrieve_response_code( $install_url_response );
if ( 200 !== $code ) {
self::log( sprintf( 'Install URL API call returned a non-200 response code (%d)', $code ) );
return '';
}
$body = json_decode( wp_remote_retrieve_body( $install_url_response ), true );
if ( empty( $body['data']['url'] ) ) {
self::log( sprintf( 'Install URL API call returned an invalid body: %s', wp_remote_retrieve_body( $install_url_response ) ) );
return '';
}
return $body['data']['url'];
}
/**
* Deactivate a plugin.
*/

View File

@@ -393,11 +393,12 @@ class WC_Settings_Advanced extends WC_Settings_Page {
if ( ! is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' ) ) {
$enable_legacy_api_setting['desc_tip'] = sprintf(
// translators: Placeholder is a URL.
// translators: Placeholders are URLs.
__(
'⚠️ <b>The Legacy REST API will be removed in WooCommerce 9.0.</b> A separate WooCommerce extension will soon be available to keep it enabled. <b><a target="_blank" href="%s">Learn more about this change.</a></b>',
'⚠️ <b>The Legacy REST API will be removed in WooCommerce 9.0.</b> A separate WooCommerce extension will soon be available to keep it enabled. You can check Legacy REST API usages in <b><a target="_blank" href="%1$s">the WooCommerce log files</a></b> (file names start with <code>legacy_rest_api_usages</code>). <b><a target="_blank" href="%2$s">Learn more about this change.</a></b>',
'woocommerce'
),
admin_url( 'admin.php?page=wc-status&tab=logs' ),
'https://developer.woo.com/2023/10/03/the-legacy-rest-api-will-move-to-a-dedicated-extension-in-woocommerce-9-0/'
);
}

View File

@@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<h2 class="wc-shipping-zones-heading">
<span><?php esc_html_e( 'Shipping classes', 'woocommerce' ); ?></span>
<a class="button button-primary wc-shipping-class-add-new" href="#"><?php esc_html_e( 'Add shipping class', 'woocommerce' ); ?></a>
<a class="page-title-action wc-shipping-class-add-new" href="#"><?php esc_html_e( 'Add shipping class', 'woocommerce' ); ?></a>
</h2>
<p class="wc-shipping-zone-help-text">

View File

@@ -98,7 +98,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<script type="text/html" id="tmpl-wc-shipping-zone-method-row-blank">
<tr>
<td class="wc-shipping-zone-method-blank-state" colspan="4">
<td class="wc-shipping-zone-method-blank-state" colspan="5">
<p><?php esc_html_e( 'You can add multiple shipping methods within this zone. Only customers within the zone will see them.', 'woocommerce' ); ?></p>
</td>
</tr>
@@ -149,10 +149,13 @@ if ( ! defined( 'ABSPATH' ) ) {
</article>
<footer>
<div class="inner">
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large">
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
<div class="wc-backbone-modal-action-{{ data.status === 'existing' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
</button>
<div>
<button id="btn-back" class="button button-large wc-backbone-modal-back-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Back', 'woocommerce' ); ?></button>
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large">
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
<div class="wc-backbone-modal-action-{{ data.status === 'existing' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
</button>
</div>
<div class="wc-shipping-zone-method-modal-info wc-shipping-zone-method-modal-info-{{ data.status === 'existing' ? 'inactive' : 'active' }}"><?php esc_html_e( 'STEP 2 OF 2', 'woocommerce' ); ?></div>
</div>
</footer>
@@ -198,9 +201,20 @@ if ( ! defined( 'ABSPATH' ) ) {
if ( ! $method->supports( 'shipping-zones' ) ) {
continue;
}
$description = wp_kses_post( $method->get_method_description() );
echo '<div class="wc-shipping-zone-method-input"><input type="radio" value="' . esc_attr( $method->id ) . '" id="' . esc_attr( $method->id ) . '" name="add_method_id"/><label for="' . esc_attr( $method->id ) . '">' . esc_html( $method->get_method_title() ) . '<span class="dashicons dashicons-yes"></span></label><div class="wc-shipping-zone-method-input-help-text"><span>' . esc_html( $description ) . '</span></div></div>';
echo '<div class="wc-shipping-zone-method-input"><input type="radio" value="' . esc_attr( $method->id ) . '" id="' . esc_attr( $method->id ) . '" name="add_method_id"/><label for="' . esc_attr( $method->id ) . '">' . esc_html( $method->get_method_title() ) . '<span class="dashicons dashicons-yes"></span></label></div>';
}
echo '<div class="wc-shipping-zone-method-input-help-text-container">';
foreach ( $methods_placed_in_order as $method ) {
if ( ! $method->supports( 'shipping-zones' ) ) {
continue;
}
echo '<div id=' . esc_attr( $method->id ) . '-description class="wc-shipping-zone-method-input-help-text"><span>' . wp_kses_post( wpautop( $method->get_method_description() ) ) . '</span></div>';
}
echo '</div>'
?>
</fieldset>
</form>

View File

@@ -6,7 +6,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<h2 class="wc-shipping-zones-heading">
<span><?php esc_html_e( 'Shipping zones', 'woocommerce' ); ?></span>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&zone_id=new' ) ); ?>" class="button-primary"><?php esc_html_e( 'Add zone', 'woocommerce' ); ?></a>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&zone_id=new' ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add zone', 'woocommerce' ); ?></a>
</h2>
<p class="wc-shipping-zone-heading-help-text"><?php echo esc_html_e( 'A shipping zone consists of the region(s) you\'d like to ship to and the shipping method(s) offered. A shopper can only be matched to one zone, and we\'ll use their shipping address to show them the methods available in their area.', 'woocommerce' ); ?></p>
<table class="wc-shipping-zones widefat">
@@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</thead>
<tbody class="wc-shipping-zone-rows wc-shipping-tables-tbody"></tbody>
<tfoot data-id="0" class="wc-shipping-zone-worldwide">
<tfoot data-id="0" class="wc-shipping-zone-worldwide wc-shipping-zone-rows-tfoot">
<td width="1%" class="wc-shipping-zone-worldwide"></td>
<td class="wc-shipping-zone-name">
<?php esc_html_e( 'Rest of the world', 'woocommerce' ); ?>
@@ -55,7 +55,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<script type="text/html" id="tmpl-wc-shipping-zone-row-blank">
<?php if ( 0 === $method_count ) : ?>
<tr>
<td class="wc-shipping-zones-blank-state" colspan="4">
<td class="wc-shipping-zones-blank-state" colspan="5">
<p class="main"><?php _e( 'A shipping zone is a geographic region where a certain set of shipping methods and rates apply.', 'woocommerce' ); ?></p>
<p><?php _e( 'For example:', 'woocommerce' ); ?></p>
<ul>

View File

@@ -9,23 +9,56 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$delete_confirmation_js = sprintf(
"return window.confirm( '%s' )",
esc_js( __( 'Are you sure you want to clear all logs from the database?', 'woocommerce' ) )
);
?>
<form method="post" id="mainform" action="">
<?php $log_table_list->search_box( __( 'Search logs', 'woocommerce' ), 'log' ); ?>
<?php $log_table_list->display(); ?>
<form method="get" id="mainform">
<input type="hidden" name="page" value="wc-status" />
<input type="hidden" name="tab" value="logs" />
<?php submit_button( __( 'Flush all logs', 'woocommerce' ), 'delete', 'flush-logs' ); ?>
<?php wp_nonce_field( 'woocommerce-status-logs' ); ?>
<?php $log_table_list->search_box( __( 'Search logs', 'woocommerce' ), 'log' ); ?>
<?php $log_table_list->display(); ?>
<?php
submit_button(
__( 'Flush all logs', 'woocommerce' ),
'delete',
'flush-logs',
true,
array(
'onclick' => esc_attr( $delete_confirmation_js ),
)
);
?>
</form>
<?php
wc_enqueue_js(
"jQuery( '#flush-logs' ).on( 'click', function() {
if ( window.confirm('" . esc_js( __( 'Are you sure you want to clear all logs from the database?', 'woocommerce' ) ) . "') ) {
return true;
}
return false;
});"
);
<script>
document.addEventListener( 'DOMContentLoaded', function() {
var contextToggles = Array.from( document.getElementsByClassName( 'log-toggle' ) );
contextToggles.forEach( ( element ) => {
element.addEventListener( 'click', ( event ) => {
event.preventDefault();
const button = event.currentTarget;
const buttonLabel = button.querySelector( '.log-toggle-label' );
const buttonIcon = button.querySelector( '.dashicons' );
const context = document.getElementById( 'log-context-' + button.dataset.logId );
switch ( button.dataset.toggleStatus ) {
case 'off':
context.style.display = 'table-row';
buttonLabel.textContent = button.dataset.labelHide;
buttonIcon.classList.replace( 'dashicons-arrow-down-alt2', 'dashicons-arrow-up-alt2' );
button.dataset.toggleStatus = 'on';
break;
case 'on':
context.style.display = 'none';
buttonLabel.textContent = button.dataset.labelShow;
buttonIcon.classList.replace( 'dashicons-arrow-up-alt2', 'dashicons-arrow-down-alt2' );
button.dataset.toggleStatus = 'off';
break;
}
} );
} );
} );
</script>