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:
@@ -25,7 +25,7 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
|
||||
* @return string Formatted time for use in log entry.
|
||||
*/
|
||||
protected static function format_time( $timestamp ) {
|
||||
return date( 'c', $timestamp );
|
||||
return gmdate( 'c', $timestamp );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,14 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
|
||||
$level_string = strtoupper( $level );
|
||||
$entry = "{$time_string} {$level_string} {$message}";
|
||||
|
||||
/**
|
||||
* Filter the formatted log entry before it is written.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string $entry The formatted entry.
|
||||
* @param array $args The raw data that gets assembled into a log entry.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_format_log_entry',
|
||||
$entry,
|
||||
@@ -54,4 +62,37 @@ abstract class WC_Log_Handler implements WC_Log_Handler_Interface {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a backtrace that shows where the logging function was called.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_backtrace() {
|
||||
// Get the filenames of the logging-related classes so we can ignore them.
|
||||
$ignore_files = array_map(
|
||||
function( $class ) {
|
||||
try {
|
||||
$reflector = new \ReflectionClass( $class );
|
||||
return $reflector->getFileName();
|
||||
} catch ( Exception $exception ) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
array( wc_get_logger(), self::class, static::class )
|
||||
);
|
||||
|
||||
$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
|
||||
|
||||
$filtered_backtrace = array_filter(
|
||||
$backtrace,
|
||||
function( $frame ) use ( $ignore_files ) {
|
||||
$ignore = isset( $frame['file'] ) && in_array( $frame['file'], $ignore_files, true );
|
||||
|
||||
return ! $ignore;
|
||||
}
|
||||
);
|
||||
|
||||
return array_values( $filtered_backtrace );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1603,7 +1603,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
if ( $this->get_parent_id() ) {
|
||||
$parent_product = wc_get_product( $this->get_parent_id() );
|
||||
|
||||
if ( $parent_product && 'publish' !== $parent_product->get_status() ) {
|
||||
if ( $parent_product && 'publish' !== $parent_product->get_status() && ! current_user_can( 'edit_post', $parent_product->get_id() ) ) {
|
||||
$visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
|
||||
$settings_html = $this->generate_settings_html( $this->get_form_fields(), false );
|
||||
}
|
||||
|
||||
return '<div class="wc-shipping-zone-method-fields">' . $settings_html . '</div>';
|
||||
return '<table class="form-table">' . $settings_html . '</table>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ) ) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' );
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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/'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2714,11 +2714,13 @@ class WC_AJAX {
|
||||
$variation = wc_get_product( $variation_id );
|
||||
|
||||
if ( 'false' !== $data['date_from'] ) {
|
||||
$variation->set_date_on_sale_from( wc_clean( $data['date_from'] ) );
|
||||
$date_on_sale_from = date( 'Y-m-d 00:00:00', strtotime( wc_clean( $data['date_from'] ) ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
$variation->set_date_on_sale_from( $date_on_sale_from );
|
||||
}
|
||||
|
||||
if ( 'false' !== $data['date_to'] ) {
|
||||
$variation->set_date_on_sale_to( wc_clean( $data['date_to'] ) );
|
||||
$date_on_sale_to = date( 'Y-m-d 23:59:59', strtotime( wc_clean( $data['date_to'] ) ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
||||
$variation->set_date_on_sale_to( $date_on_sale_to );
|
||||
}
|
||||
|
||||
$variation->save();
|
||||
|
||||
@@ -116,8 +116,9 @@ final class WC_Cart_Session {
|
||||
|
||||
// Populate cart from order.
|
||||
if ( isset( $_GET['order_again'], $_GET['_wpnonce'] ) && is_user_logged_in() && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'woocommerce-order_again' ) ) { // WPCS: input var ok, sanitization ok.
|
||||
$cart = $this->populate_cart_from_order( absint( $_GET['order_again'] ), $cart ); // WPCS: input var ok.
|
||||
$order_again = true;
|
||||
$cart = $this->populate_cart_from_order( absint( $_GET['order_again'] ), $cart ); // WPCS: input var ok.
|
||||
$order_again = true;
|
||||
$update_cart_session = true;
|
||||
}
|
||||
|
||||
// Prime caches to reduce future queries.
|
||||
@@ -161,16 +162,16 @@ final class WC_Cart_Session {
|
||||
*/
|
||||
do_action( 'woocommerce_remove_cart_item_from_session', $key, $values, $product );
|
||||
|
||||
/**
|
||||
* Allow 3rd parties to override this item's is_purchasable() result with cart item data.
|
||||
*
|
||||
* @param bool $is_purchasable If false, the item will not be added to the cart. Default: product's is_purchasable() status.
|
||||
* @param string $key Cart item key.
|
||||
* @param array $values Cart item values e.g. quantity and product_id.
|
||||
* @param WC_Product $product The product being added to the cart.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
/**
|
||||
* Allow 3rd parties to override this item's is_purchasable() result with cart item data.
|
||||
*
|
||||
* @param bool $is_purchasable If false, the item will not be added to the cart. Default: product's is_purchasable() status.
|
||||
* @param string $key Cart item key.
|
||||
* @param array $values Cart item values e.g. quantity and product_id.
|
||||
* @param WC_Product $product The product being added to the cart.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
} elseif ( ! apply_filters( 'woocommerce_cart_item_is_purchasable', $product->is_purchasable(), $key, $values, $product ) ) {
|
||||
$update_cart_session = true;
|
||||
/* translators: %s: product name */
|
||||
@@ -251,18 +252,74 @@ final class WC_Cart_Session {
|
||||
/**
|
||||
* Will set cart cookies if needed and when possible.
|
||||
*
|
||||
* Headers are only updated if headers have not yet been sent.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function maybe_set_cart_cookies() {
|
||||
if ( ! headers_sent() && did_action( 'wp_loaded' ) ) {
|
||||
if ( ! $this->cart->is_empty() ) {
|
||||
$this->set_cart_cookies( true );
|
||||
} elseif ( isset( $_COOKIE['woocommerce_items_in_cart'] ) ) { // WPCS: input var ok.
|
||||
$this->set_cart_cookies( false );
|
||||
if ( headers_sent() || ! did_action( 'wp_loaded' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $this->cart->is_empty() ) {
|
||||
$this->set_cart_cookies( true );
|
||||
} elseif ( isset( $_COOKIE['woocommerce_items_in_cart'] ) ) { // WPCS: input var ok.
|
||||
$this->set_cart_cookies( false );
|
||||
}
|
||||
$this->dedupe_cookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicate cookies from the response.
|
||||
*/
|
||||
private function dedupe_cookies() {
|
||||
$all_cookies = array_filter(
|
||||
headers_list(),
|
||||
function( $header ) {
|
||||
return stripos( $header, 'Set-Cookie:' ) !== false;
|
||||
}
|
||||
);
|
||||
$final_cookies = array();
|
||||
$update_cookies = false;
|
||||
foreach ( $all_cookies as $cookie ) {
|
||||
|
||||
list(, $cookie_value) = explode( ':', $cookie, 2 );
|
||||
list($cookie_name, $cookie_value) = explode( '=', trim( $cookie_value ), 2 );
|
||||
|
||||
if ( stripos( $cookie_name, 'woocommerce_' ) !== false ) {
|
||||
$key = $this->find_cookie_by_name( $cookie_name, $final_cookies );
|
||||
if ( false !== $key ) {
|
||||
$update_cookies = true;
|
||||
unset( $final_cookies[ $key ] );
|
||||
}
|
||||
}
|
||||
$final_cookies[] = $cookie;
|
||||
}
|
||||
|
||||
if ( $update_cookies ) {
|
||||
header_remove( 'Set-Cookie' );
|
||||
foreach ( $final_cookies as $cookie ) {
|
||||
// Using header here preserves previous cookie args.
|
||||
header( $cookie, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a cookie by name in an array of cookies.
|
||||
*
|
||||
* @param string $cookie_name Name of the cookie to find.
|
||||
* @param array $cookies Array of cookies to search.
|
||||
* @return mixed Key of the cookie if found, false if not.
|
||||
*/
|
||||
private function find_cookie_by_name( $cookie_name, $cookies ) {
|
||||
foreach ( $cookies as $key => $cookie ) {
|
||||
if ( strpos( $cookie, $cookie_name ) !== false ) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the php session data for the cart and coupons.
|
||||
*/
|
||||
@@ -331,6 +388,7 @@ final class WC_Cart_Session {
|
||||
foreach ( $setcookies as $name => $value ) {
|
||||
if ( ! isset( $_COOKIE[ $name ] ) || $_COOKIE[ $name ] !== $value ) {
|
||||
wc_setcookie( $name, $value );
|
||||
$_COOKIE[ $name ] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -759,7 +759,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
||||
public function check_cart_item_stock() {
|
||||
$error = new WP_Error();
|
||||
$product_qty_in_cart = $this->get_cart_item_quantities();
|
||||
$current_session_order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0;
|
||||
$current_session_order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : absint( WC()->session->get( 'store_api_draft_order', 0 ) );
|
||||
|
||||
foreach ( $this->get_cart() as $values ) {
|
||||
$product = $values['data'];
|
||||
|
||||
@@ -303,6 +303,11 @@ class WC_Logger implements WC_Logger_Interface {
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function clear_expired_logs() {
|
||||
/**
|
||||
* Filter the retention period of log entries.
|
||||
*
|
||||
* @param int $days The number of days to retain log entries.
|
||||
*/
|
||||
$days = absint( apply_filters( 'woocommerce_logger_days_to_retain_logs', 30 ) );
|
||||
$timestamp = strtotime( "-{$days} days" );
|
||||
|
||||
|
||||
@@ -269,7 +269,8 @@ class WC_Order_Item_Product extends WC_Order_Item {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtotal.
|
||||
* Gets the item subtotal. This is the price of the item times the quantity
|
||||
* excluding taxes before coupon discounts.
|
||||
*
|
||||
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
|
||||
* @return string
|
||||
@@ -289,7 +290,8 @@ class WC_Order_Item_Product extends WC_Order_Item {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total.
|
||||
* Gets the item total. This is the price of the item times the quantity
|
||||
* excluding taxes after coupon discounts.
|
||||
*
|
||||
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
|
||||
* @return string
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
* @package WooCommerce\Classes\Payment
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
@@ -15,6 +19,8 @@ defined( 'ABSPATH' ) || exit;
|
||||
*/
|
||||
class WC_Payment_Gateways {
|
||||
|
||||
use AccessiblePrivateMethods;
|
||||
|
||||
/**
|
||||
* Payment gateway classes.
|
||||
*
|
||||
@@ -113,6 +119,170 @@ class WC_Payment_Gateways {
|
||||
}
|
||||
|
||||
ksort( $this->payment_gateways );
|
||||
|
||||
self::add_action( 'wc_payment_gateways_initialized', array( $this, 'on_payment_gateways_initialized' ) );
|
||||
/**
|
||||
* Hook that is called when the payment gateways have been initialized.
|
||||
*
|
||||
* @param WC_Payment_Gateways $wc_payment_gateways The payment gateways instance.
|
||||
* @since 8.5.0
|
||||
*/
|
||||
do_action( 'wc_payment_gateways_initialized', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into payment gateway settings changes.
|
||||
*
|
||||
* @param WC_Payment_Gateways $wc_payment_gateways The WC_Payment_Gateways instance.
|
||||
* @since 8.5.0
|
||||
*/
|
||||
private function on_payment_gateways_initialized( WC_Payment_Gateways $wc_payment_gateways ) {
|
||||
foreach ( $this->payment_gateways as $gateway ) {
|
||||
$option_key = $gateway->get_option_key();
|
||||
self::add_action(
|
||||
'add_option_' . $option_key,
|
||||
function( $option, $value ) use ( $gateway ) {
|
||||
$this->payment_gateway_settings_option_changed( $gateway, $value, $option );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
self::add_action(
|
||||
'update_option_' . $option_key,
|
||||
function( $old_value, $value, $option ) use ( $gateway ) {
|
||||
$this->payment_gateway_settings_option_changed( $gateway, $value, $option, $old_value );
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a gateway settings option was added or updated.
|
||||
*
|
||||
* @param WC_Payment_Gateway $gateway The gateway for which the option was added or updated.
|
||||
* @param mixed $value New value.
|
||||
* @param string $option Option name.
|
||||
* @param mixed $old_value Old value. `null` when called via add_option_ hook.
|
||||
* @since 8.5.0
|
||||
*/
|
||||
private function payment_gateway_settings_option_changed( $gateway, $value, $option, $old_value = null ) {
|
||||
if ( ! $this->was_gateway_enabled( $value, $old_value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a change to a payment gateway's settings and it was just enabled. Let's send an email to the admin.
|
||||
// "untitled" shouldn't happen, but just in case.
|
||||
$this->notify_admin_payment_gateway_enabled( $gateway );
|
||||
}
|
||||
|
||||
/**
|
||||
* Email the site admin when a payment gateway has been enabled.
|
||||
*
|
||||
* @param WC_Payment_Gateway $gateway The gateway that was enabled.
|
||||
* @return bool Whether the email was sent or not.
|
||||
* @since 8.5.0
|
||||
*/
|
||||
private function notify_admin_payment_gateway_enabled( $gateway ) {
|
||||
$admin_email = get_option( 'admin_email' );
|
||||
$user = get_user_by( 'email', $admin_email );
|
||||
$username = $user ? $user->user_login : $admin_email;
|
||||
$gateway_title = $gateway->get_method_title();
|
||||
$gateway_settings_url = esc_url_raw( self_admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . $gateway->id ) );
|
||||
$site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
|
||||
$site_url = home_url();
|
||||
/**
|
||||
* Allows adding to the addresses that receive payment gateway enabled notifications.
|
||||
*
|
||||
* @param array $email_addresses The array of email addresses to notify.
|
||||
* @param WC_Payment_Gateway $gateway The gateway that was enabled.
|
||||
* @return array The augmented array of email addresses to notify.
|
||||
* @since 8.5.0
|
||||
*/
|
||||
$email_addresses = apply_filters( 'wc_payment_gateway_enabled_notification_email_addresses', array(), $gateway );
|
||||
$email_addresses[] = $admin_email;
|
||||
$email_addresses = array_unique(
|
||||
array_filter(
|
||||
$email_addresses,
|
||||
function( $email_address ) {
|
||||
return filter_var( $email_address, FILTER_VALIDATE_EMAIL );
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$logger = wc_get_container()->get( LegacyProxy::class )->call_function( 'wc_get_logger' );
|
||||
$logger->info( sprintf( 'Payment gateway enabled: "%s"', $gateway_title ) );
|
||||
|
||||
$email_text = sprintf(
|
||||
/* translators: Payment gateway enabled notification email. 1: Username, 2: Gateway Title, 3: Site URL, 4: Gateway Settings URL, 5: Admin Email, 6: Site Name, 7: Site URL. */
|
||||
__(
|
||||
'Howdy %1$s,
|
||||
|
||||
The payment gateway "%2$s" was just enabled on this site:
|
||||
%3$s
|
||||
|
||||
If this was intentional you can safely ignore and delete this email.
|
||||
|
||||
If you did not enable this payment gateway, please log in to your site and consider disabling it here:
|
||||
%4$s
|
||||
|
||||
This email has been sent to %5$s
|
||||
|
||||
Regards,
|
||||
All at %6$s
|
||||
%7$s',
|
||||
'woocommerce'
|
||||
),
|
||||
$username,
|
||||
$gateway_title,
|
||||
$site_url,
|
||||
$gateway_settings_url,
|
||||
$admin_email,
|
||||
$site_name,
|
||||
$site_url
|
||||
);
|
||||
|
||||
if ( '' !== get_option( 'blogname' ) ) {
|
||||
$site_title = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
|
||||
} else {
|
||||
$site_title = wp_parse_url( home_url(), PHP_URL_HOST );
|
||||
}
|
||||
|
||||
return wp_mail(
|
||||
$email_addresses,
|
||||
sprintf(
|
||||
/* translators: Payment gateway enabled notification email subject. %s1: Site title, $s2: Gateway title. */
|
||||
__( '[%1$s] Payment gateway "%2$s" enabled', 'woocommerce' ),
|
||||
$site_title,
|
||||
$gateway_title
|
||||
),
|
||||
$email_text
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines from changes in settings if a gateway was enabled.
|
||||
*
|
||||
* @param array $value New value.
|
||||
* @param array $old_value Old value.
|
||||
* @return bool Whether the gateway was enabled or not.
|
||||
*/
|
||||
private function was_gateway_enabled( $value, $old_value = null ) {
|
||||
if ( null === $old_value ) {
|
||||
// There was no old value, so this is a new option.
|
||||
if ( ! empty( $value ) && is_array( $value ) && isset( $value['enabled'] ) && 'yes' === $value['enabled'] && isset( $value['title'] ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// There was an old value, so this is an update.
|
||||
if (
|
||||
ArrayUtil::get_value_or_default( $value, 'enabled' ) === 'yes' &&
|
||||
ArrayUtil::get_value_or_default( $old_value, 'enabled' ) !== 'yes' ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@ use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||
use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
|
||||
use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as ProductDownloadDirectories;
|
||||
use Automattic\WooCommerce\Internal\ProductImage\MatchImageBySKU;
|
||||
use Automattic\WooCommerce\Internal\RegisterHooksInterface;
|
||||
use Automattic\WooCommerce\Internal\RestockRefundedItemsAdjuster;
|
||||
use Automattic\WooCommerce\Internal\Settings\OptionSanitizer;
|
||||
use Automattic\WooCommerce\Internal\Utilities\WebhookUtil;
|
||||
@@ -34,7 +36,7 @@ final class WooCommerce {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '8.4.0';
|
||||
public $version = '8.5.1';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
@@ -255,6 +257,7 @@ final class WooCommerce {
|
||||
$container->get( AssignDefaultCategory::class );
|
||||
$container->get( DataRegenerator::class );
|
||||
$container->get( LookupDataStore::class );
|
||||
$container->get( MatchImageBySKU::class );
|
||||
$container->get( RestockRefundedItemsAdjuster::class );
|
||||
$container->get( CustomOrdersTableController::class );
|
||||
$container->get( OptionSanitizer::class );
|
||||
@@ -262,6 +265,16 @@ final class WooCommerce {
|
||||
$container->get( FeaturesController::class );
|
||||
$container->get( WebhookUtil::class );
|
||||
$container->get( Marketplace::class );
|
||||
|
||||
/**
|
||||
* These classes have a register method for attaching hooks.
|
||||
*
|
||||
* @var RegisterHooksInterface[] $hook_register_classes
|
||||
*/
|
||||
$hook_register_classes = $container->get( RegisterHooksInterface::class );
|
||||
foreach ( $hook_register_classes as $hook_register_class ) {
|
||||
$hook_register_class->register();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,7 +1030,7 @@ final class WooCommerce {
|
||||
return;
|
||||
}
|
||||
|
||||
$message_one = __( 'You have installed a development version of WooCommerce which requires files to be built and minified. From the plugin directory, run <code>pnpm install</code> and then <code>pnpm run build --filter=woocommerce</code> to build and minify assets.', 'woocommerce' );
|
||||
$message_one = __( 'You have installed a development version of WooCommerce which requires files to be built and minified. From the plugin directory, run <code>pnpm install</code> and then <code>pnpm --filter=\'@woocommerce/plugin-woocommerce\' build</code> to build and minify assets.', 'woocommerce' );
|
||||
$message_two = sprintf(
|
||||
/* translators: 1: URL of WordPress.org Repository 2: URL of the GitHub Repository release page */
|
||||
__( 'Or you can download a pre-built version of the plugin from the <a href="%1$s">WordPress.org repository</a> or by visiting <a href="%2$s">the releases page in the GitHub repository</a>.', 'woocommerce' ),
|
||||
|
||||
@@ -647,7 +647,15 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_array( 'date_on_sale_from', $this->updated_props, true ) || in_array( 'date_on_sale_to', $this->updated_props, true ) || in_array( 'regular_price', $this->updated_props, true ) || in_array( 'sale_price', $this->updated_props, true ) || in_array( 'product_type', $this->updated_props, true ) ) {
|
||||
/**
|
||||
* It's tempting to add a check for `_price` in the updated props, so that when `wc_scheduled_sales` is called, we don't have to rely on `date_on_sale_from` being present in the list of updated props.
|
||||
*
|
||||
* However, it has a side effect of overriding the `_price` meta value with the `_sale_price` meta value when product is in sale, or with `_regular_price` meta value when product is not in sale. This is not desirable, because `_price` can also be set as a temporary active price for a product, and we don't want to override it.
|
||||
*
|
||||
* If we want to preserve previous sales schedules, a better way would be to store them in dedicated meta keys as logs.
|
||||
*/
|
||||
$product_price_props = array( 'date_on_sale_from', 'date_on_sale_to', 'regular_price', 'sale_price', 'product_type' );
|
||||
if ( count( array_intersect( $product_price_props, $this->updated_props ) ) > 0 ) {
|
||||
if ( $product->is_on_sale( 'edit' ) ) {
|
||||
update_post_meta( $product->get_id(), '_price', $product->get_sale_price( 'edit' ) );
|
||||
$product->set_price( $product->get_sale_price( 'edit' ) );
|
||||
|
||||
@@ -302,7 +302,9 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
||||
protected function set_image_data( &$product, $data ) {
|
||||
// Image URLs need converting to IDs before inserting.
|
||||
if ( isset( $data['raw_image_id'] ) ) {
|
||||
$product->set_image_id( $this->get_attachment_id_from_url( $data['raw_image_id'], $product->get_id() ) );
|
||||
$attachment_id = $this->get_attachment_id_from_url( $data['raw_image_id'], $product->get_id() );
|
||||
$product->set_image_id( $attachment_id );
|
||||
wc_product_attach_featured_image( $attachment_id, $product );
|
||||
}
|
||||
|
||||
// Gallery image URLs need converting to IDs before inserting.
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
* @since 2.6
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
@@ -17,6 +19,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Legacy_API {
|
||||
|
||||
use AccessiblePrivateMethods;
|
||||
|
||||
/**
|
||||
* This is the major version for the REST API and takes
|
||||
* first-order position in endpoint URLs.
|
||||
@@ -47,6 +51,8 @@ class WC_Legacy_API {
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'parse_request', array( $this, 'handle_rest_api_requests' ), 0 );
|
||||
$this->mark_method_as_accessible( 'maybe_display_legacy_wc_api_usage_notice' );
|
||||
self::add_action( 'admin_notices', array( $this, 'maybe_display_legacy_wc_api_usage_notice' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,6 +68,56 @@ class WC_Legacy_API {
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a log entry and update the last usage options, for a Legacy REST API request.
|
||||
*
|
||||
* @param string $route The Legacy REST API route requested.
|
||||
* @param string|null $user_agent The content of the user agent HTTP header in the request, null if not available.
|
||||
*/
|
||||
private function maybe_log_rest_api_request( string $route, ?string $user_agent ) {
|
||||
if ( is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user_agent = $user_agent ?? 'unknown';
|
||||
|
||||
$current_date = wp_date( 'Y-m-d H:i:s' );
|
||||
$stored_api_accesses = get_transient( 'wc_legacy_rest_api_usages' );
|
||||
if ( false === $stored_api_accesses ) {
|
||||
$stored_api_accesses = array(
|
||||
'user_agents' => array(),
|
||||
'first_usage' => $current_date,
|
||||
'total_count' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$stored_api_accesses_for_user_agent = $stored_api_accesses['user_agents'][ $user_agent ] ?? null;
|
||||
if ( is_null( $stored_api_accesses_for_user_agent ) ) {
|
||||
$stored_api_accesses['user_agents'][ $user_agent ] = array(
|
||||
'first_date' => $current_date,
|
||||
'last_date' => $current_date,
|
||||
'count' => 1,
|
||||
);
|
||||
} else {
|
||||
$stored_api_accesses['user_agents'][ $user_agent ]['count']++;
|
||||
$stored_api_accesses['user_agents'][ $user_agent ]['last_date'] = $current_date;
|
||||
}
|
||||
$stored_api_accesses['total_count']++;
|
||||
|
||||
set_transient( 'wc_legacy_rest_api_usages', $stored_api_accesses, time() + 2 * WEEK_IN_SECONDS );
|
||||
|
||||
/**
|
||||
* This filter allows disabling the logging of Legacy REST API usages.
|
||||
*
|
||||
* @param bool $do_logging True to enable the logging of all the Legacy REST API usages (default), false to disable.
|
||||
*
|
||||
* @since 8.5.0
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_log_legacy_rest_api_usages', true ) ) {
|
||||
wc_get_logger()->info( 'Version: ' . WC_API_REQUEST_VERSION . ", Route: $route, User agent: $user_agent", array( 'source' => 'legacy_rest_api_usages' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new endpoints.
|
||||
*
|
||||
@@ -90,30 +146,71 @@ class WC_Legacy_API {
|
||||
$wp->query_vars['wc-api-route'] = $_GET['wc-api-route'];
|
||||
}
|
||||
|
||||
if ( empty( $wp->query_vars['wc-api-version'] ) || empty( $wp->query_vars['wc-api-route'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// REST API request.
|
||||
if ( ! empty( $wp->query_vars['wc-api-version'] ) && ! empty( $wp->query_vars['wc-api-route'] ) ) {
|
||||
|
||||
wc_maybe_define_constant( 'WC_API_REQUEST', true );
|
||||
wc_maybe_define_constant( 'WC_API_REQUEST_VERSION', absint( $wp->query_vars['wc-api-version'] ) );
|
||||
wc_maybe_define_constant( 'WC_API_REQUEST', true );
|
||||
wc_maybe_define_constant( 'WC_API_REQUEST_VERSION', absint( $wp->query_vars['wc-api-version'] ) );
|
||||
|
||||
// Legacy v1 API request.
|
||||
if ( 1 === WC_API_REQUEST_VERSION ) {
|
||||
$this->handle_v1_rest_api_request();
|
||||
} elseif ( 2 === WC_API_REQUEST_VERSION ) {
|
||||
$this->handle_v2_rest_api_request();
|
||||
} else {
|
||||
$this->includes();
|
||||
$route = $wp->query_vars['wc-api-route'];
|
||||
$this->maybe_log_rest_api_request( $route, $_SERVER['HTTP_USER_AGENT'] ?? null );
|
||||
|
||||
$this->server = new WC_API_Server( $wp->query_vars['wc-api-route'] );
|
||||
// Legacy v1 API request.
|
||||
if ( 1 === WC_API_REQUEST_VERSION ) {
|
||||
$this->handle_v1_rest_api_request();
|
||||
} elseif ( 2 === WC_API_REQUEST_VERSION ) {
|
||||
$this->handle_v2_rest_api_request();
|
||||
} else {
|
||||
$this->includes();
|
||||
|
||||
// load API resource classes.
|
||||
$this->register_resources( $this->server );
|
||||
$this->server = new WC_API_Server( $route );
|
||||
|
||||
// Fire off the request.
|
||||
$this->server->serve_request();
|
||||
// load API resource classes.
|
||||
$this->register_resources( $this->server );
|
||||
|
||||
// Fire off the request.
|
||||
$this->server->serve_request();
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an admin notice with information about the last Legacy REST API usage,
|
||||
* if the corresponding transient is available and unless the Legacy REST API
|
||||
* extension is installed or the user has dismissed the notice.
|
||||
*/
|
||||
private function maybe_display_legacy_wc_api_usage_notice(): void {
|
||||
$legacy_api_usages = get_transient( 'wc_legacy_rest_api_usages' );
|
||||
if ( false === $legacy_api_usages || is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' ) || 'yes' !== get_option( 'woocommerce_api_enabled' ) ) {
|
||||
if ( WC_Admin_Notices::has_notice( 'legacy_api_usages_detected' ) ) {
|
||||
WC_Admin_Notices::remove_notice( 'legacy_api_usages_detected' );
|
||||
}
|
||||
} elseif ( ! WC_Admin_Notices::user_has_dismissed_notice( 'legacy_api_usages_detected' ) ) {
|
||||
unset( $legacy_api_usages['user_agents']['unknown'] );
|
||||
$user_agents = array_keys( $legacy_api_usages['user_agents'] );
|
||||
|
||||
exit;
|
||||
WC_Admin_Notices::add_custom_notice(
|
||||
'legacy_api_usages_detected',
|
||||
sprintf(
|
||||
'%s%s',
|
||||
sprintf(
|
||||
'<h4>%s</h4>',
|
||||
esc_html__( 'WooCommerce Legacy REST API access detected', 'woocommerce' )
|
||||
),
|
||||
sprintf(
|
||||
// translators: %1$d = count of Legacy REST API usages recorded, %2$s = date and time of first access, %3$d = count of known user agents registered, %4$s = URL.
|
||||
wpautop( wp_kses_data( __( '<p>The WooCommerce Legacy REST API has been accessed <b>%1$d</b> time(s) since <b>%2$s</b>. There are <b>%3$d</b> known user agent(s) registered. There are more details in <b><a target="_blank" href="%4$s">the WooCommerce log files</a></b> (file names start with <code>legacy_rest_api_usages</code>).', 'woocommerce' ) ) ),
|
||||
$legacy_api_usages['total_count'],
|
||||
$legacy_api_usages['first_usage'],
|
||||
count( $user_agents ),
|
||||
admin_url( 'admin.php?page=wc-status&tab=logs' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,23 +223,23 @@ class WC_Legacy_API {
|
||||
public function includes() {
|
||||
|
||||
// API server / response handlers.
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-exception.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-server.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/interface-wc-api-handler.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-json-handler.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-exception.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-server.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/interface-wc-api-handler.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-json-handler.php';
|
||||
|
||||
// Authentication.
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-authentication.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-authentication.php';
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-resource.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-coupons.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-customers.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-orders.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-products.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-reports.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-taxes.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v3/class-wc-api-webhooks.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-resource.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-coupons.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-customers.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-orders.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-products.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-reports.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-taxes.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v3/class-wc-api-webhooks.php';
|
||||
|
||||
// Allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
@@ -157,7 +254,8 @@ class WC_Legacy_API {
|
||||
*/
|
||||
public function register_resources( $server ) {
|
||||
|
||||
$api_classes = apply_filters( 'woocommerce_api_classes',
|
||||
$api_classes = apply_filters(
|
||||
'woocommerce_api_classes',
|
||||
array(
|
||||
'WC_API_Coupons',
|
||||
'WC_API_Customers',
|
||||
@@ -184,20 +282,20 @@ class WC_Legacy_API {
|
||||
private function handle_v1_rest_api_request() {
|
||||
|
||||
// Include legacy required files for v1 REST API request.
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-server.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/interface-wc-api-handler.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-json-handler.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-xml-handler.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-server.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/interface-wc-api-handler.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-json-handler.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-xml-handler.php';
|
||||
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-authentication.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-authentication.php';
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-resource.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-coupons.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-customers.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-orders.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-products.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v1/class-wc-api-reports.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-resource.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-coupons.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-customers.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-orders.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-products.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v1/class-wc-api-reports.php';
|
||||
|
||||
// Allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
@@ -205,7 +303,8 @@ class WC_Legacy_API {
|
||||
$this->server = new WC_API_Server( $GLOBALS['wp']->query_vars['wc-api-route'] );
|
||||
|
||||
// Register available resources for legacy v1 REST API request.
|
||||
$api_classes = apply_filters( 'woocommerce_api_classes',
|
||||
$api_classes = apply_filters(
|
||||
'woocommerce_api_classes',
|
||||
array(
|
||||
'WC_API_Customers',
|
||||
'WC_API_Orders',
|
||||
@@ -230,21 +329,21 @@ class WC_Legacy_API {
|
||||
* @deprecated 2.6.0
|
||||
*/
|
||||
private function handle_v2_rest_api_request() {
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-exception.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-server.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/interface-wc-api-handler.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-json-handler.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-exception.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-server.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/interface-wc-api-handler.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-json-handler.php';
|
||||
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-authentication.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-authentication.php';
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-resource.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-coupons.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-customers.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-orders.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-products.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-reports.php' );
|
||||
include_once( dirname( __FILE__ ) . '/api/v2/class-wc-api-webhooks.php' );
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-resource.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-coupons.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-customers.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-orders.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-products.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-reports.php';
|
||||
include_once dirname( __FILE__ ) . '/api/v2/class-wc-api-webhooks.php';
|
||||
|
||||
// allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
@@ -252,7 +351,8 @@ class WC_Legacy_API {
|
||||
$this->server = new WC_API_Server( $GLOBALS['wp']->query_vars['wc-api-route'] );
|
||||
|
||||
// Register available resources for legacy v2 REST API request.
|
||||
$api_classes = apply_filters( 'woocommerce_api_classes',
|
||||
$api_classes = apply_filters(
|
||||
'woocommerce_api_classes',
|
||||
array(
|
||||
'WC_API_Customers',
|
||||
'WC_API_Orders',
|
||||
|
||||
@@ -77,12 +77,13 @@ class WC_Log_Handler_DB extends WC_Log_Handler {
|
||||
'%s', // possible serialized context.
|
||||
);
|
||||
|
||||
unset( $context['source'] );
|
||||
if ( ! empty( $context ) ) {
|
||||
try {
|
||||
$insert['context'] = serialize( $context ); // @codingStandardsIgnoreLine.
|
||||
} catch ( Exception $e ) {
|
||||
$insert['context'] = serialize( 'There was an error while serializing the context: ' . $e->getMessage() );
|
||||
if ( isset( $context['backtrace'] ) && true === filter_var( $context['backtrace'], FILTER_VALIDATE_BOOLEAN ) ) {
|
||||
$context['backtrace'] = self::get_backtrace();
|
||||
}
|
||||
|
||||
$insert['context'] = wp_json_encode( $context, JSON_PRETTY_PRINT );
|
||||
}
|
||||
|
||||
return false !== $wpdb->insert( "{$wpdb->prefix}woocommerce_log", $insert, $format );
|
||||
|
||||
@@ -25,7 +25,9 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'onboarding-tasks' => true,
|
||||
'product-variation-management' => true,
|
||||
'product-virtual-downloadable' => true,
|
||||
'product-external-affiliate' => false,
|
||||
'product-external-affiliate' => true,
|
||||
'product-grouped' => true,
|
||||
'product-linked' => false,
|
||||
'remote-inbox-notifications' => true,
|
||||
'remote-free-extensions' => true,
|
||||
'payment-gateway-suggestions' => true,
|
||||
|
||||
@@ -354,7 +354,7 @@ abstract class WC_REST_CRUD_Controller extends WC_REST_Posts_Controller {
|
||||
$result = $query->query( $query_args );
|
||||
|
||||
$total_posts = $query->found_posts;
|
||||
if ( $total_posts < 1 ) {
|
||||
if ( $total_posts < 1 && isset( $query_args['paged'] ) && absint( $query_args['paged'] ) > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $query_args['paged'] );
|
||||
$count_query = new WP_Query();
|
||||
|
||||
@@ -367,7 +367,7 @@ abstract class WC_REST_Posts_Controller extends WC_REST_Controller {
|
||||
$page = (int) $query_args['paged'];
|
||||
$total_posts = $posts_query->found_posts;
|
||||
|
||||
if ( $total_posts < 1 ) {
|
||||
if ( $total_posts < 1 && $page > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $query_args['paged'] );
|
||||
$count_query = new WP_Query();
|
||||
|
||||
@@ -333,6 +333,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
||||
|
||||
if ( 0 === $index ) {
|
||||
$product->set_image_id( $attachment_id );
|
||||
wc_product_attach_featured_image( $attachment_id, $product );
|
||||
} else {
|
||||
$gallery[] = $attachment_id;
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
|
||||
public function sanitize_cost( $value ) {
|
||||
$value = is_null( $value ) ? '' : $value;
|
||||
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $value );
|
||||
// Thrown an error on the front end if the evaluate_cost will fail.
|
||||
$dummy_cost = $this->evaluate_cost(
|
||||
$value,
|
||||
|
||||
@@ -94,9 +94,12 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
|
||||
public function sanitize_cost( $value ) {
|
||||
$value = is_null( $value ) ? '' : $value;
|
||||
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $value );
|
||||
|
||||
if ( ! is_numeric( $value ) ) {
|
||||
$test_value = str_replace( wc_get_price_decimal_separator(), '.', $value );
|
||||
$test_value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $test_value );
|
||||
|
||||
if ( $test_value && ! is_numeric( $test_value ) ) {
|
||||
throw new Exception( __( 'Please enter a valid number', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,12 @@ class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
|
||||
public function sanitize_cost( $value ) {
|
||||
$value = is_null( $value ) ? '' : $value;
|
||||
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
|
||||
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $value );
|
||||
|
||||
if ( $value && ! is_numeric( $value ) ) {
|
||||
$test_value = str_replace( wc_get_price_decimal_separator(), '.', $value );
|
||||
$test_value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $test_value );
|
||||
|
||||
if ( $test_value && ! is_numeric( $test_value ) ) {
|
||||
throw new Exception( __( 'Please enter a valid number', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,14 @@ class WC_Shortcode_Cart {
|
||||
$address['postcode'] = isset( $_POST['calc_shipping_postcode'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_postcode'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
$address['city'] = isset( $_POST['calc_shipping_city'] ) ? wc_clean( wp_unslash( $_POST['calc_shipping_city'] ) ) : ''; // WPCS: input var ok, CSRF ok, sanitization ok.
|
||||
|
||||
if ( $address['postcode'] ) {
|
||||
$address['postcode'] = wc_format_postcode( $address['postcode'], $address['country'] );
|
||||
}
|
||||
|
||||
$address = apply_filters( 'woocommerce_cart_calculate_shipping_address', $address );
|
||||
|
||||
if ( $address['postcode'] && ! WC_Validation::is_postcode( $address['postcode'], $address['country'] ) ) {
|
||||
throw new Exception( __( 'Please enter a valid postcode / ZIP.', 'woocommerce' ) );
|
||||
} elseif ( $address['postcode'] ) {
|
||||
$address['postcode'] = wc_format_postcode( $address['postcode'], $address['country'] );
|
||||
}
|
||||
|
||||
if ( $address['country'] ) {
|
||||
|
||||
@@ -2003,9 +2003,7 @@ function wc_remove_number_precision_deep( $value ) {
|
||||
* - an instance which will be used directly as the logger
|
||||
* In either case, the class or instance *must* implement WC_Logger_Interface.
|
||||
*
|
||||
* @see WC_Logger_Interface
|
||||
*
|
||||
* @return WC_Logger
|
||||
* @return WC_Logger_Interface
|
||||
*/
|
||||
function wc_get_logger() {
|
||||
static $logger = null;
|
||||
|
||||
@@ -12,6 +12,7 @@ use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||
use Automattic\WooCommerce\Utilities\NumberUtil;
|
||||
use Automattic\WooCommerce\Internal\ProductImage\MatchImageBySKU;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -448,6 +449,7 @@ function wc_scheduled_sales() {
|
||||
|
||||
if ( $sale_price ) {
|
||||
$product->set_price( $sale_price );
|
||||
$product->set_date_on_sale_from( '' );
|
||||
} else {
|
||||
$product->set_date_on_sale_to( '' );
|
||||
$product->set_date_on_sale_from( '' );
|
||||
@@ -1656,3 +1658,45 @@ function wc_update_product_lookup_tables_rating_count_batch( $offset = 0, $limit
|
||||
}
|
||||
}
|
||||
add_action( 'wc_update_product_lookup_tables_rating_count_batch', 'wc_update_product_lookup_tables_rating_count_batch', 10, 2 );
|
||||
|
||||
/**
|
||||
* Attach product featured image. Use image filename to match a product sku when product is not provided.
|
||||
*
|
||||
* @since 8.5.0
|
||||
* @param int $attachment_id Media attachment ID.
|
||||
* @param WC_Product $product Optional product object.
|
||||
* @return void
|
||||
*/
|
||||
function wc_product_attach_featured_image( $attachment_id, $product = null ) {
|
||||
$attachment_post = get_post( $attachment_id );
|
||||
if ( ! $attachment_post ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( null === $product && wc_get_container()->get( MatchImageBySKU::class )->is_enabled() ) {
|
||||
// On upload the attachment post title is the uploaded file's filename.
|
||||
$file_name = pathinfo( $attachment_post->post_title, PATHINFO_FILENAME );
|
||||
if ( ! $file_name ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product_id = wc_get_product_id_by_sku( $file_name );
|
||||
$product = wc_get_product( $product_id );
|
||||
}
|
||||
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product->set_image_id( $attachment_id );
|
||||
$product->save();
|
||||
if ( 0 === $attachment_post->post_parent ) {
|
||||
wp_update_post(
|
||||
array(
|
||||
'ID' => $attachment_id,
|
||||
'post_parent' => $product->get_id(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'add_attachment', 'wc_product_attach_featured_image' );
|
||||
|
||||
@@ -363,8 +363,9 @@ function wc_body_class( $classes ) {
|
||||
* @since 3.4.0
|
||||
*/
|
||||
function wc_no_js() {
|
||||
$type_attr = current_theme_supports( 'html5', 'script' ) ? '' : " type='text/javascript'";
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
<script<?php echo $type_attr; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
(function () {
|
||||
var c = document.body.className;
|
||||
c = c.replace(/woocommerce-no-js/, 'woocommerce-js');
|
||||
@@ -2723,10 +2724,30 @@ if ( ! function_exists( 'woocommerce_order_details_table' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wc_get_template(
|
||||
'order/order-details.php',
|
||||
array(
|
||||
'order_id' => $order_id,
|
||||
'order_id' => $order_id,
|
||||
/**
|
||||
* Determines if the order downloads table should be shown (in the context of the order details
|
||||
* template).
|
||||
*
|
||||
* By default, this is true if the order has at least one dowloadable items and download is permitted
|
||||
* (which is partly determined by the order status). For special cases, though, this can be overridden
|
||||
* and the downloads table can be forced to render (or forced not to render).
|
||||
*
|
||||
* @since 8.5.0
|
||||
*
|
||||
* @param bool $show_downloads If the downloads table should be shown.
|
||||
* @param WC_Order $order The related order.
|
||||
*/
|
||||
'show_downloads' => apply_filters( 'woocommerce_order_downloads_table_show_downloads', ( $order->has_downloadable_item() && $order->is_download_permitted() ), $order ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user