PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.4
WooCommerce v10.9.4
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / class-wc-admin-brands.php
woocommerce / includes / admin Last commit date
helper 2 months ago importers 1 year ago list-tables 1 month ago marketplace-suggestions 11 months ago meta-boxes 1 month ago notes 2 months ago plugin-updates 2 years ago reports 3 months ago settings 6 days ago views 1 month ago class-wc-admin-addons.php 9 months ago class-wc-admin-api-keys-table-list.php 2 years ago class-wc-admin-api-keys.php 11 months ago class-wc-admin-assets.php 1 month ago class-wc-admin-attributes.php 1 month ago class-wc-admin-brands.php 4 months ago class-wc-admin-customize.php 5 years ago class-wc-admin-dashboard-setup.php 1 month ago class-wc-admin-dashboard.php 4 months ago class-wc-admin-duplicate-product.php 5 months ago class-wc-admin-exporters.php 1 year ago class-wc-admin-help.php 2 years ago class-wc-admin-importers.php 11 months ago class-wc-admin-log-table-list.php 4 months ago class-wc-admin-marketplace-promotions.php 4 months ago class-wc-admin-menus.php 1 month ago class-wc-admin-meta-boxes.php 1 month ago class-wc-admin-notices.php 1 month ago class-wc-admin-permalink-settings.php 5 years ago class-wc-admin-pointers.php 3 years ago class-wc-admin-post-types.php 1 year ago class-wc-admin-profile.php 1 year ago class-wc-admin-reports.php 4 months ago class-wc-admin-settings.php 3 months ago class-wc-admin-setup-wizard.php 1 month ago class-wc-admin-status.php 1 year ago class-wc-admin-taxonomies.php 1 month ago class-wc-admin-upload-downloadable-product.php 2 years ago class-wc-admin-webhooks-table-list.php 1 month ago class-wc-admin-webhooks.php 1 month ago class-wc-admin.php 3 months ago wc-admin-functions.php 7 months ago wc-meta-box-functions.php 1 year ago woocommerce-legacy-reports.php 1 year ago
class-wc-admin-brands.php
856 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName.
2 /**
3 * Brands Admin Page
4 *
5 * Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
6 *
7 * @package WooCommerce\Admin
8 * @version x.x.x
9 */
10
11 declare( strict_types = 1);
12
13 use Automattic\Jetpack\Constants;
14
15 /**
16 * WC_Brands_Admin class.
17 */
18 class WC_Brands_Admin {
19
20 /**
21 * Settings array (Deprecated).
22 *
23 * @var array
24 */
25 public $settings_tabs;
26
27 /**
28 * Settings form fields (Deprecated).
29 *
30 * @var array
31 */
32 private $settings;
33
34 /**
35 * Admin fields.
36 *
37 * @var array
38 */
39 public $fields = array();
40
41 /**
42 * __construct function.
43 */
44 public function __construct() {
45
46 add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
47 add_action( 'admin_enqueue_scripts', array( $this, 'styles' ) );
48 add_action( 'product_brand_add_form_fields', array( $this, 'add_thumbnail_field' ) );
49 add_action( 'product_brand_edit_form_fields', array( $this, 'edit_thumbnail_field' ), 10, 1 );
50 add_action( 'created_term', array( $this, 'thumbnail_field_save' ), 10, 1 );
51 add_action( 'edit_term', array( $this, 'thumbnail_field_save' ), 10, 1 );
52 add_action( 'product_brand_pre_add_form', array( $this, 'taxonomy_description' ) );
53 add_filter( 'woocommerce_sortable_taxonomies', array( $this, 'sort_brands' ) );
54 add_filter( 'manage_edit-product_brand_columns', array( $this, 'columns' ) );
55 add_filter( 'manage_product_brand_custom_column', array( $this, 'column' ), 10, 3 );
56 add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ), 20, 1 );
57 add_filter(
58 'woocommerce_products_admin_list_table_filters',
59 function ( $args ) {
60 $args['product_brand'] = array( $this, 'render_product_brand_filter' );
61 return $args;
62 }
63 );
64
65 // Hiding setting for future deprecation. Only users who have touched these settings should see it.
66 $setting_value = get_option( 'wc_brands_show_description' );
67 if ( is_string( $setting_value ) ) {
68
69 // Add the settings fields to each tab.
70 add_action(
71 'before_woocommerce_init',
72 function () {
73 $this->init_form_fields();
74 $this->settings_tabs = array(
75 'brands' => __( 'Brands', 'woocommerce' ),
76 );
77 }
78 );
79 add_action( 'woocommerce_get_sections_products', array( $this, 'add_settings_tab' ) );
80 add_action( 'woocommerce_get_settings_products', array( $this, 'add_settings_section' ), null, 2 );
81 }
82
83 add_action( 'woocommerce_update_options_catalog', array( $this, 'save_admin_settings' ) );
84
85 /* 2.1 */
86 add_action( 'woocommerce_update_options_products', array( $this, 'save_admin_settings' ) );
87
88 // Add brands filtering to the coupon creation screens.
89 add_action( 'woocommerce_coupon_options_usage_restriction', array( $this, 'add_coupon_brands_fields' ) );
90 add_action( 'woocommerce_coupon_options_save', array( $this, 'save_coupon_brands' ) );
91
92 // Permalinks.
93 add_filter( 'pre_update_option_woocommerce_permalinks', array( $this, 'validate_product_base' ) );
94
95 add_action( 'current_screen', array( $this, 'add_brand_base_setting' ) );
96
97 // CSV Import/Export Support.
98 // https://woocommerce.com/document/product-csv-importer-exporter/
99 // Import.
100 add_filter( 'woocommerce_csv_product_import_mapping_options', array( $this, 'add_column_to_importer_exporter' ), 10 );
101 add_filter( 'woocommerce_csv_product_import_mapping_default_columns', array( $this, 'add_default_column_mapping' ), 10 );
102 add_filter( 'woocommerce_product_importer_formatting_callbacks', array( $this, 'add_formatting_callback' ), 10, 2 );
103 add_filter( 'woocommerce_product_import_inserted_product_object', array( $this, 'process_import' ), 10, 2 );
104
105 // Export.
106 add_filter( 'woocommerce_product_export_column_names', array( $this, 'add_column_to_importer_exporter' ), 10 );
107 add_filter( 'woocommerce_product_export_product_default_columns', array( $this, 'add_column_to_importer_exporter' ), 10 );
108 add_filter( 'woocommerce_product_export_product_column_brand_ids', array( $this, 'get_column_value_brand_ids' ), 10, 2 );
109 }
110
111 /**
112 * Add the settings for the new "Brands" subtab.
113 *
114 * @since 9.4.0
115 *
116 * @param array $settings Settings.
117 * @param array $current_section Current section.
118 */
119 public function add_settings_section( $settings, $current_section ) {
120 if ( 'brands' === $current_section ) {
121 $settings = $this->settings;
122 }
123 return $settings;
124 }
125
126 /**
127 * Add a new "Brands" subtab to the "Products" tab.
128 *
129 * @since 9.4.0
130 * @param array $sections Sections.
131 */
132 public function add_settings_tab( $sections ) {
133 $sections = array_merge( $sections, $this->settings_tabs );
134 return $sections;
135 }
136
137 /**
138 * Display coupon filter fields relating to brands.
139 *
140 * @since 9.4.0
141 * @return void
142 */
143 public function add_coupon_brands_fields() {
144 global $post;
145 // Brands.
146 ?>
147 <div class="options_group"><div class="hr-section hr-section-coupon_restrictions"><?php echo esc_html__( 'And', 'woocommerce' ); ?></div>
148 <p class="form-field"><label for="product_brands"><?php esc_html_e( 'Product brands', 'woocommerce' ); ?></label>
149 <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' ); ?>">
150 <?php
151 $category_ids = (array) get_post_meta( $post->ID, 'product_brands', true );
152 $categories = get_terms(
153 array(
154 'taxonomy' => 'product_brand',
155 'orderby' => 'name',
156 'hide_empty' => false,
157 )
158 );
159
160 if ( $categories ) {
161 foreach ( $categories as $cat ) {
162 echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $category_ids, true ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
163 }
164 }
165 ?>
166 </select>
167 <?php
168 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' ) );
169 // Exclude Brands.
170 ?>
171 <p class="form-field"><label for="exclude_product_brands"><?php esc_html_e( 'Exclude brands', 'woocommerce' ); ?></label>
172 <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' ); ?>">
173 <?php
174 $category_ids = (array) get_post_meta( $post->ID, 'exclude_product_brands', true );
175 $categories = get_terms(
176 array(
177 'taxonomy' => 'product_brand',
178 'orderby' => 'name',
179 'hide_empty' => false,
180 )
181 );
182
183 if ( $categories ) {
184 foreach ( $categories as $cat ) {
185 echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $category_ids, true ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
186 }
187 }
188 ?>
189 </select>
190 <?php
191 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' ) );
192 ?>
193 </div>
194 <?php
195 }
196
197 /**
198 * Save coupon filter fields relating to brands.
199 *
200 * @since 9.4.0
201 * @param int $post_id Post ID.
202 * @return void
203 */
204 public function save_coupon_brands( $post_id ) {
205 $product_brands = isset( $_POST['product_brands'] ) ? array_map( 'intval', $_POST['product_brands'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
206 $exclude_product_brands = isset( $_POST['exclude_product_brands'] ) ? array_map( 'intval', $_POST['exclude_product_brands'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
207
208 // Save.
209 update_post_meta( $post_id, 'product_brands', $product_brands );
210 update_post_meta( $post_id, 'exclude_product_brands', $exclude_product_brands );
211 }
212
213 /**
214 * Prepare form fields to be used in the various tabs.
215 */
216 public function init_form_fields() {
217
218 /**
219 * Filter Brands settings.
220 *
221 * @since 9.4.0
222 *
223 * @param array $settings Brands settings.
224 */
225 $this->settings = apply_filters(
226 'woocommerce_brands_settings_fields',
227 array(
228 array(
229 'name' => __( 'Brands Archives', 'woocommerce' ),
230 'type' => 'title',
231 'desc' => '',
232 'id' => 'brands_archives',
233 ),
234 array(
235 'name' => __( 'Show description', 'woocommerce' ),
236 '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' ),
237 'tip' => '',
238 'id' => 'wc_brands_show_description',
239 'css' => '',
240 'std' => 'yes',
241 'type' => 'checkbox',
242 ),
243 array(
244 'type' => 'sectionend',
245 'id' => 'brands_archives',
246 ),
247 )
248 );
249 }
250
251 /**
252 * Enqueue scripts.
253 *
254 * @return void
255 */
256 public function scripts() {
257 $screen = get_current_screen();
258 $version = Constants::get_constant( 'WC_VERSION' );
259 $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
260
261 if ( 'edit-product' === $screen->id ) {
262 wp_register_script(
263 'wc-brands-enhanced-select',
264 WC()->plugin_url() . '/assets/js/admin/wc-brands-enhanced-select' . $suffix . '.js',
265 array( 'jquery', 'selectWoo', 'wc-enhanced-select', 'wp-api' ),
266 $version,
267 true
268 );
269 wp_localize_script(
270 'wc-brands-enhanced-select',
271 'wc_brands_enhanced_select_params',
272 array( 'ajax_url' => get_rest_url() . 'brands/search' )
273 );
274 wp_enqueue_script( 'wc-brands-enhanced-select' );
275 }
276
277 if ( in_array( $screen->id, array( 'edit-product_brand' ), true ) ) {
278 wp_enqueue_media();
279 wp_enqueue_style( 'woocommerce_admin_styles' );
280 }
281 }
282
283 /**
284 * Enqueue styles.
285 *
286 * @return void
287 */
288 public function styles() {
289 $version = Constants::get_constant( 'WC_VERSION' );
290 wp_enqueue_style( 'brands-admin-styles', WC()->plugin_url() . '/assets/css/brands-admin.css', array(), $version );
291 }
292
293 /**
294 * Admin settings function.
295 */
296 public function admin_settings() {
297 woocommerce_admin_fields( $this->settings );
298 }
299
300 /**
301 * Save admin settings function.
302 */
303 public function save_admin_settings() {
304 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
305 if ( isset( $_GET['section'] ) && 'brands' === $_GET['section'] ) {
306 woocommerce_update_options( $this->settings );
307 }
308 }
309
310 /**
311 * Category thumbnails.
312 */
313 public function add_thumbnail_field() {
314 global $woocommerce;
315 ?>
316 <div class="form-field">
317 <label><?php esc_html_e( 'Thumbnail', 'woocommerce' ); ?></label>
318 <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>
319 <div style="line-height:60px;">
320 <input type="hidden" id="product_cat_thumbnail_id" name="product_cat_thumbnail_id" />
321 <button type="button" class="upload_image_button button"><?php esc_html_e( 'Upload/Add image', 'woocommerce' ); ?></button>
322 <button type="button" class="remove_image_button button"><?php esc_html_e( 'Remove image', 'woocommerce' ); ?></button>
323 </div>
324 <script type="text/javascript">
325
326 jQuery(function(){
327 // Only show the "remove image" button when needed
328 if ( ! jQuery('#product_cat_thumbnail_id').val() ) {
329 jQuery('.remove_image_button').hide();
330 }
331
332 // Uploading files
333 var file_frame;
334
335 function clearThumbnailField() {
336 jQuery('#product_cat_thumbnail img').attr('src', '<?php echo esc_js( wc_placeholder_img_src() ); ?>');
337 jQuery('#product_cat_thumbnail_id').val('');
338 jQuery('.remove_image_button').hide();
339 }
340
341 jQuery(document).on( 'click', '.upload_image_button', function( event ){
342
343 event.preventDefault();
344
345 // If the media frame already exists, reopen it.
346 if ( file_frame ) {
347 file_frame.open();
348 return;
349 }
350
351 // Create the media frame.
352 file_frame = wp.media.frames.downloadable_file = wp.media({
353 title: '<?php echo esc_js( __( 'Choose an image', 'woocommerce' ) ); ?>',
354 button: {
355 text: '<?php echo esc_js( __( 'Use image', 'woocommerce' ) ); ?>',
356 },
357 multiple: false
358 });
359
360 // When an image is selected, run a callback.
361 file_frame.on( 'select', function() {
362 attachment = file_frame.state().get('selection').first().toJSON();
363
364 jQuery('#product_cat_thumbnail_id').val( attachment.id );
365 jQuery('#product_cat_thumbnail img').attr('src', attachment.url );
366 jQuery('.remove_image_button').show();
367 });
368
369 // Finally, open the modal.
370 file_frame.open();
371 });
372
373 jQuery(document).on( 'click', '.remove_image_button', function( event ){
374 clearThumbnailField();
375 return false;
376 });
377
378 jQuery( document ).on( 'ajaxComplete', function( event, request, options ) {
379 if ( request && 4 === request.readyState && 200 === request.status
380 && options.data && 0 <= options.data.indexOf( 'action=add-tag' ) ) {
381
382 var res = wpAjax.parseAjaxResponse( request.responseXML, 'ajax-response' );
383 if ( ! res || res.errors ) {
384 return;
385 }
386
387 clearThumbnailField();
388
389 return;
390 }
391 } );
392 });
393
394 </script>
395 <div class="clear"></div>
396 </div>
397 <?php
398 }
399
400 /**
401 * Edit thumbnail field row.
402 *
403 * @param WP_Term $term Current taxonomy term object.
404 */
405 public function edit_thumbnail_field( $term ) {
406 global $woocommerce;
407
408 $image = '';
409 $thumbnail_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
410 if ( $thumbnail_id ) {
411 $image = wp_get_attachment_url( $thumbnail_id );
412 }
413 if ( empty( $image ) ) {
414 $image = wc_placeholder_img_src();
415 }
416 ?>
417 <tr class="form-field">
418 <th scope="row" valign="top"><label><?php esc_html_e( 'Thumbnail', 'woocommerce' ); ?></label></th>
419 <td>
420 <div id="product_cat_thumbnail" style="float:left;margin-right:10px;"><img src="<?php echo esc_url( $image ); ?>" width="60px" height="60px" /></div>
421 <div style="line-height:60px;">
422 <input type="hidden" id="product_cat_thumbnail_id" name="product_cat_thumbnail_id" value="<?php echo esc_attr( $thumbnail_id ); ?>" />
423 <button type="button" class="upload_image_button button"><?php esc_html_e( 'Upload/Add image', 'woocommerce' ); ?></button>
424 <button type="button" class="remove_image_button button"><?php esc_html_e( 'Remove image', 'woocommerce' ); ?></button>
425 </div>
426 <script type="text/javascript">
427
428 jQuery(function(){
429
430 // Only show the "remove image" button when needed
431 if ( ! jQuery('#product_cat_thumbnail_id').val() )
432 jQuery('.remove_image_button').hide();
433
434 // Uploading files
435 var file_frame;
436
437 jQuery(document).on( 'click', '.upload_image_button', function( event ){
438
439 event.preventDefault();
440
441 // If the media frame already exists, reopen it.
442 if ( file_frame ) {
443 file_frame.open();
444 return;
445 }
446
447 // Create the media frame.
448 file_frame = wp.media.frames.downloadable_file = wp.media({
449 title: '<?php echo esc_js( __( 'Choose an image', 'woocommerce' ) ); ?>',
450 button: {
451 text: '<?php echo esc_js( __( 'Use image', 'woocommerce' ) ); ?>',
452 },
453 multiple: false
454 });
455
456 // When an image is selected, run a callback.
457 file_frame.on( 'select', function() {
458 attachment = file_frame.state().get('selection').first().toJSON();
459
460 jQuery('#product_cat_thumbnail_id').val( attachment.id );
461 jQuery('#product_cat_thumbnail img').attr('src', attachment.url );
462 jQuery('.remove_image_button').show();
463 });
464
465 // Finally, open the modal.
466 file_frame.open();
467 });
468
469 jQuery(document).on( 'click', '.remove_image_button', function( event ){
470 jQuery('#product_cat_thumbnail img').attr('src', '<?php echo esc_js( wc_placeholder_img_src() ); ?>');
471 jQuery('#product_cat_thumbnail_id').val('');
472 jQuery('.remove_image_button').hide();
473 return false;
474 });
475 });
476
477 </script>
478 <div class="clear"></div>
479 </td>
480 </tr>
481 <?php
482 }
483
484 /**
485 * Saves thumbnail field.
486 *
487 * @param int $term_id Term ID.
488 *
489 * @return void
490 */
491 public function thumbnail_field_save( $term_id ) {
492 if ( isset( $_POST['product_cat_thumbnail_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
493 update_term_meta( $term_id, 'thumbnail_id', absint( $_POST['product_cat_thumbnail_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
494 }
495 }
496
497 /**
498 * Brand taxonomy description.
499 */
500 public function taxonomy_description() {
501 echo wp_kses_post( wpautop( __( 'Brands can be added and managed from this screen. You can optionally upload a brand image to display in brand widgets and on brand archives', 'woocommerce' ) ) );
502 }
503
504 /**
505 * Sort brands function.
506 *
507 * @param array $sortable Sortable array.
508 */
509 public function sort_brands( $sortable ) {
510 $sortable[] = 'product_brand';
511 return $sortable;
512 }
513
514 /**
515 * Add brands column in second-to-last position.
516 *
517 * @since 9.4.0
518 * @param mixed $columns Columns.
519 * @return array
520 */
521 public function product_columns( $columns ) {
522 if ( empty( $columns ) ) {
523 return $columns;
524 }
525
526 $column_index = 'taxonomy-product_brand';
527 $brands_column = $columns[ $column_index ];
528 unset( $columns[ $column_index ] );
529 return array_merge(
530 array_slice( $columns, 0, -2, true ),
531 array( $column_index => $brands_column ),
532 array_slice( $columns, -2, null, true )
533 );
534 }
535
536
537 /**
538 * Columns function.
539 *
540 * @param mixed $columns Columns.
541 */
542 public function columns( $columns ) {
543 if ( empty( $columns ) ) {
544 return $columns;
545 }
546
547 $new_columns = array();
548 $new_columns['cb'] = $columns['cb'];
549 $new_columns['thumb'] = __( 'Image', 'woocommerce' );
550 unset( $columns['cb'] );
551 $columns = array_merge( $new_columns, $columns );
552 return $columns;
553 }
554
555 /**
556 * Column function.
557 *
558 * @param mixed $columns Columns.
559 * @param mixed $column Column.
560 * @param mixed $id ID.
561 */
562 public function column( $columns, $column, $id ) {
563 if ( 'thumb' === $column ) {
564 global $woocommerce;
565
566 $image = '';
567 $thumbnail_id = get_term_meta( $id, 'thumbnail_id', true );
568
569 if ( $thumbnail_id ) {
570 $image = wp_get_attachment_url( $thumbnail_id );
571 }
572 if ( empty( $image ) ) {
573 $image = wc_placeholder_img_src();
574 }
575
576 $columns .= '<img src="' . $image . '" alt="Thumbnail" class="wp-post-image" height="48" width="48" />';
577
578 }
579 return $columns;
580 }
581
582 /**
583 * Renders either dropdown or a search field for brands depending on the threshold value of
584 * woocommerce_product_brand_filter_threshold filter.
585 */
586 public function render_product_brand_filter() {
587 // phpcs:disable WordPress.Security.NonceVerification
588 $brands_count = (int) wp_count_terms( 'product_brand' );
589 $current_brand_slug = wc_clean( wp_unslash( $_GET['product_brand'] ?? '' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
590
591 /**
592 * Filter the brands threshold count.
593 *
594 * @since 9.4.0
595 *
596 * @param int $value Threshold.
597 */
598 if ( $brands_count <= apply_filters( 'woocommerce_product_brand_filter_threshold', 100 ) ) {
599 wc_product_dropdown_categories(
600 array(
601 'pad_counts' => true,
602 'show_count' => true,
603 'orderby' => 'name',
604 'selected' => $current_brand_slug,
605 'show_option_none' => __( 'Filter by brand', 'woocommerce' ),
606 'option_none_value' => '',
607 'value_field' => 'slug',
608 'taxonomy' => 'product_brand',
609 'name' => 'product_brand',
610 'class' => 'dropdown_product_brand',
611 )
612 );
613 } else {
614 $current_brand = $current_brand_slug ? get_term_by( 'slug', $current_brand_slug, 'product_brand' ) : '';
615 $selected_option = '';
616 if ( $current_brand_slug && $current_brand ) {
617 $selected_option = '<option value="' . esc_attr( $current_brand_slug ) . '" selected="selected">' . esc_html( htmlspecialchars( wp_kses_post( $current_brand->name ) ) ) . '</option>';
618 }
619 $placeholder = esc_attr__( 'Filter by brand', 'woocommerce' );
620 ?>
621 <select class="wc-brands-search" name="product_brand" data-placeholder="<?php echo $placeholder; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" data-allow_clear="true">
622 <?php echo $selected_option; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
623 </select>
624 <?php
625 }
626 // phpcs:enable WordPress.Security.NonceVerification
627 }
628
629 /**
630 * Add brand base permalink setting.
631 */
632 public function add_brand_base_setting() {
633 $screen = get_current_screen();
634 if ( ! $screen || 'options-permalink' !== $screen->id ) {
635 return;
636 }
637
638 add_settings_field(
639 'woocommerce_product_brand_slug',
640 __( 'Product brand base', 'woocommerce' ),
641 array( $this, 'product_brand_slug_input' ),
642 'permalink',
643 'optional'
644 );
645
646 $this->save_permalink_settings();
647 }
648
649 /**
650 * Add a slug input box.
651 */
652 public function product_brand_slug_input() {
653 $permalink = get_option( 'woocommerce_brand_permalink', '' );
654 ?>
655 <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' ); ?>" />
656 <?php
657 }
658
659 /**
660 * Save permalink settings.
661 *
662 * We need to save the options ourselves;
663 * settings api does not trigger save for the permalink page.
664 */
665 public function save_permalink_settings() {
666 if ( ! is_admin() ) {
667 return;
668 }
669
670 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
671 update_option( 'woocommerce_brand_permalink', wc_sanitize_permalink( trim( wc_clean( wp_unslash( $_POST['woocommerce_product_brand_slug'] ) ) ) ) );
672 }
673 }
674
675 /**
676 * Validate the product base.
677 *
678 * Must have an additional slug, not just the brand as the base.
679 *
680 * @param array $value Value.
681 */
682 public function validate_product_base( $value ) {
683 if ( '/%product_brand%/' === trailingslashit( $value['product_base'] ) ) {
684 $value['product_base'] = '/' . _x( 'product', 'slug', 'woocommerce' ) . $value['product_base'];
685 }
686
687 return $value;
688 }
689
690 /**
691 * Add csv column for importing/exporting.
692 *
693 * @param array $options Mapping options.
694 * @return array $options
695 */
696 public function add_column_to_importer_exporter( $options ) {
697 $options['brand_ids'] = __( 'Brands', 'woocommerce' );
698 return $options;
699 }
700
701 /**
702 * Add default column mapping.
703 *
704 * @param array $mappings Mappings.
705 * @return array $mappings
706 */
707 public function add_default_column_mapping( $mappings ) {
708 $new_mapping = array( __( 'Brands', 'woocommerce' ) => 'brand_ids' );
709 return array_merge( $mappings, $new_mapping );
710 }
711
712 /**
713 * Add formatting callback for brand_ids during CSV import.
714 *
715 * @param array $callbacks Formatting callbacks.
716 * @param WC_Product_Importer $importer Importer instance.
717 * @return array $callbacks
718 */
719 public function add_formatting_callback( $callbacks, $importer ) {
720 $mapped_keys = $importer->get_mapped_keys();
721
722 // Find the index of brand_ids in the mapped keys.
723 $brand_ids_index = array_search( 'brand_ids', $mapped_keys, true );
724
725 // If brand_ids exists in the mapping, add our custom parser.
726 if ( false !== $brand_ids_index ) {
727 $callbacks[ $brand_ids_index ] = array( $this, 'parse_brands_field' );
728 }
729
730 return $callbacks;
731 }
732
733 /**
734 * Add brands to newly imported product.
735 *
736 * @param WC_Product $product Product being imported.
737 * @param array $data Raw CSV data.
738 */
739 public function process_import( $product, $data ) {
740 if ( empty( $data['brand_ids'] ) || ! is_array( $data['brand_ids'] ) ) {
741 return;
742 }
743
744 $brand_ids = array_map( 'intval', $data['brand_ids'] );
745 wp_set_object_terms( $product->get_id(), $brand_ids, 'product_brand' );
746 }
747
748 /**
749 * Parse brands field from a CSV during import.
750 *
751 * Based on WC_Product_CSV_Importer::parse_categories_field()
752 *
753 * @param string $value Field value.
754 * @return array
755 */
756 public function parse_brands_field( $value ) {
757
758 if ( empty( $value ) ) {
759 return array();
760 }
761
762 // Based on WC_Product_Importer::explode_values().
763 $values = str_replace( '\\,', '::separator::', explode( ',', $value ) );
764 $row_terms = array();
765 foreach ( $values as $row_value ) {
766 $row_terms[] = trim( str_replace( '::separator::', ',', $row_value ) );
767 }
768
769 $brands = array();
770 foreach ( $row_terms as $row_term ) {
771 $parent = null;
772 $_terms = array_map( 'trim', explode( '>', $row_term ) );
773 $total = count( $_terms );
774
775 foreach ( $_terms as $index => $_term ) {
776 // Don't allow users without capabilities to create new brands.
777 if ( ! current_user_can( 'manage_product_terms' ) ) {
778 break;
779 }
780
781 $term = term_exists( $_term, 'product_brand', $parent );
782
783 if ( is_array( $term ) ) {
784 $term_id = $term['term_id'];
785 } else {
786 $term = wp_insert_term( $_term, 'product_brand', array( 'parent' => intval( $parent ) ) );
787
788 if ( is_wp_error( $term ) ) {
789 break; // We cannot continue if the term cannot be inserted.
790 }
791
792 $term_id = $term['term_id'];
793 }
794
795 // Only requires assign the last category.
796 if ( ( 1 + $index ) === $total ) {
797 $brands[] = $term_id;
798 } else {
799 // Store parent to be able to insert or query brands based in parent ID.
800 $parent = $term_id;
801 }
802 }
803 }
804
805 return $brands;
806 }
807
808 /**
809 * Get brands column value for csv export.
810 *
811 * @param string $value What will be exported.
812 * @param WC_Product $product Product being exported.
813 * @return string Brands separated by commas and child brands as "parent > child".
814 */
815 public function get_column_value_brand_ids( $value, $product ) {
816 $brand_ids = wp_parse_id_list( wp_get_post_terms( $product->get_id(), 'product_brand', array( 'fields' => 'ids' ) ) );
817
818 if ( ! count( $brand_ids ) ) {
819 return '';
820 }
821
822 // Based on WC_CSV_Exporter::format_term_ids().
823 $formatted_brands = array();
824 foreach ( $brand_ids as $brand_id ) {
825 $formatted_term = array();
826 $ancestor_ids = array_reverse( get_ancestors( $brand_id, 'product_brand' ) );
827
828 foreach ( $ancestor_ids as $ancestor_id ) {
829 $term = get_term( $ancestor_id, 'product_brand' );
830 if ( $term && ! is_wp_error( $term ) ) {
831 $formatted_term[] = $term->name;
832 }
833 }
834
835 $term = get_term( $brand_id, 'product_brand' );
836
837 if ( $term && ! is_wp_error( $term ) ) {
838 $formatted_term[] = $term->name;
839 }
840
841 $formatted_brands[] = implode( ' > ', $formatted_term );
842 }
843
844 // Based on WC_CSV_Exporter::implode_values().
845 $values_to_implode = array();
846 foreach ( $formatted_brands as $brand ) {
847 $brand = (string) is_scalar( $brand ) ? $brand : '';
848 $values_to_implode[] = str_replace( ',', '\\,', $brand );
849 }
850
851 return implode( ', ', $values_to_implode );
852 }
853 }
854
855 $GLOBALS['WC_Brands_Admin'] = new WC_Brands_Admin();
856