plugin updates
This commit is contained in:
@@ -220,6 +220,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'i18n_delete_product_notice' => __( 'This product has produced sales and may be linked to existing orders. Are you sure you want to delete it?', 'woocommerce' ),
|
||||
'i18n_remove_personal_data_notice' => __( 'This action cannot be reversed. Are you sure you wish to erase personal data from the selected orders?', 'woocommerce' ),
|
||||
'i18n_confirm_delete' => __( 'Are you sure you wish to delete this item?', 'woocommerce' ),
|
||||
'i18n_global_unique_id_error' => __( 'Please enter only numbers and hyphens (-).', 'woocommerce' ),
|
||||
'decimal_point' => $decimal,
|
||||
'mon_decimal_point' => wc_get_price_decimal_separator(),
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
@@ -276,7 +277,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
if ( in_array( $screen_id, array( 'product', 'edit-product' ) ) ) {
|
||||
wp_enqueue_media();
|
||||
wp_register_script( 'wc-admin-product-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-product' . $suffix . '.js', array( 'wc-admin-meta-boxes', 'media-models' ), $version );
|
||||
wp_register_script( 'wc-admin-variation-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-product-variation' . $suffix . '.js', array( 'wc-admin-meta-boxes', 'serializejson', 'media-models', 'backbone', 'jquery-ui-sortable', 'wc-backbone-modal' ), $version );
|
||||
wp_register_script( 'wc-admin-variation-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-product-variation' . $suffix . '.js', array( 'wc-admin-meta-boxes', 'serializejson', 'media-models', 'backbone', 'jquery-ui-sortable', 'wc-backbone-modal', 'wp-data', 'wp-notices' ), $version );
|
||||
|
||||
wp_enqueue_script( 'wc-admin-product-meta-boxes' );
|
||||
wp_enqueue_script( 'wc-admin-variation-meta-boxes' );
|
||||
|
||||
@@ -156,6 +156,9 @@ class WC_Admin_Duplicate_Product {
|
||||
if ( '' !== $product->get_sku( 'edit' ) ) {
|
||||
$duplicate->set_sku( wc_product_generate_unique_sku( 0, $product->get_sku( 'edit' ) ) );
|
||||
}
|
||||
if ( '' !== $product->get_global_unique_id( 'edit' ) ) {
|
||||
$duplicate->set_global_unique_id( '' );
|
||||
}
|
||||
$duplicate->set_status( 'draft' );
|
||||
$duplicate->set_date_created( null );
|
||||
$duplicate->set_slug( '' );
|
||||
@@ -195,6 +198,9 @@ class WC_Admin_Duplicate_Product {
|
||||
if ( '' !== $child->get_sku( 'edit' ) ) {
|
||||
$child_duplicate->set_sku( wc_product_generate_unique_sku( 0, $child->get_sku( 'edit' ) ) );
|
||||
}
|
||||
if ( '' !== $child->get_global_unique_id( 'edit' ) ) {
|
||||
$child_duplicate->set_global_unique_id( '' );
|
||||
}
|
||||
|
||||
foreach ( $meta_to_exclude as $meta_key ) {
|
||||
$child_duplicate->delete_meta_data( $meta_key );
|
||||
|
||||
@@ -164,7 +164,7 @@ class WC_Admin_Notices {
|
||||
* and the Legacy REST API plugin is not installed.
|
||||
*/
|
||||
private static function maybe_add_legacy_api_removal_notice() {
|
||||
if ( wc_get_container()->get( WebhookUtil::class )->get_legacy_webhooks_count() > 0 && is_null( WC()->api ) ) {
|
||||
if ( wc_get_container()->get( WebhookUtil::class )->get_legacy_webhooks_count() > 0 && ! WC()->legacy_rest_api_is_available() ) {
|
||||
self::add_custom_notice(
|
||||
'legacy_webhooks_unsupported_in_woo_90',
|
||||
sprintf(
|
||||
@@ -189,7 +189,7 @@ class WC_Admin_Notices {
|
||||
* Remove the admin notice about the unsupported webhooks if the Legacy REST API plugin is installed.
|
||||
*/
|
||||
private static function maybe_remove_legacy_api_removal_notice() {
|
||||
if ( self::has_notice( 'legacy_webhooks_unsupported_in_woo_90' ) && ( ! is_null( WC()->api ) || 0 === wc_get_container()->get( WebhookUtil::class )->get_legacy_webhooks_count() ) ) {
|
||||
if ( self::has_notice( 'legacy_webhooks_unsupported_in_woo_90' ) && ( WC()->legacy_rest_api_is_available() || 0 === wc_get_container()->get( WebhookUtil::class )->get_legacy_webhooks_count() ) ) {
|
||||
self::remove_notice( 'legacy_webhooks_unsupported_in_woo_90' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ class WC_Admin_Post_Types {
|
||||
add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
|
||||
add_filter( 'woocommerce_order_updated_messages', array( $this, 'order_updated_messages' ) );
|
||||
add_filter( 'bulk_post_updated_messages', array( $this, 'bulk_post_updated_messages' ), 10, 2 );
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function () {
|
||||
$this->maybe_display_warning_for_password_protected_coupon();
|
||||
}
|
||||
);
|
||||
|
||||
// Disable Auto Save.
|
||||
add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) );
|
||||
@@ -256,6 +262,33 @@ class WC_Admin_Post_Types {
|
||||
return $bulk_messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a warning when editing a password-protected coupon.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
private function maybe_display_warning_for_password_protected_coupon() {
|
||||
if ( ! function_exists( 'get_current_screen' ) || 'shop_coupon' !== get_current_screen()->id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $GLOBALS['post'] ) || 'shop_coupon' !== $GLOBALS['post']->post_type ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_admin_notice(
|
||||
__(
|
||||
'This coupon is password protected. WooCommerce does not support password protection for coupons. You can temporarily hide a coupon by making it private. Alternatively, usage limits and restrictions can be configured below.',
|
||||
'woocommerce'
|
||||
),
|
||||
array(
|
||||
'type' => 'warning',
|
||||
'id' => 'wc-password-protected-coupon-warning',
|
||||
'additional_classes' => empty( $GLOBALS['post']->post_password ) ? array( 'hidden' ) : array(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom bulk edit - form.
|
||||
*
|
||||
|
||||
@@ -47,7 +47,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
if ( empty( self::$settings ) ) {
|
||||
$settings = array();
|
||||
|
||||
include_once dirname( __FILE__ ) . '/settings/class-wc-settings-page.php';
|
||||
include_once __DIR__ . '/settings/class-wc-settings-page.php';
|
||||
|
||||
$settings[] = include __DIR__ . '/settings/class-wc-settings-general.php';
|
||||
$settings[] = include __DIR__ . '/settings/class-wc-settings-products.php';
|
||||
@@ -153,7 +153,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
// Get tabs for the settings page.
|
||||
$tabs = apply_filters( 'woocommerce_settings_tabs_array', array() );
|
||||
|
||||
include dirname( __FILE__ ) . '/views/html-admin-settings.php';
|
||||
include __DIR__ . '/views/html-admin-settings.php';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,11 +514,19 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
$visibility_class[] = $value['row_class'];
|
||||
}
|
||||
|
||||
$must_disable = $value['disabled'] ?? false;
|
||||
|
||||
if ( $must_disable ) {
|
||||
$visibility_class[] = 'disabled';
|
||||
}
|
||||
|
||||
$container_class = implode( ' ', $visibility_class );
|
||||
$must_disable = $value['disabled'] ?? false;
|
||||
$has_title = isset( $value['title'] ) && '' !== $value['title'];
|
||||
$has_legend = isset( $value['legend'] ) && '' !== $value['legend'];
|
||||
|
||||
if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
|
||||
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
||||
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
||||
|
||||
$tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
|
||||
?>
|
||||
<tr class="<?php echo esc_attr( $container_class ); ?>">
|
||||
@@ -535,9 +543,9 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( ! empty( $value['title'] ) ) {
|
||||
if ( $has_title || $has_legend ) {
|
||||
?>
|
||||
<legend class="screen-reader-text"><span><?php echo esc_html( $value['title'] ); ?></span></legend>
|
||||
<legend class="<?php echo $has_legend ? '' : 'screen-reader-text'; ?>"><span><?php echo esc_html( $has_legend ? $value['legend'] : $value['title'] ); ?></span></legend>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -550,7 +558,6 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
type="checkbox"
|
||||
class="<?php echo esc_attr( isset( $value['class'] ) ? $value['class'] : '' ); ?>"
|
||||
value="1"
|
||||
<?php disabled( $value['disabled'] ?? false ); ?>
|
||||
<?php checked( $option_value, 'yes' ); ?>
|
||||
<?php echo implode( ' ', $custom_attributes ); // WPCS: XSS ok. ?>
|
||||
/> <?php echo $description; // WPCS: XSS ok. ?>
|
||||
|
||||
@@ -76,7 +76,7 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table {
|
||||
|
||||
// Title.
|
||||
$warning_prefix =
|
||||
is_null( wc()->api ) && $this->uses_legacy_rest_api( $webhook ) ?
|
||||
$this->uses_legacy_rest_api( $webhook ) && ! WC()->legacy_rest_api_is_available() ?
|
||||
sprintf(
|
||||
"<span title='%s'>⚠️</span>️ ",
|
||||
esc_html__( 'This webhook is configured to be delivered using the Legacy REST API, but the Legacy REST API plugin is not installed on this site.', 'woocommerce' )
|
||||
|
||||
@@ -332,7 +332,7 @@ class WC_Admin_Webhooks {
|
||||
private static function maybe_display_legacy_rest_api_warning() {
|
||||
global $webhooks_table_list;
|
||||
|
||||
if ( ! is_null( wc()->api ) ) {
|
||||
if ( WC()->legacy_rest_api_is_available() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,14 @@ class WC_Helper_Admin {
|
||||
$connect_url_args['wc-helper-nonce'] = wp_create_nonce( 'connect' );
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['utm_source'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$connect_url_args['utm_source'] = wc_clean( wp_unslash( $_GET['utm_source'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['utm_campaign'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$connect_url_args['utm_campaign'] = wc_clean( wp_unslash( $_GET['utm_campaign'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
|
||||
return add_query_arg(
|
||||
$connect_url_args,
|
||||
admin_url( 'admin.php' )
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* @package WooCommerce\Admin\Helper
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
@@ -26,7 +28,7 @@ class WC_Helper_Updater {
|
||||
add_action( 'pre_set_site_transient_update_themes', array( __CLASS__, 'transient_update_themes' ), 21, 1 );
|
||||
add_action( 'upgrader_process_complete', array( __CLASS__, 'upgrader_process_complete' ) );
|
||||
add_action( 'upgrader_pre_download', array( __CLASS__, 'block_expired_updates' ), 10, 2 );
|
||||
add_action( 'plugins_loaded', array( __CLASS__, 'add_hook_for_modifying_update_notices' ) );
|
||||
add_action( 'admin_init', array( __CLASS__, 'add_hook_for_modifying_update_notices' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,9 +188,11 @@ class WC_Helper_Updater {
|
||||
public static function add_connect_woocom_plugin_message() {
|
||||
$connect_page_url = add_query_arg(
|
||||
array(
|
||||
'page' => 'wc-admin',
|
||||
'tab' => 'my-subscriptions',
|
||||
'path' => rawurlencode( '/extensions' ),
|
||||
'page' => 'wc-admin',
|
||||
'tab' => 'my-subscriptions',
|
||||
'path' => rawurlencode( '/extensions' ),
|
||||
'utm_source' => 'pu',
|
||||
'utm_campaign' => 'pu_plugin_screen_connect',
|
||||
),
|
||||
admin_url( 'admin.php' )
|
||||
);
|
||||
@@ -252,41 +256,68 @@ class WC_Helper_Updater {
|
||||
* @return void.
|
||||
*/
|
||||
public static function display_notice_for_expired_and_expiring_subscriptions( $plugin_data, $response ) {
|
||||
|
||||
// Extract product ID from the response.
|
||||
$product_id = preg_replace( '/[^0-9]/', '', $response->id );
|
||||
|
||||
// Get the subscription details based on product ID.
|
||||
$subscription = current(
|
||||
wp_list_filter(
|
||||
WC_Helper::get_subscriptions(),
|
||||
array( 'product_id' => $product_id )
|
||||
)
|
||||
);
|
||||
|
||||
// Check if subscription is empty.
|
||||
if ( empty( $subscription ) ) {
|
||||
// Product subscriptions.
|
||||
$subscriptions = wp_list_filter( WC_Helper::get_installed_subscriptions(), array( 'product_id' => $product_id ) );
|
||||
if ( empty( $subscriptions ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expired_subscription = current(
|
||||
array_filter(
|
||||
$subscriptions,
|
||||
function ( $subscription ) {
|
||||
return ! empty( $subscription['expired'] ) && ! $subscription['lifetime'];
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$expiring_subscription = current(
|
||||
array_filter(
|
||||
$subscriptions,
|
||||
function ( $subscription ) {
|
||||
return ! empty( $subscription['expiring'] ) && ! $subscription['autorenew'];
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Prepare the expiry notice based on subscription status.
|
||||
$expiry_notice = '';
|
||||
if ( ! empty( $subscription['expired'] ) && ! $subscription['lifetime'] ) {
|
||||
if ( ! empty( $expired_subscription ) ) {
|
||||
|
||||
$renew_link = add_query_arg(
|
||||
array(
|
||||
'utm_source' => 'pu',
|
||||
'utm_campaign' => 'pu_plugin_screen_renew',
|
||||
),
|
||||
PluginsHelper::WOO_SUBSCRIPTION_PAGE_URL
|
||||
);
|
||||
|
||||
/* translators: 1: Product regular price */
|
||||
$product_price = ! empty( $subscription['product_regular_price'] ) ? sprintf( __( 'for %s ', 'woocommerce' ), esc_html( $subscription['product_regular_price'] ) ) : '';
|
||||
$product_price = ! empty( $expired_subscription['product_regular_price'] ) ? sprintf( __( 'for %s ', 'woocommerce' ), esc_html( $expired_subscription['product_regular_price'] ) ) : '';
|
||||
|
||||
$expiry_notice = sprintf(
|
||||
/* translators: 1: URL to My Subscriptions page 2: Product price */
|
||||
__( ' Your subscription expired, <a href="%1$s" class="woocommerce-renew-subscription">renew %2$s</a>to update.', 'woocommerce' ),
|
||||
esc_url( 'https://woocommerce.com/my-account/my-subscriptions/' ),
|
||||
esc_url( $renew_link ),
|
||||
$product_price
|
||||
);
|
||||
} elseif ( ! empty( $subscription['expiring'] ) && ! $subscription['autorenew'] ) {
|
||||
} elseif ( ! empty( $expiring_subscription ) ) {
|
||||
$renew_link = add_query_arg(
|
||||
array(
|
||||
'utm_source' => 'pu',
|
||||
'utm_campaign' => 'pu_plugin_screen_enable_autorenew',
|
||||
),
|
||||
PluginsHelper::WOO_SUBSCRIPTION_PAGE_URL
|
||||
);
|
||||
|
||||
$expiry_notice = sprintf(
|
||||
/* translators: 1: Expiry date 1: URL to My Subscriptions page */
|
||||
__( ' Your subscription expires on %1$s, <a href="%2$s" class="woocommerce-enable-autorenew">enable auto-renew</a> to continue receiving updates.', 'woocommerce' ),
|
||||
date_i18n( 'F jS', $subscription['expires'] ),
|
||||
esc_url( 'https://woocommerce.com/my-account/my-subscriptions/' )
|
||||
date_i18n( 'F jS', $expiring_subscription['expires'] ),
|
||||
esc_url( $renew_link )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ class WC_Helper {
|
||||
include_once __DIR__ . '/class-wc-helper-admin.php';
|
||||
include_once __DIR__ . '/class-wc-helper-subscriptions-api.php';
|
||||
include_once __DIR__ . '/class-wc-helper-orders-api.php';
|
||||
include_once __DIR__ . '/class-wc-product-usage-notice.php';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -785,6 +786,14 @@ class WC_Helper {
|
||||
$redirect_url_args['install'] = sanitize_text_field( wp_unslash( $_GET['install'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['utm_source'] ) ) {
|
||||
$redirect_url_args['utm_source'] = wc_clean( wp_unslash( $_GET['utm_source'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['utm_campaign'] ) ) {
|
||||
$redirect_url_args['utm_campaign'] = wc_clean( wp_unslash( $_GET['utm_campaign'] ) );
|
||||
}
|
||||
|
||||
$redirect_uri = add_query_arg(
|
||||
$redirect_url_args,
|
||||
admin_url( 'admin.php' )
|
||||
@@ -1296,6 +1305,60 @@ class WC_Helper {
|
||||
return ! empty( $subscription );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's connected subscriptions that are installed on the current
|
||||
* site.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_installed_subscriptions() {
|
||||
static $installed_subscriptions = null;
|
||||
|
||||
// Cache installed_subscriptions in the current request.
|
||||
if ( is_null( $installed_subscriptions ) ) {
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
$site_id = isset( $auth['site_id'] ) ? absint( $auth['site_id'] ) : 0;
|
||||
if ( 0 === $site_id ) {
|
||||
$installed_subscriptions = array();
|
||||
return $installed_subscriptions;
|
||||
}
|
||||
|
||||
$installed_subscriptions = array_filter(
|
||||
self::get_subscriptions(),
|
||||
function ( $subscription ) use ( $site_id ) {
|
||||
return in_array( $site_id, $subscription['connections'], true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $installed_subscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscription state of a given product ID.
|
||||
*
|
||||
* @since TBD
|
||||
*
|
||||
* @param int $product_id The product id.
|
||||
*
|
||||
* @return array Array of state_name => (bool) state
|
||||
*/
|
||||
public static function get_product_subscription_state( $product_id ) {
|
||||
$product_subscriptions = wp_list_filter( self::get_installed_subscriptions(), array( 'product_id' => $product_id ) );
|
||||
|
||||
$subscription = ! empty( $product_subscriptions )
|
||||
? array_shift( $product_subscriptions )
|
||||
: array();
|
||||
|
||||
return array(
|
||||
'unregistered' => empty( $subscription ),
|
||||
'expired' => ( isset( $subscription['expired'] ) && $subscription['expired'] ),
|
||||
'expiring' => ( isset( $subscription['expiring'] ) && $subscription['expiring'] ),
|
||||
'key' => $subscription['product_key'] ?? '',
|
||||
'order_id' => $subscription['order_id'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscription entry from product_id. If multiple subscriptions are
|
||||
* found with the same product id and $single is set to true, will return the
|
||||
@@ -1482,6 +1545,41 @@ class WC_Helper {
|
||||
return $woo_themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules for displaying notice regarding marketplace product usage.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_product_usage_notice_rules() {
|
||||
$cache_key = '_woocommerce_helper_product_usage_notice_rules';
|
||||
$data = get_transient( $cache_key );
|
||||
if ( false !== $data ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$request = WC_Helper_API::get(
|
||||
'product-usage-notice-rules',
|
||||
array(
|
||||
'authenticated' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// Retry in 15 minutes for non-200 response.
|
||||
if ( wp_remote_retrieve_response_code( $request ) !== 200 ) {
|
||||
set_transient( $cache_key, array(), 15 * MINUTE_IN_SECONDS );
|
||||
return array();
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||
if ( empty( $data ) || ! is_array( $data ) ) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
set_transient( $cache_key, $data, 1 * HOUR_IN_SECONDS );
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the connected user's subscriptions.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Usage Notice.
|
||||
*
|
||||
* @package WooCommerce\Admin\Helper
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Product usage notice class.
|
||||
*/
|
||||
class WC_Product_Usage_Notice {
|
||||
/**
|
||||
* User meta key prefix to store dismiss counts per product. Product ID is
|
||||
* the suffix part.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DISMISSED_COUNT_META_PREFIX = '_woocommerce_product_usage_notice_dismissed_count_';
|
||||
|
||||
/**
|
||||
* User meta key prefix to store timestamp of last dismissed product usage notice.
|
||||
* Product ID is the suffix part.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DISMISSED_TIMESTAMP_META_PREFIX = '_woocommerce_product_usage_notice_dismissed_timestamp_';
|
||||
|
||||
/**
|
||||
* User meta key prefix to store timestamp of last clicked remind later from
|
||||
* product usage notice. Product ID is the suffix part.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REMIND_LATER_TIMESTAMP_META_PREFIX = '_woocommerce_product_usage_notice_remind_later_timestamp_';
|
||||
|
||||
/**
|
||||
* User meta key to store timestamp of last dismissed of any product usage
|
||||
* notices. There's no product ID in the meta key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const LAST_DISMISSED_TIMESTAMP_META = '_woocommerce_product_usage_notice_last_dismissed_timestamp';
|
||||
|
||||
/**
|
||||
* Array of product usage notice rules from helper API.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $product_usage_notice_rules = array();
|
||||
|
||||
/**
|
||||
* Current product usage notice rule applied to the current admin screen.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $current_notice_rule = array();
|
||||
|
||||
/**
|
||||
* Loads the class, runs on init.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function load() {
|
||||
add_action( 'current_screen', array( __CLASS__, 'maybe_show_product_usage_notice' ) );
|
||||
|
||||
add_action( 'wp_ajax_woocommerce_dismiss_product_usage_notice', array( __CLASS__, 'ajax_dismiss' ) );
|
||||
add_action( 'wp_ajax_woocommerce_remind_later_product_usage_notice', array( __CLASS__, 'ajax_remind_later' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe show product usage notice in a given screen object.
|
||||
*
|
||||
* @param \WP_Screen $screen Current \WP_Screen object.
|
||||
*/
|
||||
public static function maybe_show_product_usage_notice( $screen ) {
|
||||
$user_id = get_current_user_id();
|
||||
if ( ! $user_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! WC_Helper::is_site_connected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$product_usage_notice_rules = WC_Helper::get_product_usage_notice_rules();
|
||||
if ( empty( self::$product_usage_notice_rules ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$current_notice_rule = self::get_current_notice_rule( $screen );
|
||||
if ( empty( self::$current_notice_rule ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product_id = self::$current_notice_rule['id'];
|
||||
|
||||
if ( self::is_notice_throttled( $user_id, $product_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_product_usage_notice_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user clicked "remind later" recently.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_remind_later_clicked_recently( int $user_id, int $product_id ): bool {
|
||||
$last_remind_later_ts = absint(
|
||||
get_user_meta(
|
||||
$user_id,
|
||||
self::REMIND_LATER_TIMESTAMP_META_PREFIX . $product_id,
|
||||
true
|
||||
)
|
||||
);
|
||||
if ( 0 === $last_remind_later_ts ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$seconds_since_clicked_remind_later = time() - $last_remind_later_ts;
|
||||
|
||||
$wait_after_remind_later = self::$current_notice_rule['wait_in_seconds_after_remind_later'];
|
||||
|
||||
return $seconds_since_clicked_remind_later < $wait_after_remind_later;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has reached max dismissals of product usage notice.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_reached_max_dismissals( int $user_id, int $product_id ): bool {
|
||||
$dismiss_count = absint(
|
||||
get_user_meta(
|
||||
$user_id,
|
||||
self::DISMISSED_COUNT_META_PREFIX . $product_id,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
$max_dismissals = self::$current_notice_rule['max_dismissals'];
|
||||
|
||||
return $dismiss_count >= $max_dismissals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user dismissed any product usage notices recently.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_any_notices_dismissed_recently( int $user_id ): bool {
|
||||
$global_last_dismissed_ts = absint(
|
||||
get_user_meta(
|
||||
$user_id,
|
||||
self::LAST_DISMISSED_TIMESTAMP_META,
|
||||
true
|
||||
)
|
||||
);
|
||||
if ( 0 === $global_last_dismissed_ts ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$seconds_since_dismissed = time() - $global_last_dismissed_ts;
|
||||
|
||||
$wait_after_any_dismisses = self::$product_usage_notice_rules['wait_in_seconds_after_any_dismisses'];
|
||||
|
||||
return $seconds_since_dismissed < $wait_after_any_dismisses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user dismissed given product usage notice recently.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_product_notice_dismissed_recently( int $user_id, int $product_id ): bool {
|
||||
$last_dismissed_ts = absint(
|
||||
get_user_meta(
|
||||
$user_id,
|
||||
self::DISMISSED_TIMESTAMP_META_PREFIX . $product_id,
|
||||
true
|
||||
)
|
||||
);
|
||||
if ( 0 === $last_dismissed_ts ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$seconds_since_dismissed = time() - $last_dismissed_ts;
|
||||
|
||||
$wait_after_dismiss = self::$current_notice_rule['wait_in_seconds_after_dismiss'];
|
||||
|
||||
return $seconds_since_dismissed < $wait_after_dismiss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether current notice is throttled for the user and product.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_notice_throttled( int $user_id, int $product_id ): bool {
|
||||
return self::is_remind_later_clicked_recently( $user_id, $product_id ) ||
|
||||
self::has_reached_max_dismissals( $user_id, $product_id ) ||
|
||||
self::is_any_notices_dismissed_recently( $user_id ) ||
|
||||
self::is_product_notice_dismissed_recently( $user_id, $product_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts needed to display product usage notice (or modal).
|
||||
*/
|
||||
public static function enqueue_product_usage_notice_scripts() {
|
||||
WCAdminAssets::register_style( 'woo-product-usage-notice', 'style', array( 'wp-components' ) );
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'woo-product-usage-notice', true );
|
||||
|
||||
$subscribe_url = add_query_arg(
|
||||
array(
|
||||
'add-to-cart' => self::$current_notice_rule['id'],
|
||||
'utm_source' => 'pu',
|
||||
'utm_medium' => 'product',
|
||||
'utm_campaign' => 'pu_modal_subscribe',
|
||||
),
|
||||
'https://woocommerce.com/cart/'
|
||||
);
|
||||
|
||||
$renew_url = add_query_arg(
|
||||
array(
|
||||
'renew_product' => self::$current_notice_rule['id'],
|
||||
'product_key' => self::$current_notice_rule['state']['key'],
|
||||
'order_id' => self::$current_notice_rule['state']['order_id'],
|
||||
'utm_source' => 'pu',
|
||||
'utm_medium' => 'product',
|
||||
'utm_campaign' => 'pu_modal_renew',
|
||||
),
|
||||
'https://woocommerce.com/cart/'
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'wc-admin-woo-product-usage-notice',
|
||||
'wooProductUsageNotice',
|
||||
array(
|
||||
'subscribeUrl' => $subscribe_url,
|
||||
'renewUrl' => $renew_url,
|
||||
'dismissAction' => 'woocommerce_dismiss_product_usage_notice',
|
||||
'remindLaterAction' => 'woocommerce_remind_later_product_usage_notice',
|
||||
'productId' => self::$current_notice_rule['id'],
|
||||
'productName' => self::$current_notice_rule['name'],
|
||||
'productRegularPrice' => self::$current_notice_rule['regular_price'],
|
||||
'dismissNonce' => wp_create_nonce( 'dismiss_product_usage_notice' ),
|
||||
'remindLaterNonce' => wp_create_nonce( 'remind_later_product_usage_notice' ),
|
||||
'showAs' => self::$current_notice_rule['show_as'],
|
||||
'colorScheme' => self::$current_notice_rule['color_scheme'],
|
||||
'subscriptionState' => self::$current_notice_rule['state'],
|
||||
'screenId' => get_current_screen()->id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product usage notice rule from a given WP_Screen object.
|
||||
*
|
||||
* @param \WP_Screen $screen Current \WP_Screen object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_current_notice_rule( $screen ) {
|
||||
foreach ( self::$product_usage_notice_rules['products'] as $product_id => $rule ) {
|
||||
if ( ! isset( $rule['screens'][ $screen->id ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check query strings.
|
||||
if ( ! self::query_string_matches( $screen, $rule ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$product_id = absint( $product_id );
|
||||
$state = WC_Helper::get_product_subscription_state( $product_id );
|
||||
if ( $state['expired'] || $state['unregistered'] ) {
|
||||
$rule['id'] = $product_id;
|
||||
$rule['state'] = $state;
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the screen and GET parameter matches a given rule.
|
||||
*
|
||||
* @param \WP_Screen $screen Current \WP_Screen object.
|
||||
* @param array $rule Product usage notice rule.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function query_string_matches( $screen, $rule ) {
|
||||
if ( empty( $rule['screens'][ $screen->id ]['qs'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$qs = $rule['screens'][ $screen->id ]['qs'];
|
||||
foreach ( $qs as $key => $val ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
if ( empty( $_GET[ $key ] ) || $_GET[ $key ] !== $val ) {
|
||||
return false;
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for dismiss action of product usage notice.
|
||||
*/
|
||||
public static function ajax_dismiss() {
|
||||
if ( ! check_ajax_referer( 'dismiss_product_usage_notice' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
if ( ! $user_id ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$product_id = absint( $_GET['product_id'] ?? 0 );
|
||||
if ( ! $product_id ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$dismiss_count = absint( get_user_meta( $user_id, self::DISMISSED_COUNT_META_PREFIX . $product_id, true ) );
|
||||
update_user_meta( $user_id, self::DISMISSED_COUNT_META_PREFIX . $product_id, $dismiss_count + 1 );
|
||||
|
||||
update_user_meta( $user_id, self::DISMISSED_TIMESTAMP_META_PREFIX . $product_id, time() );
|
||||
update_user_meta( $user_id, self::LAST_DISMISSED_TIMESTAMP_META, time() );
|
||||
|
||||
wp_die( 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for "remind later" action of product usage notice.
|
||||
*/
|
||||
public static function ajax_remind_later() {
|
||||
if ( ! check_ajax_referer( 'remind_later_product_usage_notice' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
if ( ! $user_id ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$product_id = absint( $_GET['product_id'] ?? 0 );
|
||||
if ( ! $product_id ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
update_user_meta( $user_id, self::REMIND_LATER_TIMESTAMP_META_PREFIX . $product_id, time() );
|
||||
|
||||
wp_die( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
WC_Product_Usage_Notice::load();
|
||||
@@ -369,6 +369,7 @@ class WC_Meta_Box_Product_Data {
|
||||
$errors = $product->set_props(
|
||||
array(
|
||||
'sku' => isset( $_POST['_sku'] ) ? wc_clean( wp_unslash( $_POST['_sku'] ) ) : null,
|
||||
'global_unique_id' => isset( $_POST['_global_unique_id'] ) ? wc_clean( wp_unslash( $_POST['_global_unique_id'] ) ) : null,
|
||||
'purchase_note' => isset( $_POST['_purchase_note'] ) ? wp_kses_post( wp_unslash( $_POST['_purchase_note'] ) ) : '',
|
||||
'downloadable' => isset( $_POST['_downloadable'] ),
|
||||
'virtual' => isset( $_POST['_virtual'] ),
|
||||
@@ -544,6 +545,7 @@ class WC_Meta_Box_Product_Data {
|
||||
'image_id' => isset( $_POST['upload_image_id'][ $i ] ) ? wc_clean( wp_unslash( $_POST['upload_image_id'][ $i ] ) ) : null,
|
||||
'attributes' => self::prepare_set_attributes( $parent->get_attributes(), 'attribute_', $i ),
|
||||
'sku' => isset( $_POST['variable_sku'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_sku'][ $i ] ) ) : '',
|
||||
'global_unique_id' => isset( $_POST['variable_global_unique_id'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_global_unique_id'][ $i ] ) ) : '',
|
||||
'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_weight'][ $i ] ) ) : '',
|
||||
'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_length'][ $i ] ) ) : '',
|
||||
'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_width'][ $i ] ) ) : '',
|
||||
|
||||
@@ -78,7 +78,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<td class="wc-order-edit-line-item">
|
||||
<?php if ( $order->is_editable() ) : ?>
|
||||
<div class="wc-order-edit-line-item-actions">
|
||||
<a class="edit-order-item" href="#"></a><a class="delete-order-item" href="#"></a>
|
||||
<a class="edit-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Edit fee', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Edit fee', 'woocommerce' ); ?>"></a><a class="delete-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Delete fee', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Delete fee', 'woocommerce' ); ?>"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
@@ -181,7 +181,7 @@ $row_class = apply_filters( 'woocommerce_admin_html_order_item_class', ! empt
|
||||
<td class="wc-order-edit-line-item" width="1%">
|
||||
<div class="wc-order-edit-line-item-actions">
|
||||
<?php if ( $order->is_editable() ) : ?>
|
||||
<a class="edit-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Edit item', 'woocommerce' ); ?>"></a><a class="delete-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Delete item', 'woocommerce' ); ?>"></a>
|
||||
<a class="edit-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Edit item', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Edit item', 'woocommerce' ); ?>"></a><a class="delete-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Delete item', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Delete item', 'woocommerce' ); ?>"></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -109,7 +109,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<td class="wc-order-edit-line-item">
|
||||
<?php if ( $order->is_editable() ) : ?>
|
||||
<div class="wc-order-edit-line-item-actions">
|
||||
<a class="edit-order-item" href="#"></a><a class="delete-order-item" href="#"></a>
|
||||
<a class="edit-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Edit shipping', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Edit shipping', 'woocommerce' ); ?>"></a><a class="delete-order-item tips" href="#" data-tip="<?php esc_attr_e( 'Delete shipping', 'woocommerce' ); ?>" aria-label="<?php esc_attr_e( 'Delete shipping', 'woocommerce' ); ?>"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
@@ -28,6 +28,18 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
||||
do_action( 'woocommerce_product_options_sku' );
|
||||
|
||||
woocommerce_wp_text_input(
|
||||
array(
|
||||
'id' => '_global_unique_id',
|
||||
'value' => $product_object->get_global_unique_id( 'edit' ),
|
||||
'label' => __( 'GTIN, UPC, EAN or ISBN', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enter a barcode or any other identifier unique to this product. It can help you list this product on other channels or marketplaces.', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
|
||||
do_action( 'woocommerce_product_options_global_unique_id' );
|
||||
|
||||
?>
|
||||
<div class="inline notice woocommerce-message show_if_variable">
|
||||
<img class="info-icon" src="<?php echo esc_url( $info_img_url ); ?>" />
|
||||
|
||||
@@ -70,27 +70,43 @@ defined( 'ABSPATH' ) || exit;
|
||||
</h3>
|
||||
<div class="woocommerce_variable_attributes wc-metabox-content" style="display: none;">
|
||||
<div class="data">
|
||||
<p class="form-row form-row-first upload_image">
|
||||
<a href="#" class="upload_image_button tips <?php echo $variation_object->get_image_id( 'edit' ) ? 'remove' : ''; ?>" data-tip="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_attr__( 'Remove this image', 'woocommerce' ) : esc_attr__( 'Upload an image', 'woocommerce' ); ?>" rel="<?php echo esc_attr( $variation_id ); ?>">
|
||||
<img src="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_url( wp_get_attachment_thumb_url( $variation_object->get_image_id( 'edit' ) ) ) : esc_url( wc_placeholder_img_src() ); ?>" /><input type="hidden" name="upload_image_id[<?php echo esc_attr( $loop ); ?>]" class="upload_image_id" value="<?php echo esc_attr( $variation_object->get_image_id( 'edit' ) ); ?>" />
|
||||
</a>
|
||||
</p>
|
||||
<?php
|
||||
if ( wc_product_sku_enabled() ) {
|
||||
<div class="form-flex-box">
|
||||
<p class="form-row upload_image">
|
||||
<a href="#" class="upload_image_button tips <?php echo $variation_object->get_image_id( 'edit' ) ? 'remove' : ''; ?>" data-tip="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_attr__( 'Remove this image', 'woocommerce' ) : esc_attr__( 'Upload an image', 'woocommerce' ); ?>" rel="<?php echo esc_attr( $variation_id ); ?>">
|
||||
<img src="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_url( wp_get_attachment_thumb_url( $variation_object->get_image_id( 'edit' ) ) ) : esc_url( wc_placeholder_img_src() ); ?>" /><input type="hidden" name="upload_image_id[<?php echo esc_attr( $loop ); ?>]" class="upload_image_id" value="<?php echo esc_attr( $variation_object->get_image_id( 'edit' ) ); ?>" />
|
||||
</a>
|
||||
</p>
|
||||
<div class="form-row form-row-last">
|
||||
<?php
|
||||
if ( wc_product_sku_enabled() ) {
|
||||
woocommerce_wp_text_input(
|
||||
array(
|
||||
'id' => "variable_sku{$loop}",
|
||||
'name' => "variable_sku[{$loop}]",
|
||||
'value' => $variation_object->get_sku( 'edit' ),
|
||||
'placeholder' => $variation_object->get_sku(),
|
||||
'label' => '<abbr title="' . esc_attr__( 'Stock Keeping Unit', 'woocommerce' ) . '">' . esc_html__( 'SKU', 'woocommerce' ) . '</abbr>',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'SKU refers to a Stock-keeping unit, a unique identifier for each distinct product and service that can be purchased.', 'woocommerce' ),
|
||||
'wrapper_class' => 'form-row',
|
||||
)
|
||||
);
|
||||
}
|
||||
woocommerce_wp_text_input(
|
||||
array(
|
||||
'id' => "variable_sku{$loop}",
|
||||
'name' => "variable_sku[{$loop}]",
|
||||
'value' => $variation_object->get_sku( 'edit' ),
|
||||
'placeholder' => $variation_object->get_sku(),
|
||||
'label' => '<abbr title="' . esc_attr__( 'Stock Keeping Unit', 'woocommerce' ) . '">' . esc_html__( 'SKU', 'woocommerce' ) . '</abbr>',
|
||||
'id' => "variable_global_unique_id{$loop}",
|
||||
'name' => "variable_global_unique_id[{$loop}]",
|
||||
'value' => $variation_object->get_global_unique_id( 'edit' ),
|
||||
'placeholder' => $variation_object->get_global_unique_id(),
|
||||
'label' => __( 'GTIN, UPC, EAN or ISBN', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'SKU refers to a Stock-keeping unit, a unique identifier for each distinct product and service that can be purchased.', 'woocommerce' ),
|
||||
'wrapper_class' => 'form-row form-row-last',
|
||||
'description' => __( 'Enter a barcode or any other identifier unique to this product. It can help you list this product on other channels or marketplaces.', 'woocommerce' ),
|
||||
'wrapper_class' => 'form-row',
|
||||
)
|
||||
);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<p class="form-row form-row-full options">
|
||||
<label>
|
||||
<?php esc_html_e( 'Enabled', 'woocommerce' ); ?>
|
||||
|
||||
@@ -53,7 +53,7 @@ class WC_Report_Customer_List extends WP_List_Table {
|
||||
if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$linked = wc_update_new_customer_past_orders( absint( $_GET['link_orders'] ) );
|
||||
/* translators: single or plural number of orders */
|
||||
echo '<div class="updated"><p>' . sprintf( esc_html( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) ) . '</p></div>';
|
||||
echo '<div class="updated"><p>' . esc_html( sprintf( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) ) . '</p></div>';
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['refresh'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'refresh' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
|
||||
@@ -11,6 +11,8 @@ if ( class_exists( 'WC_Settings_Accounts', false ) ) {
|
||||
return new WC_Settings_Accounts();
|
||||
}
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
|
||||
/**
|
||||
* WC_Settings_Accounts.
|
||||
*/
|
||||
@@ -50,8 +52,9 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
'id' => 'account_registration_options',
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Guest checkout', 'woocommerce' ),
|
||||
'desc' => __( 'Allow customers to place orders without an account', 'woocommerce' ),
|
||||
'title' => __( 'Checkout', 'woocommerce' ),
|
||||
'desc' => __( 'Enable guest checkout (recommended)', 'woocommerce' ),
|
||||
'desc_tip' => __( 'Allows customers to checkout without an account.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_enable_guest_checkout',
|
||||
'default' => 'yes',
|
||||
'type' => 'checkbox',
|
||||
@@ -60,7 +63,7 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Login', 'woocommerce' ),
|
||||
'desc' => __( 'Allow customers to log into an existing account during checkout', 'woocommerce' ),
|
||||
'desc' => __( 'Enable log-in during checkout', 'woocommerce' ),
|
||||
'id' => 'woocommerce_enable_checkout_login_reminder',
|
||||
'default' => 'no',
|
||||
'type' => 'checkbox',
|
||||
@@ -69,31 +72,38 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Account creation', 'woocommerce' ),
|
||||
'desc' => __( 'Allow customers to create an account during checkout', 'woocommerce' ),
|
||||
'desc' => __( 'During checkout', 'woocommerce' ),
|
||||
'desc_tip' => __( 'Customers can create an account before placing their order.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_enable_signup_and_login_from_checkout',
|
||||
'default' => 'no',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => 'start',
|
||||
'legend' => __( 'Allow customers to create an account:', 'woocommerce' ),
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Account creation', 'woocommerce' ),
|
||||
'desc' => __( 'On "My account" page', 'woocommerce' ),
|
||||
'id' => 'woocommerce_enable_myaccount_registration',
|
||||
'default' => 'no',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => 'end',
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Account creation options', 'woocommerce' ),
|
||||
'desc' => __( 'Use email address as account login (recommended)', 'woocommerce' ),
|
||||
'desc_tip' => __( 'If unchecked, customers will need to set a username during account creation.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_registration_generate_username',
|
||||
'default' => 'yes',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => 'start',
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
'desc' => __( 'Allow customers to create an account on the "My account" page', 'woocommerce' ),
|
||||
'id' => 'woocommerce_enable_myaccount_registration',
|
||||
'default' => 'no',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => '',
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
'desc' => __( 'When creating an account, automatically generate an account username for the customer based on their name, surname or email', 'woocommerce' ),
|
||||
'id' => 'woocommerce_registration_generate_username',
|
||||
'default' => 'yes',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => '',
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
'desc' => __( 'When creating an account, send the new user a link to set their password', 'woocommerce' ),
|
||||
'title' => __( 'Account creation options', 'woocommerce' ),
|
||||
'desc' => __( 'Send password setup link (recommended)', 'woocommerce' ),
|
||||
'desc_tip' => __( 'New customers receive an email to set up their password.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_registration_generate_password',
|
||||
'default' => 'yes',
|
||||
'type' => 'checkbox',
|
||||
@@ -118,7 +128,7 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
'id' => 'woocommerce_erasure_request_removes_download_data',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'no',
|
||||
'checkboxgroup' => 'end',
|
||||
'checkboxgroup' => '',
|
||||
'autoload' => false,
|
||||
),
|
||||
array(
|
||||
@@ -127,7 +137,7 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
'desc_tip' => __( 'Adds an option to the orders screen for removing personal data in bulk. Note that removing personal data cannot be undone.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_allow_bulk_remove_personal_data',
|
||||
'type' => 'checkbox',
|
||||
'checkboxgroup' => 'start',
|
||||
'checkboxgroup' => 'end',
|
||||
'default' => 'no',
|
||||
'autoload' => false,
|
||||
),
|
||||
@@ -229,10 +239,71 @@ class WC_Settings_Accounts extends WC_Settings_Page {
|
||||
),
|
||||
);
|
||||
|
||||
return apply_filters(
|
||||
'woocommerce_' . $this->id . '_settings',
|
||||
$account_settings
|
||||
);
|
||||
// Change settings when using the block based checkout.
|
||||
if ( CartCheckoutUtils::is_checkout_block_default() ) {
|
||||
$account_settings = array_filter(
|
||||
$account_settings,
|
||||
function ( $setting ) {
|
||||
return 'woocommerce_registration_generate_username' !== $setting['id'];
|
||||
},
|
||||
);
|
||||
$account_settings = array_map(
|
||||
function ( $setting ) {
|
||||
if ( 'woocommerce_registration_generate_password' === $setting['id'] ) {
|
||||
unset( $setting['checkboxgroup'] );
|
||||
}
|
||||
return $setting;
|
||||
},
|
||||
$account_settings
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter account settings.
|
||||
*
|
||||
* @hook woocommerce_account_settings
|
||||
* @since 3.5.0
|
||||
* @param array $account_settings Account settings.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_' . $this->id . '_settings', $account_settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the HTML for the settings.
|
||||
*/
|
||||
public function output() {
|
||||
parent::output();
|
||||
|
||||
// The following code toggles disabled state on the account options based on other values.
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const checkboxes = [
|
||||
document.getElementById("woocommerce_enable_signup_and_login_from_checkout"),
|
||||
document.getElementById("woocommerce_enable_myaccount_registration"),
|
||||
document.getElementById("woocommerce_enable_signup_from_checkout_for_subscriptions")
|
||||
];
|
||||
const inputs = [
|
||||
document.getElementById("woocommerce_registration_generate_username"),
|
||||
document.getElementById("woocommerce_registration_generate_password")
|
||||
];
|
||||
|
||||
function updateInputs() {
|
||||
const isChecked = checkboxes.some(cb => cb && cb.checked);
|
||||
inputs.forEach(input => {
|
||||
if ( ! input ) {
|
||||
return;
|
||||
}
|
||||
input.disabled = !isChecked;
|
||||
input.closest('td').classList.toggle("disabled", !isChecked);
|
||||
});
|
||||
}
|
||||
|
||||
checkboxes.forEach(cb => cb && cb.addEventListener('change', updateInputs));
|
||||
updateInputs(); // Initial state
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ class WC_Settings_Advanced extends WC_Settings_Page {
|
||||
__( 'The legacy REST API is NOT enabled', 'woocommerce' );
|
||||
|
||||
$legacy_api_setting_tip =
|
||||
is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' ) ?
|
||||
WC()->legacy_rest_api_is_available() ?
|
||||
__( 'ℹ️️ The WooCommerce Legacy REST API extension is installed and active.', 'woocommerce' ) :
|
||||
sprintf(
|
||||
/* translators: placeholders are URLs */
|
||||
|
||||
@@ -189,17 +189,20 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
|
||||
break;
|
||||
case 'action':
|
||||
$setup_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) );
|
||||
// Override the behaviour for WooPayments plugin.
|
||||
// Override the behaviour for the WooPayments plugin.
|
||||
if (
|
||||
// Keep old brand name for backwards compatibility.
|
||||
( 'WooCommerce Payments' === $method_title || 'WooPayments' === $method_title ) &&
|
||||
class_exists( 'WC_Payments_Account' )
|
||||
) {
|
||||
if ( ! WooCommercePayments::is_connected() || WooCommercePayments::is_account_partially_onboarded() ) {
|
||||
// The CTA text and label is "Finish set up" if the account is not connected or not completely onboarded.
|
||||
$setup_url = WC_Payments_Account::get_connect_url(); // Plugin will handle the redirection to the connect page or directly to the provider (e.g. Stripe).
|
||||
// The CTA text and label is "Finish setup" if the account is not connected or not completely onboarded.
|
||||
// Plugin will handle the redirection to the connect page or directly to the provider (e.g. Stripe).
|
||||
$setup_url = WC_Payments_Account::get_connect_url();
|
||||
// Add the `from` parameter to the URL, so we know where the user came from.
|
||||
$setup_url = add_query_arg( 'from', 'WCADMIN_PAYMENT_SETTINGS', $setup_url );
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish set up', 'woocommerce' ) . '</a>';
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish setup', 'woocommerce' ) . '</a>';
|
||||
} else {
|
||||
// If the account is fully onboarded, the CTA text and label is "Manage" regardless gateway is enabled or not.
|
||||
/* Translators: %s Payment gateway name. */
|
||||
@@ -210,7 +213,7 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Manage the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Manage', 'woocommerce' ) . '</a>';
|
||||
} else {
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish set up', 'woocommerce' ) . '</a>';
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish setup', 'woocommerce' ) . '</a>';
|
||||
}
|
||||
break;
|
||||
case 'status':
|
||||
|
||||
@@ -446,6 +446,20 @@ class WC_Settings_Products extends WC_Settings_Page {
|
||||
),
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __( 'Count partial downloads', 'woocommerce' ),
|
||||
'desc' => __( 'Count downloads even if only part of a file is fetched.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_downloads_count_partial',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'yes',
|
||||
'desc_tip' => sprintf(
|
||||
/* Translators: 1: opening link tag 2: closing link tag. */
|
||||
__( 'Repeat fetches made within a reasonable window of time (by default, 30 minutes) will not be counted twice. This is a generally reasonably way to enforce download limits in relation to ranged requests. %1$sLearn more.%2$s', 'woocommerce' ),
|
||||
'<a href="https://woocommerce.com/document/digital-downloadable-product-handling/">',
|
||||
'</a>'
|
||||
),
|
||||
),
|
||||
|
||||
array(
|
||||
'type' => 'sectionend',
|
||||
'id' => 'digital_download_options',
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
use Automattic\WooCommerce\Blocks\Shipping\ShippingController;
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
@@ -242,10 +243,15 @@ do_action( 'woocommerce_shipping_zone_after_methods_table', $zone );
|
||||
);
|
||||
|
||||
} else {
|
||||
/* translators: %s: Local pickup settings page URL. */
|
||||
$message = __( 'Local pickup: Set up pickup locations in the <a href="%s">Local pickup settings page</a>.', 'woocommerce' );
|
||||
if ( LocalPickupUtils::is_local_pickup_enabled() ) {
|
||||
/* translators: %s: Local pickup settings page URL. */
|
||||
$message = __( 'Local pickup: Manage existing pickup locations in the <a href="%s">Local pickup settings page</a>.', 'woocommerce' );
|
||||
}
|
||||
printf(
|
||||
wp_kses(
|
||||
/* translators: %s: Local pickup settings page URL. */
|
||||
__( 'Local pickup: Set up pickup locations in the <a href="%s">Local pickup settings page</a>.', 'woocommerce' ),
|
||||
$message,
|
||||
array( 'a' => array( 'href' => array() ) )
|
||||
),
|
||||
esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping§ion=pickup_location' ) )
|
||||
|
||||
@@ -147,9 +147,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
$legacy_api_option_name =
|
||||
is_null( wc()->api ) ?
|
||||
__( 'Legacy API v3 (⚠️ NOT AVAILABLE)', 'woocommerce' ) :
|
||||
__( 'Legacy API v3 (deprecated)', 'woocommerce' );
|
||||
WC()->legacy_rest_api_is_available() ?
|
||||
__( 'Legacy API v3 (deprecated)', 'woocommerce' ) :
|
||||
__( 'Legacy API v3 (⚠️ NOT AVAILABLE)', 'woocommerce' );
|
||||
?>
|
||||
<option value="legacy_v3" <?php selected( 'legacy_v3', $webhook->get_api_version(), true ); ?>><?php echo esc_html( $legacy_api_option_name ); ?></option>
|
||||
</select>
|
||||
|
||||
@@ -102,12 +102,12 @@ if ( file_exists( $plugin_path ) ) {
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The WooCommerce Legacy REST API plugin running on this site.', 'woocommerce' ) ); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ( is_null( wc()->api ) ) {
|
||||
echo '<mark class="info-icon"><span class="dashicons dashicons-info"></span> ' . esc_html__( 'The Legacy REST API plugin is not installed on this site.', 'woocommerce' ) . '</mark>';
|
||||
} else {
|
||||
if ( WC()->legacy_rest_api_is_available() ) {
|
||||
$plugin_path = wc_get_container()->get( \Automattic\WooCommerce\Utilities\PluginUtil::class )->get_wp_plugin_id( 'woocommerce-legacy-rest-api' );
|
||||
$version = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_path )['Version'] ?? '';
|
||||
echo '<mark class="yes"><span class="dashicons dashicons-yes"></span> ' . esc_html( $version ) . ' <code class="private">' . esc_html( wc()->api->get_rest_api_package_path() ) . '</code></mark> ';
|
||||
} else {
|
||||
echo '<mark class="info-icon"><span class="dashicons dashicons-info"></span> ' . esc_html__( 'The Legacy REST API plugin is not installed on this site.', 'woocommerce' ) . '</mark>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
@@ -695,8 +695,8 @@ if ( 0 < $mu_plugins_count ) :
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td data-export-label="API Enabled"><?php esc_html_e( 'API enabled', 'woocommerce' ); ?>:</td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'Does your site have REST API enabled?', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||
<td data-export-label="Legacy API Enabled"><?php esc_html_e( 'Legacy API enabled', 'woocommerce' ); ?>:</td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'Does your site have the Legacy REST API enabled?', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||
<td><?php echo $settings['api_enabled'] ? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>' : '<mark class="no">–</mark>'; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -42,7 +42,7 @@ if ( ! $tab_exists ) {
|
||||
?>
|
||||
<p class="submit">
|
||||
<?php if ( empty( $GLOBALS['hide_save_button'] ) ) : ?>
|
||||
<button name="save" class="woocommerce-save-button components-button is-primary" type="submit" value="<?php esc_attr_e( 'Save changes', 'woocommerce' ); ?>"><?php esc_html_e( 'Save changes', 'woocommerce' ); ?></button>
|
||||
<button name="save" disabled class="woocommerce-save-button components-button is-primary" type="submit" value="<?php esc_attr_e( 'Save changes', 'woocommerce' ); ?>"><?php esc_html_e( 'Save changes', 'woocommerce' ); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php wp_nonce_field( 'woocommerce-settings' ); ?>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user