plugin updates

This commit is contained in:
Tony Volpe
2024-11-15 13:53:04 -05:00
parent 1293d604ca
commit 0238f0c4ca
2009 changed files with 163492 additions and 89543 deletions

View File

@@ -557,7 +557,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
}
// Marketplace promotions.
if ( in_array( $screen_id, array( 'woocommerce_page_wc-admin' ), true ) ) {
if ( in_array( $screen_id, array( 'edit-shop_coupon', 'woocommerce_page_wc-admin' ), true ) ) {
$promotions = WC_Admin_Marketplace_Promotions::get_active_promotions();

View File

@@ -0,0 +1,792 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName.
/**
* Brands Admin Page
*
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
*
* @package WooCommerce\Admin
* @version 9.4.0
*/
declare( strict_types = 1);
use Automattic\Jetpack\Constants;
/**
* WC_Brands_Admin class.
*/
class WC_Brands_Admin {
/**
* Settings array.
*
* @var array
*/
public $settings_tabs;
/**
* Admin fields.
*
* @var array
*/
public $fields = array();
/**
* __construct function.
*/
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'styles' ) );
add_action( 'product_brand_add_form_fields', array( $this, 'add_thumbnail_field' ) );
add_action( 'product_brand_edit_form_fields', array( $this, 'edit_thumbnail_field' ), 10, 1 );
add_action( 'created_term', array( $this, 'thumbnail_field_save' ), 10, 1 );
add_action( 'edit_term', array( $this, 'thumbnail_field_save' ), 10, 1 );
add_action( 'product_brand_pre_add_form', array( $this, 'taxonomy_description' ) );
add_filter( 'woocommerce_sortable_taxonomies', array( $this, 'sort_brands' ) );
add_filter( 'manage_edit-product_brand_columns', array( $this, 'columns' ) );
add_filter( 'manage_product_brand_custom_column', array( $this, 'column' ), 10, 3 );
add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ), 20, 1 );
add_filter(
'woocommerce_products_admin_list_table_filters',
function ( $args ) {
$args['product_brand'] = array( $this, 'render_product_brand_filter' );
return $args;
}
);
$this->settings_tabs = array(
'brands' => __( 'Brands', 'woocommerce' ),
);
// Hiding setting for future depreciation. Only users who have touched this settings should see it.
$setting_value = get_option( 'wc_brands_show_description' );
if ( is_string( $setting_value ) ) {
// Add the settings fields to each tab.
$this->init_form_fields();
add_action( 'woocommerce_get_sections_products', array( $this, 'add_settings_tab' ) );
add_action( 'woocommerce_get_settings_products', array( $this, 'add_settings_section' ), null, 2 );
}
add_action( 'woocommerce_update_options_catalog', array( $this, 'save_admin_settings' ) );
/* 2.1 */
add_action( 'woocommerce_update_options_products', array( $this, 'save_admin_settings' ) );
// Add brands filtering to the coupon creation screens.
add_action( 'woocommerce_coupon_options_usage_restriction', array( $this, 'add_coupon_brands_fields' ) );
add_action( 'woocommerce_coupon_options_save', array( $this, 'save_coupon_brands' ) );
// Permalinks.
add_filter( 'pre_update_option_woocommerce_permalinks', array( $this, 'validate_product_base' ) );
add_action( 'current_screen', array( $this, 'add_brand_base_setting' ) );
// CSV Import/Export Support.
// https://github.com/woocommerce/woocommerce/wiki/Product-CSV-Importer-&-Exporter
// Import.
add_filter( 'woocommerce_csv_product_import_mapping_options', array( $this, 'add_column_to_importer_exporter' ), 10 );
add_filter( 'woocommerce_csv_product_import_mapping_default_columns', array( $this, 'add_default_column_mapping' ), 10 );
add_filter( 'woocommerce_product_import_inserted_product_object', array( $this, 'process_import' ), 10, 2 );
// Export.
add_filter( 'woocommerce_product_export_column_names', array( $this, 'add_column_to_importer_exporter' ), 10 );
add_filter( 'woocommerce_product_export_product_default_columns', array( $this, 'add_column_to_importer_exporter' ), 10 );
add_filter( 'woocommerce_product_export_product_column_brand_ids', array( $this, 'get_column_value_brand_ids' ), 10, 2 );
}
/**
* Add the settings for the new "Brands" subtab.
*
* @since 9.4.0
*
* @param array $settings Settings.
* @param array $current_section Current section.
*/
public function add_settings_section( $settings, $current_section ) {
if ( 'brands' === $current_section ) {
$settings = $this->settings;
}
return $settings;
}
/**
* Add a new "Brands" subtab to the "Products" tab.
*
* @since 9.4.0
* @param array $sections Sections.
*/
public function add_settings_tab( $sections ) {
$sections = array_merge( $sections, $this->settings_tabs );
return $sections;
}
/**
* Display coupon filter fields relating to brands.
*
* @since 9.4.0
* @return void
*/
public function add_coupon_brands_fields() {
global $post;
// Brands.
?>
<p class="form-field"><label for="product_brands"><?php esc_html_e( 'Product brands', 'woocommerce' ); ?></label>
<select id="product_brands" name="product_brands[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php esc_attr_e( 'Any brand', 'woocommerce' ); ?>">
<?php
$category_ids = (array) get_post_meta( $post->ID, 'product_brands', true );
$categories = get_terms(
array(
'taxonomy' => 'product_brand',
'orderby' => 'name',
'hide_empty' => false,
)
);
if ( $categories ) {
foreach ( $categories as $cat ) {
echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $category_ids, true ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
}
}
?>
</select>
<?php
echo wc_help_tip( esc_html__( 'A product must be associated with this brand for the coupon to remain valid or, for "Product Discounts", products with these brands will be discounted.', 'woocommerce' ) );
// Exclude Brands.
?>
<p class="form-field"><label for="exclude_product_brands"><?php esc_html_e( 'Exclude brands', 'woocommerce' ); ?></label>
<select id="exclude_product_brands" name="exclude_product_brands[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php esc_attr_e( 'No brands', 'woocommerce' ); ?>">
<?php
$category_ids = (array) get_post_meta( $post->ID, 'exclude_product_brands', true );
$categories = get_terms(
array(
'taxonomy' => 'product_brand',
'orderby' => 'name',
'hide_empty' => false,
)
);
if ( $categories ) {
foreach ( $categories as $cat ) {
echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $category_ids, true ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
}
}
?>
</select>
<?php
echo wc_help_tip( esc_html__( 'Product must not be associated with these brands for the coupon to remain valid or, for "Product Discounts", products associated with these brands will not be discounted.', 'woocommerce' ) );
}
/**
* Save coupon filter fields relating to brands.
*
* @since 9.4.0
* @param int $post_id Post ID.
* @return void
*/
public function save_coupon_brands( $post_id ) {
$product_brands = isset( $_POST['product_brands'] ) ? array_map( 'intval', $_POST['product_brands'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
$exclude_product_brands = isset( $_POST['exclude_product_brands'] ) ? array_map( 'intval', $_POST['exclude_product_brands'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
// Save.
update_post_meta( $post_id, 'product_brands', $product_brands );
update_post_meta( $post_id, 'exclude_product_brands', $exclude_product_brands );
}
/**
* Prepare form fields to be used in the various tabs.
*/
public function init_form_fields() {
/**
* Filter Brands settings.
*
* @since 9.4.0
*
* @param array $settings Brands settings.
*/
$this->settings = apply_filters(
'woocommerce_brands_settings_fields',
array(
array(
'name' => __( 'Brands Archives', 'woocommerce' ),
'type' => 'title',
'desc' => '',
'id' => 'brands_archives',
),
array(
'name' => __( 'Show description', 'woocommerce' ),
'desc' => __( 'Choose to show the brand description on the archive page. Turn this off if you intend to use the description widget instead. Please note: this is only for themes that do not show the description.', 'woocommerce' ),
'tip' => '',
'id' => 'wc_brands_show_description',
'css' => '',
'std' => 'yes',
'type' => 'checkbox',
),
array(
'type' => 'sectionend',
'id' => 'brands_archives',
),
)
);
}
/**
* Enqueue scripts.
*
* @return void
*/
public function scripts() {
$screen = get_current_screen();
$version = Constants::get_constant( 'WC_VERSION' );
$suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
if ( 'edit-product' === $screen->id ) {
wp_register_script(
'wc-brands-enhanced-select',
WC()->plugin_url() . '/assets/js/admin/wc-brands-enhanced-select' . $suffix . '.js',
array( 'jquery', 'selectWoo', 'wc-enhanced-select', 'wp-api' ),
$version,
true
);
wp_localize_script(
'wc-brands-enhanced-select',
'wc_brands_enhanced_select_params',
array( 'ajax_url' => get_rest_url() . 'brands/search' )
);
wp_enqueue_script( 'wc-brands-enhanced-select' );
}
if ( in_array( $screen->id, array( 'edit-product_brand' ), true ) ) {
wp_enqueue_media();
wp_enqueue_style( 'woocommerce_admin_styles' );
}
}
/**
* Enqueue styles.
*
* @return void
*/
public function styles() {
$version = Constants::get_constant( 'WC_VERSION' );
wp_enqueue_style( 'brands-admin-styles', WC()->plugin_url() . '/assets/css/brands-admin.css', array(), $version );
}
/**
* Admin settings function.
*/
public function admin_settings() {
woocommerce_admin_fields( $this->settings );
}
/**
* Save admin settings function.
*/
public function save_admin_settings() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['section'] ) && 'brands' === $_GET['section'] ) {
woocommerce_update_options( $this->settings );
}
}
/**
* Category thumbnails.
*/
public function add_thumbnail_field() {
global $woocommerce;
?>
<div class="form-field">
<label><?php esc_html_e( 'Thumbnail', 'woocommerce' ); ?></label>
<div id="product_cat_thumbnail" style="float:left;margin-right:10px;"><img src="<?php echo esc_url( wc_placeholder_img_src() ); ?>" width="60px" height="60px" /></div>
<div style="line-height:60px;">
<input type="hidden" id="product_cat_thumbnail_id" name="product_cat_thumbnail_id" />
<button type="button" class="upload_image_button button"><?php esc_html_e( 'Upload/Add image', 'woocommerce' ); ?></button>
<button type="button" class="remove_image_button button"><?php esc_html_e( 'Remove image', 'woocommerce' ); ?></button>
</div>
<script type="text/javascript">
jQuery(function(){
// Only show the "remove image" button when needed
if ( ! jQuery('#product_cat_thumbnail_id').val() ) {
jQuery('.remove_image_button').hide();
}
// Uploading files
var file_frame;
jQuery(document).on( 'click', '.upload_image_button', function( event ){
event.preventDefault();
// If the media frame already exists, reopen it.
if ( file_frame ) {
file_frame.open();
return;
}
// Create the media frame.
file_frame = wp.media.frames.downloadable_file = wp.media({
title: '<?php echo esc_js( __( 'Choose an image', 'woocommerce' ) ); ?>',
button: {
text: '<?php echo esc_js( __( 'Use image', 'woocommerce' ) ); ?>',
},
multiple: false
});
// When an image is selected, run a callback.
file_frame.on( 'select', function() {
attachment = file_frame.state().get('selection').first().toJSON();
jQuery('#product_cat_thumbnail_id').val( attachment.id );
jQuery('#product_cat_thumbnail img').attr('src', attachment.url );
jQuery('.remove_image_button').show();
});
// Finally, open the modal.
file_frame.open();
});
jQuery(document).on( 'click', '.remove_image_button', function( event ){
jQuery('#product_cat_thumbnail img').attr('src', '<?php echo esc_js( wc_placeholder_img_src() ); ?>');
jQuery('#product_cat_thumbnail_id').val('');
jQuery('.remove_image_button').hide();
return false;
});
});
</script>
<div class="clear"></div>
</div>
<?php
}
/**
* Edit thumbnail field row.
*
* @param WP_Term $term Current taxonomy term object.
*/
public function edit_thumbnail_field( $term ) {
global $woocommerce;
$image = '';
$thumbnail_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
if ( $thumbnail_id ) {
$image = wp_get_attachment_url( $thumbnail_id );
}
if ( empty( $image ) ) {
$image = wc_placeholder_img_src();
}
?>
<tr class="form-field">
<th scope="row" valign="top"><label><?php esc_html_e( 'Thumbnail', 'woocommerce' ); ?></label></th>
<td>
<div id="product_cat_thumbnail" style="float:left;margin-right:10px;"><img src="<?php echo esc_url( $image ); ?>" width="60px" height="60px" /></div>
<div style="line-height:60px;">
<input type="hidden" id="product_cat_thumbnail_id" name="product_cat_thumbnail_id" value="<?php echo esc_attr( $thumbnail_id ); ?>" />
<button type="button" class="upload_image_button button"><?php esc_html_e( 'Upload/Add image', 'woocommerce' ); ?></button>
<button type="button" class="remove_image_button button"><?php esc_html_e( 'Remove image', 'woocommerce' ); ?></button>
</div>
<script type="text/javascript">
jQuery(function(){
// Only show the "remove image" button when needed
if ( ! jQuery('#product_cat_thumbnail_id').val() )
jQuery('.remove_image_button').hide();
// Uploading files
var file_frame;
jQuery(document).on( 'click', '.upload_image_button', function( event ){
event.preventDefault();
// If the media frame already exists, reopen it.
if ( file_frame ) {
file_frame.open();
return;
}
// Create the media frame.
file_frame = wp.media.frames.downloadable_file = wp.media({
title: '<?php echo esc_js( __( 'Choose an image', 'woocommerce' ) ); ?>',
button: {
text: '<?php echo esc_js( __( 'Use image', 'woocommerce' ) ); ?>',
},
multiple: false
});
// When an image is selected, run a callback.
file_frame.on( 'select', function() {
attachment = file_frame.state().get('selection').first().toJSON();
jQuery('#product_cat_thumbnail_id').val( attachment.id );
jQuery('#product_cat_thumbnail img').attr('src', attachment.url );
jQuery('.remove_image_button').show();
});
// Finally, open the modal.
file_frame.open();
});
jQuery(document).on( 'click', '.remove_image_button', function( event ){
jQuery('#product_cat_thumbnail img').attr('src', '<?php echo esc_js( wc_placeholder_img_src() ); ?>');
jQuery('#product_cat_thumbnail_id').val('');
jQuery('.remove_image_button').hide();
return false;
});
});
</script>
<div class="clear"></div>
</td>
</tr>
<?php
}
/**
* Saves thumbnail field.
*
* @param int $term_id Term ID.
*
* @return void
*/
public function thumbnail_field_save( $term_id ) {
if ( isset( $_POST['product_cat_thumbnail_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
update_term_meta( $term_id, 'thumbnail_id', absint( $_POST['product_cat_thumbnail_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
}
/**
* Description for brand page.
*/
public function taxonomy_description() {
echo wp_kses_post( wpautop( __( 'Brands be added and managed from this screen. You can optionally upload a brand image to display in brand widgets and on brand archives', 'woocommerce' ) ) );
}
/**
* Sort brands function.
*
* @param array $sortable Sortable array.
*/
public function sort_brands( $sortable ) {
$sortable[] = 'product_brand';
return $sortable;
}
/**
* Add brands column in second-to-last position.
*
* @since 9.4.0
* @param mixed $columns Columns.
* @return array
*/
public function product_columns( $columns ) {
if ( empty( $columns ) ) {
return $columns;
}
$column_index = 'taxonomy-product_brand';
$brands_column = $columns[ $column_index ];
unset( $columns[ $column_index ] );
return array_merge(
array_slice( $columns, 0, -2, true ),
array( $column_index => $brands_column ),
array_slice( $columns, -2, null, true )
);
}
/**
* Columns function.
*
* @param mixed $columns Columns.
*/
public function columns( $columns ) {
if ( empty( $columns ) ) {
return $columns;
}
$new_columns = array();
$new_columns['cb'] = $columns['cb'];
$new_columns['thumb'] = __( 'Image', 'woocommerce' );
unset( $columns['cb'] );
$columns = array_merge( $new_columns, $columns );
return $columns;
}
/**
* Column function.
*
* @param mixed $columns Columns.
* @param mixed $column Column.
* @param mixed $id ID.
*/
public function column( $columns, $column, $id ) {
if ( 'thumb' === $column ) {
global $woocommerce;
$image = '';
$thumbnail_id = get_term_meta( $id, 'thumbnail_id', true );
if ( $thumbnail_id ) {
$image = wp_get_attachment_url( $thumbnail_id );
}
if ( empty( $image ) ) {
$image = wc_placeholder_img_src();
}
$columns .= '<img src="' . $image . '" alt="Thumbnail" class="wp-post-image" height="48" width="48" />';
}
return $columns;
}
/**
* Renders either dropdown or a search field for brands depending on the threshold value of
* woocommerce_product_brand_filter_threshold filter.
*/
public function render_product_brand_filter() {
// phpcs:disable WordPress.Security.NonceVerification
$brands_count = (int) wp_count_terms( 'product_brand' );
$current_brand_slug = wc_clean( wp_unslash( $_GET['product_brand'] ?? '' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
/**
* Filter the brands threshold count.
*
* @since 9.4.0
*
* @param int $value Threshold.
*/
if ( $brands_count <= apply_filters( 'woocommerce_product_brand_filter_threshold', 100 ) ) {
wc_product_dropdown_categories(
array(
'pad_counts' => true,
'show_count' => true,
'orderby' => 'name',
'selected' => $current_brand_slug,
'show_option_none' => __( 'Filter by brand', 'woocommerce' ),
'option_none_value' => '',
'value_field' => 'slug',
'taxonomy' => 'product_brand',
'name' => 'product_brand',
'class' => 'dropdown_product_brand',
)
);
} else {
$current_brand = $current_brand_slug ? get_term_by( 'slug', $current_brand_slug, 'product_brand' ) : '';
$selected_option = '';
if ( $current_brand_slug && $current_brand ) {
$selected_option = '<option value="' . esc_attr( $current_brand_slug ) . '" selected="selected">' . esc_html( htmlspecialchars( wp_kses_post( $current_brand->name ) ) ) . '</option>';
}
$placeholder = esc_attr__( 'Filter by brand', 'woocommerce' );
?>
<select class="wc-brands-search" name="product_brand" data-placeholder="<?php echo $placeholder; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" data-allow_clear="true">
<?php echo $selected_option; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</select>
<?php
}
// phpcs:enable WordPress.Security.NonceVerification
}
/**
* Add brand base permalink setting.
*/
public function add_brand_base_setting() {
$screen = get_current_screen();
if ( ! $screen || 'options-permalink' !== $screen->id ) {
return;
}
add_settings_field(
'woocommerce_product_brand_slug',
__( 'Product brand base', 'woocommerce' ),
array( $this, 'product_brand_slug_input' ),
'permalink',
'optional'
);
$this->save_permalink_settings();
}
/**
* Add a slug input box.
*/
public function product_brand_slug_input() {
$permalink = get_option( 'woocommerce_brand_permalink', '' );
?>
<input name="woocommerce_product_brand_slug" type="text" class="regular-text code" value="<?php echo esc_attr( $permalink ); ?>" placeholder="<?php echo esc_attr_x( 'brand', 'slug', 'woocommerce' ); ?>" />
<?php
}
/**
* Save permalnks settings.
*
* We need to save the options ourselves;
* settings api does not trigger save for the permalinks page.
*/
public function save_permalink_settings() {
if ( ! is_admin() ) {
return;
}
if ( isset( $_POST['permalink_structure'], $_POST['wc-permalinks-nonce'], $_POST['woocommerce_product_brand_slug'] ) && wp_verify_nonce( wp_unslash( $_POST['wc-permalinks-nonce'] ), 'wc-permalinks' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
update_option( 'woocommerce_brand_permalink', wc_sanitize_permalink( trim( wc_clean( wp_unslash( $_POST['woocommerce_product_brand_slug'] ) ) ) ) );
}
}
/**
* Validate the product base.
*
* Must have an additional slug, not just the brand as the base.
*
* @param array $value Value.
*/
public function validate_product_base( $value ) {
if ( '/%product_brand%/' === trailingslashit( $value['product_base'] ) ) {
$value['product_base'] = '/' . _x( 'product', 'slug', 'woocommerce' ) . $value['product_base'];
}
return $value;
}
/**
* Add csv column for importing/exporting.
*
* @param array $options Mapping options.
* @return array $options
*/
public function add_column_to_importer_exporter( $options ) {
$options['brand_ids'] = __( 'Brands', 'woocommerce' );
return $options;
}
/**
* Add default column mapping.
*
* @param array $mappings Mappings.
* @return array $mappings
*/
public function add_default_column_mapping( $mappings ) {
$new_mapping = array( __( 'Brands', 'woocommerce' ) => 'brand_ids' );
return array_merge( $mappings, $new_mapping );
}
/**
* Add brands to newly imported product.
*
* @param WC_Product $product Product being imported.
* @param array $data Raw CSV data.
*/
public function process_import( $product, $data ) {
if ( empty( $data['brand_ids'] ) ) {
return;
}
$brand_ids = array_map( 'intval', $this->parse_brands_field( $data['brand_ids'] ) );
wp_set_object_terms( $product->get_id(), $brand_ids, 'product_brand' );
}
/**
* Parse brands field from a CSV during import.
*
* Based on WC_Product_CSV_Importer::parse_categories_field()
*
* @param string $value Field value.
* @return array
*/
public function parse_brands_field( $value ) {
// Based on WC_Product_Importer::explode_values().
$values = str_replace( '\\,', '::separator::', explode( ',', $value ) );
$row_terms = array();
foreach ( $values as $row_value ) {
$row_terms[] = trim( str_replace( '::separator::', ',', $row_value ) );
}
$brands = array();
foreach ( $row_terms as $row_term ) {
$parent = null;
// WC Core uses '>', but for some reason it's already escaped at this point.
$_terms = array_map( 'trim', explode( '&gt;', $row_term ) );
$total = count( $_terms );
foreach ( $_terms as $index => $_term ) {
$term = term_exists( $_term, 'product_brand', $parent );
if ( is_array( $term ) ) {
$term_id = $term['term_id'];
} else {
$term = wp_insert_term( $_term, 'product_brand', array( 'parent' => intval( $parent ) ) );
if ( is_wp_error( $term ) ) {
break; // We cannot continue if the term cannot be inserted.
}
$term_id = $term['term_id'];
}
// Only requires assign the last category.
if ( ( 1 + $index ) === $total ) {
$brands[] = $term_id;
} else {
// Store parent to be able to insert or query brands based in parent ID.
$parent = $term_id;
}
}
}
return $brands;
}
/**
* Get brands column value for csv export.
*
* @param string $value What will be exported.
* @param WC_Product $product Product being exported.
* @return string Brands separated by commas and child brands as "parent > child".
*/
public function get_column_value_brand_ids( $value, $product ) {
$brand_ids = wp_parse_id_list( wp_get_post_terms( $product->get_id(), 'product_brand', array( 'fields' => 'ids' ) ) );
if ( ! count( $brand_ids ) ) {
return '';
}
// Based on WC_CSV_Exporter::format_term_ids().
$formatted_brands = array();
foreach ( $brand_ids as $brand_id ) {
$formatted_term = array();
$ancestor_ids = array_reverse( get_ancestors( $brand_id, 'product_brand' ) );
foreach ( $ancestor_ids as $ancestor_id ) {
$term = get_term( $ancestor_id, 'product_brand' );
if ( $term && ! is_wp_error( $term ) ) {
$formatted_term[] = $term->name;
}
}
$term = get_term( $brand_id, 'product_brand' );
if ( $term && ! is_wp_error( $term ) ) {
$formatted_term[] = $term->name;
}
$formatted_brands[] = implode( ' > ', $formatted_term );
}
// Based on WC_CSV_Exporter::implode_values().
$values_to_implode = array();
foreach ( $formatted_brands as $brand ) {
$brand = (string) is_scalar( $brand ) ? $brand : '';
$values_to_implode[] = str_replace( ',', '\\,', $brand );
}
return implode( ', ', $values_to_implode );
}
}
$GLOBALS['WC_Brands_Admin'] = new WC_Brands_Admin();

View File

@@ -97,6 +97,8 @@ class WC_Admin_Marketplace_Promotions {
return array();
}
$promotions = self::merge_promos( $promotions );
return self::filter_out_inactive_promotions( $promotions );
}
@@ -144,6 +146,7 @@ class WC_Admin_Marketplace_Promotions {
}
$promotions = json_decode( wp_remote_retrieve_body( $raw_promotions ), true );
if ( ! is_array( $promotions ) ) {
$promotions = array();
@@ -277,6 +280,27 @@ class WC_Admin_Marketplace_Promotions {
return $active_promotions;
}
/**
* Promos arrive in the array of promotions as an array of arrays with the key 'promos'.
* We merge them into the main array.
*
* @param ?array $promotions Promotions data received from WCCOM.
* May have an element with the key 'promos', which contains an array.
*
* @return array
* */
private static function merge_promos( ?array $promotions = array() ): array {
if (
! empty( $promotions['promos'] )
&& is_array( $promotions['promos'] )
) {
$promotions = array_merge( $promotions, $promotions['promos'] );
unset( $promotions['promos'] );
}
return $promotions;
}
/**
* Callback for the `woocommerce_marketplace_menu_items` filter
* in `Automattic\WooCommerce\Internal\Admin\Marketplace::get_marketplace_pages`.

View File

@@ -896,7 +896,8 @@ class WC_Admin_Post_Types {
return false;
}
$old_price = (float) $product->{"get_{$price_type}_price"}();
$old_price = $product->{"get_{$price_type}_price"}();
$old_price = '' === $old_price ? (float) $product->get_regular_price() : (float) $old_price;
$price_changed = false;
$change_price = absint( $request_data[ "change_{$price_type}_price" ] );
@@ -906,13 +907,17 @@ class WC_Admin_Post_Types {
switch ( $change_price ) {
case 1:
$new_price = $price;
if ( empty( $price ) ) {
$new_price = $product->get_regular_price();
} else {
$new_price = $price;
}
break;
case 2:
if ( $is_percentage ) {
$percent = $price / 100;
$new_price = $old_price + ( $old_price * $percent );
} else {
} elseif ( ! empty( $price ) ) {
$new_price = $old_price + $price;
}
break;
@@ -920,7 +925,7 @@ class WC_Admin_Post_Types {
if ( $is_percentage ) {
$percent = $price / 100;
$new_price = max( 0, $old_price - ( $old_price * $percent ) );
} else {
} elseif ( ! empty( $price ) ) {
$new_price = max( 0, $old_price - $price );
}
break;

View File

@@ -85,6 +85,7 @@ class WC_Helper_Admin {
if ( WC_Helper::is_site_connected() ) {
$settings['wccomHelper']['subscription_expired_notice'] = PluginsHelper::get_expired_subscription_notice( false );
$settings['wccomHelper']['subscription_expiring_notice'] = PluginsHelper::get_expiring_subscription_notice( false );
$settings['wccomHelper']['subscription_missing_notice'] = PluginsHelper::get_missing_subscription_notice();
}
return $settings;

View File

@@ -40,11 +40,13 @@ class WC_Helper_Updater {
}
if ( WC_Helper::is_site_connected() ) {
add_action( 'load-plugins.php', array( __CLASS__, 'setup_message_for_expired_and_expiring_subscriptions' ), 11 );
add_action( 'load-plugins.php', array( __CLASS__, 'setup_message_for_plugins_without_subscription' ), 11 );
}
}
/**
* Add the hook for modifying default WPCore update notices on the plugins management page.
* This is for plugins with expired or expiring subscriptions.
*/
public static function setup_message_for_expired_and_expiring_subscriptions() {
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
@@ -52,6 +54,16 @@ class WC_Helper_Updater {
}
}
/**
* Add the hook for modifying default WPCore update notices on the plugins management page.
* This is for plugins without a subscription.
*/
public static function setup_message_for_plugins_without_subscription() {
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
add_action( 'in_plugin_update_message-' . $plugin['_filename'], array( __CLASS__, 'display_notice_for_plugins_without_subscription' ), 10, 2 );
}
}
/**
* Runs in a cron thread, or in a visitor thread if triggered
* by _maybe_update_plugins(), or in an auto-update thread.
@@ -294,10 +306,11 @@ class WC_Helper_Updater {
$renew_link = add_query_arg(
array(
'add-to-cart' => $product_id,
'utm_source' => 'pu',
'utm_campaign' => 'pu_plugin_screen_renew',
),
PluginsHelper::WOO_SUBSCRIPTION_PAGE_URL
PluginsHelper::WOO_CART_PAGE_URL
);
/* translators: 1: Product regular price */
@@ -340,6 +353,52 @@ class WC_Helper_Updater {
}
}
/**
* Runs on in_plugin_update_message-{file-name}, show a message if plugin is without a subscription.
* Only Woo local plugins are passed to this function.
*
* @see setup_message_for_plugins_without_subscription
* @param object $plugin_data An array of plugin metadata.
* @param object $response An object of metadata about the available plugin update.
*
* @return void.
*/
public static function display_notice_for_plugins_without_subscription( $plugin_data, $response ) {
// Extract product ID from the response.
$product_id = preg_replace( '/[^0-9]/', '', $response->id );
if ( WC_Helper::has_product_subscription( $product_id ) ) {
return;
}
// Prepare the expiry notice based on subscription status.
$purchase_link = add_query_arg(
array(
'add-to-cart' => $product_id,
'utm_source' => 'pu',
'utm_campaign' => 'pu_plugin_screen_purchase',
),
PluginsHelper::WOO_CART_PAGE_URL,
);
$notice = sprintf(
/* translators: 1: URL to My Subscriptions page */
__( ' You don\'t have a subscription, <a href="%1$s" class="woocommerce-purchase-subscription">subscribe</a> to update.', 'woocommerce' ),
esc_url( $purchase_link ),
);
// Display the expiry notice.
echo wp_kses(
$notice,
array(
'a' => array(
'href' => array(),
'class' => array(),
),
)
);
}
/**
* Get update data for all plugins.
*

View File

@@ -371,7 +371,7 @@ class WC_Product_CSV_Importer_Controller {
*
* @since 3.1.0
*/
'lines' => apply_filters( 'woocommerce_product_import_batch_size', 30 ),
'lines' => apply_filters( 'woocommerce_product_import_batch_size', 1 ),
'parse' => true,
);

View File

@@ -118,8 +118,14 @@ if ( wc_tax_enabled() ) {
<li><strong><?php esc_html_e( 'Coupon(s)', 'woocommerce' ); ?></strong></li>
<?php
foreach ( $coupons as $item_id => $item ) :
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) ); // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
$class = $order->is_editable() ? 'code editable' : 'code';
$coupon_info = $item->get_meta( 'coupon_info' );
if ( $coupon_info ) {
$coupon_info = json_decode( $coupon_info, true );
$post_id = $coupon_info[0]; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
} else {
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' AND post_date < %s LIMIT 1;", $item->get_code(), $order->get_date_created()->format( 'Y-m-d H:i:s' ) ) ); // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
}
$class = $order->is_editable() ? 'code editable' : 'code';
?>
<li class="<?php echo esc_attr( $class ); ?>">
<?php if ( $post_id ) : ?>

View File

@@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<strong><?php echo esc_html( wc_attribute_label( $attribute->get_name() ) ); ?></strong>
<input type="hidden" name="attribute_names[<?php echo esc_attr( $i ); ?>]" value="<?php echo esc_attr( $attribute->get_name() ); ?>" />
<?php else : ?>
<input type="text" class="attribute_name" name="attribute_names[<?php echo esc_attr( $i ); ?>]" value="<?php echo esc_attr( $attribute->get_name() ); ?>" placeholder="<?php esc_attr_e( 'f.e. size or color', 'woocommerce' ); ?>" />
<input type="text" class="attribute_name" name="attribute_names[<?php echo esc_attr( $i ); ?>]" value="<?php echo esc_attr( $attribute->get_name() ); ?>" placeholder="<?php esc_attr_e( 'e.g. length or weight', 'woocommerce' ); ?>" />
<?php endif; ?>
<input type="hidden" name="attribute_position[<?php echo esc_attr( $i ); ?>]" class="attribute_position" value="<?php echo esc_attr( $attribute->get_position() ); ?>" />
</td>

View File

@@ -57,6 +57,16 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page {
//phpcs:disable WordPress.Security.NonceVerification.Recommended
global $current_section;
// We don't want to output anything from the action for now. So we buffer it and discard it.
ob_start();
/**
* Fires before the payment gateways settings fields are rendered.
*
* @since 1.5.7
*/
do_action( 'woocommerce_admin_field_payment_gateways' );
ob_end_clean();
// Load gateways so we can show any global options they may have.
$payment_gateways = WC()->payment_gateways->payment_gateways();
@@ -91,6 +101,11 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page {
global $hide_save_button;
$hide_save_button = true;
echo '<div id="experimental_wc_settings_payments_' . esc_attr( $section ) . '"></div>';
// Output the gateways data to the page so the React app can use it.
$controller = new WC_REST_Payment_Gateways_Controller();
$response = $controller->get_items( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways' ) );
echo '<script type="application/json" id="experimental_wc_settings_payments_gateways">' . wp_json_encode( $response->data ) . '</script>';
}
/**