PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 8.8.7
WooCommerce v8.8.7
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-post-types.php
woocommerce / includes / admin Last commit date
helper 2 years ago importers 2 years ago list-tables 2 years ago marketplace-suggestions 2 years ago meta-boxes 2 years ago notes 2 years ago plugin-updates 2 years ago reports 2 years ago settings 2 years ago views 2 years ago class-wc-admin-addons.php 2 years ago class-wc-admin-api-keys-table-list.php 2 years ago class-wc-admin-api-keys.php 2 years ago class-wc-admin-assets.php 2 years ago class-wc-admin-attributes.php 3 years ago class-wc-admin-customize.php 5 years ago class-wc-admin-dashboard-setup.php 2 years ago class-wc-admin-dashboard.php 2 years ago class-wc-admin-duplicate-product.php 5 years ago class-wc-admin-exporters.php 3 years ago class-wc-admin-help.php 2 years ago class-wc-admin-importers.php 2 years ago class-wc-admin-log-table-list.php 2 years ago class-wc-admin-marketplace-promotions.php 2 years ago class-wc-admin-menus.php 2 years ago class-wc-admin-meta-boxes.php 2 years ago class-wc-admin-notices.php 2 years ago class-wc-admin-permalink-settings.php 5 years ago class-wc-admin-pointers.php 3 years ago class-wc-admin-post-types.php 2 years ago class-wc-admin-profile.php 2 years ago class-wc-admin-reports.php 5 years ago class-wc-admin-settings.php 2 years ago class-wc-admin-setup-wizard.php 2 years ago class-wc-admin-status.php 2 years ago class-wc-admin-taxonomies.php 3 years ago class-wc-admin-upload-downloadable-product.php 2 years ago class-wc-admin-webhooks-table-list.php 2 years ago class-wc-admin-webhooks.php 2 years ago class-wc-admin.php 2 years ago wc-admin-functions.php 2 years ago wc-meta-box-functions.php 2 years ago
class-wc-admin-post-types.php
934 lines
1 <?php
2 /**
3 * Post Types Admin
4 *
5 * @package WooCommerce\Admin
6 * @version 3.3.0
7 */
8
9 use Automattic\Jetpack\Constants;
10 use Automattic\WooCommerce\Utilities\NumberUtil;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 if ( class_exists( 'WC_Admin_Post_Types', false ) ) {
17 new WC_Admin_Post_Types();
18 return;
19 }
20
21 /**
22 * WC_Admin_Post_Types Class.
23 *
24 * Handles the edit posts views and some functionality on the edit post screen for WC post types.
25 */
26 class WC_Admin_Post_Types {
27
28 /**
29 * Constructor.
30 */
31 public function __construct() {
32 include_once __DIR__ . '/class-wc-admin-meta-boxes.php';
33
34 if ( ! function_exists( 'duplicate_post_plugin_activation' ) ) {
35 include_once __DIR__ . '/class-wc-admin-duplicate-product.php';
36 }
37
38 // Load correct list table classes for current screen.
39 add_action( 'current_screen', array( $this, 'setup_screen' ) );
40 add_action( 'check_ajax_referer', array( $this, 'setup_screen' ) );
41
42 // Admin notices.
43 add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
44 add_filter( 'woocommerce_order_updated_messages', array( $this, 'order_updated_messages' ) );
45 add_filter( 'bulk_post_updated_messages', array( $this, 'bulk_post_updated_messages' ), 10, 2 );
46
47 // Disable Auto Save.
48 add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) );
49
50 // Extra post data and screen elements.
51 add_action( 'edit_form_top', array( $this, 'edit_form_top' ) );
52 add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 );
53 add_action( 'edit_form_after_title', array( $this, 'edit_form_after_title' ) );
54 add_filter( 'default_hidden_meta_boxes', array( $this, 'hidden_meta_boxes' ), 10, 2 );
55 add_action( 'post_submitbox_misc_actions', array( $this, 'product_data_visibility' ) );
56
57 include_once __DIR__ . '/class-wc-admin-upload-downloadable-product.php';
58
59 // Hide template for CPT archive.
60 add_filter( 'theme_page_templates', array( $this, 'hide_cpt_archive_templates' ), 10, 3 );
61 add_action( 'edit_form_top', array( $this, 'show_cpt_archive_notice' ) );
62
63 // Add a post display state for special WC pages.
64 add_filter( 'display_post_states', array( $this, 'add_display_post_states' ), 10, 2 );
65
66 // Bulk / quick edit.
67 add_action( 'bulk_edit_custom_box', array( $this, 'bulk_edit' ), 10, 2 );
68 add_action( 'quick_edit_custom_box', array( $this, 'quick_edit' ), 10, 2 );
69 add_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ), 10, 2 );
70 add_action( 'woocommerce_product_bulk_and_quick_edit', array( $this, 'bulk_and_quick_edit_save_post' ), 10, 2 );
71 }
72
73 /**
74 * Looks at the current screen and loads the correct list table handler.
75 *
76 * @since 3.3.0
77 */
78 public function setup_screen() {
79 global $wc_list_table;
80
81 $request_data = $this->request_data();
82
83 $screen_id = false;
84
85 if ( function_exists( 'get_current_screen' ) ) {
86 $screen = get_current_screen();
87 $screen_id = isset( $screen, $screen->id ) ? $screen->id : '';
88 }
89
90 if ( ! empty( $request_data['screen'] ) ) {
91 $screen_id = wc_clean( wp_unslash( $request_data['screen'] ) );
92 }
93
94 switch ( $screen_id ) {
95 case 'edit-shop_order':
96 include_once __DIR__ . '/list-tables/class-wc-admin-list-table-orders.php';
97 $wc_list_table = new WC_Admin_List_Table_Orders();
98 break;
99 case 'edit-shop_coupon':
100 include_once __DIR__ . '/list-tables/class-wc-admin-list-table-coupons.php';
101 $wc_list_table = new WC_Admin_List_Table_Coupons();
102 break;
103 case 'edit-product':
104 include_once __DIR__ . '/list-tables/class-wc-admin-list-table-products.php';
105 $wc_list_table = new WC_Admin_List_Table_Products();
106 break;
107 }
108
109 // Ensure the table handler is only loaded once. Prevents multiple loads if a plugin calls check_ajax_referer many times.
110 remove_action( 'current_screen', array( $this, 'setup_screen' ) );
111 remove_action( 'check_ajax_referer', array( $this, 'setup_screen' ) );
112 }
113
114 /**
115 * Change messages when a post type is updated.
116 *
117 * @param array $messages Array of messages.
118 * @return array
119 */
120 public function post_updated_messages( $messages ) {
121 global $post;
122
123 $messages['product'] = array(
124 0 => '', // Unused. Messages start at index 1.
125 /* translators: %1$s: Product link opening tag. %2$s: Product link closing tag.*/
126 1 => sprintf( __( 'Product updated. %1$sView Product%2$s', 'woocommerce' ), '<a id="woocommerce-product-updated-message-view-product__link" href="' . esc_url( get_permalink( $post->ID ) ) . '">', '</a>' ),
127 2 => __( 'Custom field updated.', 'woocommerce' ),
128 3 => __( 'Custom field deleted.', 'woocommerce' ),
129 4 => __( 'Product updated.', 'woocommerce' ),
130 5 => __( 'Revision restored.', 'woocommerce' ),
131 /* translators: %1$s: Product link opening tag. %2$s: Product link closing tag.*/
132 6 => sprintf( __( 'Product published. %1$sView Product%2$s', 'woocommerce' ), '<a id="woocommerce-product-updated-message-view-product__link" href="' . esc_url( get_permalink( $post->ID ) ) . '">', '</a>' ),
133 7 => __( 'Product saved.', 'woocommerce' ),
134 /* translators: %s: product url */
135 8 => sprintf( __( 'Product submitted. <a target="_blank" href="%s">Preview product</a>', 'woocommerce' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
136 9 => sprintf(
137 /* translators: 1: date 2: product url */
138 __( 'Product scheduled for: %1$s. <a target="_blank" href="%2$s">Preview product</a>', 'woocommerce' ),
139 '<strong>' . date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) . '</strong>',
140 esc_url( get_permalink( $post->ID ) )
141 ),
142 /* translators: %s: product url */
143 10 => sprintf( __( 'Product draft updated. <a target="_blank" href="%s">Preview product</a>', 'woocommerce' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
144 );
145
146 $messages = $this->order_updated_messages( $messages );
147
148 $messages['shop_coupon'] = array(
149 0 => '', // Unused. Messages start at index 1.
150 1 => __( 'Coupon updated.', 'woocommerce' ),
151 2 => __( 'Custom field updated.', 'woocommerce' ),
152 3 => __( 'Custom field deleted.', 'woocommerce' ),
153 4 => __( 'Coupon updated.', 'woocommerce' ),
154 5 => __( 'Revision restored.', 'woocommerce' ),
155 6 => __( 'Coupon updated.', 'woocommerce' ),
156 7 => __( 'Coupon saved.', 'woocommerce' ),
157 8 => __( 'Coupon submitted.', 'woocommerce' ),
158 9 => sprintf(
159 /* translators: %s: date */
160 __( 'Coupon scheduled for: %s.', 'woocommerce' ),
161 '<strong>' . date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) . '</strong>'
162 ),
163 10 => __( 'Coupon draft updated.', 'woocommerce' ),
164 );
165
166 return $messages;
167 }
168
169 /**
170 * Add messages when an order is updated.
171 *
172 * @param array $messages Array of messages.
173 *
174 * @return array
175 */
176 public function order_updated_messages( array $messages ) {
177 global $post, $theorder;
178
179 if ( ! isset( $theorder ) || ! $theorder instanceof WC_Abstract_Order ) {
180 if ( ! isset( $post ) || 'shop_order' !== $post->post_type ) {
181 return $messages;
182 } else {
183 \Automattic\WooCommerce\Utilities\OrderUtil::init_theorder_object( $post );
184 }
185 }
186
187 $messages['shop_order'] = array(
188 0 => '', // Unused. Messages start at index 1.
189 1 => __( 'Order updated.', 'woocommerce' ),
190 2 => __( 'Custom field updated.', 'woocommerce' ),
191 3 => __( 'Custom field deleted.', 'woocommerce' ),
192 4 => __( 'Order updated.', 'woocommerce' ),
193 5 => __( 'Revision restored.', 'woocommerce' ),
194 6 => __( 'Order updated.', 'woocommerce' ),
195 7 => __( 'Order saved.', 'woocommerce' ),
196 8 => __( 'Order submitted.', 'woocommerce' ),
197 9 => sprintf(
198 /* translators: %s: date */
199 __( 'Order scheduled for: %s.', 'woocommerce' ),
200 '<strong>' . date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $theorder->get_date_created() ?? $post->post_date ) ) . '</strong>'
201 ),
202 10 => __( 'Order draft updated.', 'woocommerce' ),
203 11 => __( 'Order updated and sent.', 'woocommerce' ),
204 );
205
206 return $messages;
207 }
208
209 /**
210 * Specify custom bulk actions messages for different post types.
211 *
212 * @param array $bulk_messages Array of messages.
213 * @param array $bulk_counts Array of how many objects were updated.
214 * @return array
215 */
216 public function bulk_post_updated_messages( $bulk_messages, $bulk_counts ) {
217 $bulk_messages['product'] = array(
218 /* translators: %s: product count */
219 'updated' => _n( '%s product updated.', '%s products updated.', $bulk_counts['updated'], 'woocommerce' ),
220 /* translators: %s: product count */
221 'locked' => _n( '%s product not updated, somebody is editing it.', '%s products not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),
222 /* translators: %s: product count */
223 'deleted' => _n( '%s product permanently deleted.', '%s products permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),
224 /* translators: %s: product count */
225 'trashed' => _n( '%s product moved to the Trash.', '%s products moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),
226 /* translators: %s: product count */
227 'untrashed' => _n( '%s product restored from the Trash.', '%s products restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),
228 );
229
230 $bulk_messages['shop_order'] = array(
231 /* translators: %s: order count */
232 'updated' => _n( '%s order updated.', '%s orders updated.', $bulk_counts['updated'], 'woocommerce' ),
233 /* translators: %s: order count */
234 'locked' => _n( '%s order not updated, somebody is editing it.', '%s orders not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),
235 /* translators: %s: order count */
236 'deleted' => _n( '%s order permanently deleted.', '%s orders permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),
237 /* translators: %s: order count */
238 'trashed' => _n( '%s order moved to the Trash.', '%s orders moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),
239 /* translators: %s: order count */
240 'untrashed' => _n( '%s order restored from the Trash.', '%s orders restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),
241 );
242
243 $bulk_messages['shop_coupon'] = array(
244 /* translators: %s: coupon count */
245 'updated' => _n( '%s coupon updated.', '%s coupons updated.', $bulk_counts['updated'], 'woocommerce' ),
246 /* translators: %s: coupon count */
247 'locked' => _n( '%s coupon not updated, somebody is editing it.', '%s coupons not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),
248 /* translators: %s: coupon count */
249 'deleted' => _n( '%s coupon permanently deleted.', '%s coupons permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),
250 /* translators: %s: coupon count */
251 'trashed' => _n( '%s coupon moved to the Trash.', '%s coupons moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),
252 /* translators: %s: coupon count */
253 'untrashed' => _n( '%s coupon restored from the Trash.', '%s coupons restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),
254 );
255
256 return $bulk_messages;
257 }
258
259 /**
260 * Custom bulk edit - form.
261 *
262 * @param string $column_name Column being shown.
263 * @param string $post_type Post type being shown.
264 */
265 public function bulk_edit( $column_name, $post_type ) {
266 if ( 'price' !== $column_name || 'product' !== $post_type ) {
267 return;
268 }
269
270 $shipping_class = get_terms(
271 'product_shipping_class',
272 array(
273 'hide_empty' => false,
274 )
275 );
276
277 include WC()->plugin_path() . '/includes/admin/views/html-bulk-edit-product.php';
278 }
279
280 /**
281 * Custom quick edit - form.
282 *
283 * @param string $column_name Column being shown.
284 * @param string $post_type Post type being shown.
285 */
286 public function quick_edit( $column_name, $post_type ) {
287 if ( 'price' !== $column_name || 'product' !== $post_type ) {
288 return;
289 }
290
291 $shipping_class = get_terms(
292 'product_shipping_class',
293 array(
294 'hide_empty' => false,
295 )
296 );
297
298 include WC()->plugin_path() . '/includes/admin/views/html-quick-edit-product.php';
299 }
300
301 /**
302 * Offers a way to hook into save post without causing an infinite loop
303 * when quick/bulk saving product info.
304 *
305 * @since 3.0.0
306 * @param int $post_id Post ID being saved.
307 * @param object $post Post object being saved.
308 */
309 public function bulk_and_quick_edit_hook( $post_id, $post ) {
310 remove_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ) );
311 do_action( 'woocommerce_product_bulk_and_quick_edit', $post_id, $post );
312 add_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ), 10, 2 );
313 }
314
315 /**
316 * Quick and bulk edit saving.
317 *
318 * @param int $post_id Post ID being saved.
319 * @param object $post Post object being saved.
320 * @return int
321 */
322 public function bulk_and_quick_edit_save_post( $post_id, $post ) {
323 $request_data = $this->request_data();
324
325 // If this is an autosave, our form has not been submitted, so we don't want to do anything.
326 if ( Constants::is_true( 'DOING_AUTOSAVE' ) ) {
327 return $post_id;
328 }
329
330 // Don't save revisions and autosaves.
331 if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || 'product' !== $post->post_type || ! current_user_can( 'edit_post', $post_id ) ) {
332 return $post_id;
333 }
334
335 // Check nonce.
336 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
337 if ( ! isset( $request_data['woocommerce_quick_edit_nonce'] ) || ! wp_verify_nonce( $request_data['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) {
338 return $post_id;
339 }
340
341 // Get the product and save.
342 $product = wc_get_product( $post );
343
344 if ( ! empty( $request_data['woocommerce_quick_edit'] ) ) { // WPCS: input var ok.
345 $this->quick_edit_save( $post_id, $product );
346 } else {
347 $this->bulk_edit_save( $post_id, $product );
348 }
349
350 return $post_id;
351 }
352
353 /**
354 * Quick edit.
355 *
356 * @param int $post_id Post ID being saved.
357 * @param WC_Product $product Product object.
358 */
359 private function quick_edit_save( $post_id, $product ) {
360 $request_data = $this->request_data();
361
362 $data_store = $product->get_data_store();
363 $old_regular_price = $product->get_regular_price();
364 $old_sale_price = $product->get_sale_price();
365 $input_to_props = array(
366 '_weight' => 'weight',
367 '_length' => 'length',
368 '_width' => 'width',
369 '_height' => 'height',
370 '_visibility' => 'catalog_visibility',
371 '_tax_class' => 'tax_class',
372 '_tax_status' => 'tax_status',
373 );
374
375 foreach ( $input_to_props as $input_var => $prop ) {
376 if ( isset( $request_data[ $input_var ] ) ) {
377 $product->{"set_{$prop}"}( wc_clean( wp_unslash( $request_data[ $input_var ] ) ) );
378 }
379 }
380
381 if ( isset( $request_data['_sku'] ) ) {
382 $sku = $product->get_sku();
383 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
384 $new_sku = (string) wc_clean( $request_data['_sku'] );
385
386 if ( $new_sku !== $sku ) {
387 if ( ! empty( $new_sku ) ) {
388 $unique_sku = wc_product_has_unique_sku( $post_id, $new_sku );
389 if ( $unique_sku ) {
390 $product->set_sku( wc_clean( wp_unslash( $new_sku ) ) );
391 }
392 } else {
393 $product->set_sku( '' );
394 }
395 }
396 }
397
398 if ( ! empty( $request_data['_shipping_class'] ) ) {
399 if ( '_no_shipping_class' === $request_data['_shipping_class'] ) {
400 $product->set_shipping_class_id( 0 );
401 } else {
402 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
403 $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $request_data['_shipping_class'] ) );
404 $product->set_shipping_class_id( $shipping_class_id );
405 }
406 }
407
408 if ( ! empty( $request_data['_tax_class'] ) ) {
409 $tax_class = sanitize_title( wp_unslash( $request_data['_tax_class'] ) );
410 if ( 'standard' === $tax_class ) {
411 $tax_class = '';
412 }
413 $product->set_tax_class( $tax_class );
414 }
415
416 $product->set_featured( isset( $request_data['_featured'] ) );
417
418 if ( $product->is_type( 'simple' ) || $product->is_type( 'external' ) ) {
419
420 if ( isset( $request_data['_regular_price'] ) ) {
421 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
422 $new_regular_price = ( '' === $request_data['_regular_price'] ) ? '' : wc_format_decimal( $request_data['_regular_price'] );
423 $product->set_regular_price( $new_regular_price );
424 } else {
425 $new_regular_price = null;
426 }
427 if ( isset( $request_data['_sale_price'] ) ) {
428 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
429 $new_sale_price = ( '' === $request_data['_sale_price'] ) ? '' : wc_format_decimal( $request_data['_sale_price'] );
430 $product->set_sale_price( $new_sale_price );
431 } else {
432 $new_sale_price = null;
433 }
434
435 // Handle price - remove dates and set to lowest.
436 $price_changed = false;
437
438 if ( ! is_null( $new_regular_price ) && $new_regular_price !== $old_regular_price ) {
439 $price_changed = true;
440 } elseif ( ! is_null( $new_sale_price ) && $new_sale_price !== $old_sale_price ) {
441 $price_changed = true;
442 }
443
444 if ( $price_changed ) {
445 $product->set_date_on_sale_to( '' );
446 $product->set_date_on_sale_from( '' );
447 }
448 }
449
450 // Handle Stock Data.
451 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
452 $manage_stock = ! empty( $request_data['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no';
453 $backorders = ! empty( $request_data['_backorders'] ) ? wc_clean( $request_data['_backorders'] ) : 'no';
454 if ( ! empty( $request_data['_stock_status'] ) ) {
455 $stock_status = wc_clean( $request_data['_stock_status'] );
456 } else {
457 $stock_status = $product->is_type( 'variable' ) ? null : 'instock';
458 }
459 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
460
461 $product->set_manage_stock( $manage_stock );
462
463 if ( 'external' !== $product->get_type() ) {
464 $product->set_backorders( $backorders );
465 }
466
467 if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
468 $stock_amount = 'yes' === $manage_stock && isset( $request_data['_stock'] ) && is_numeric( wp_unslash( $request_data['_stock'] ) ) ? wc_stock_amount( wp_unslash( $request_data['_stock'] ) ) : '';
469 $product->set_stock_quantity( $stock_amount );
470 }
471
472 $product = $this->maybe_update_stock_status( $product, $stock_status );
473
474 $product->save();
475
476 do_action( 'woocommerce_product_quick_edit_save', $product );
477 }
478
479 /**
480 * Bulk edit.
481 *
482 * @param int $post_id Post ID being saved.
483 * @param WC_Product $product Product object.
484 */
485 public function bulk_edit_save( $post_id, $product ) {
486 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
487
488 $request_data = $this->request_data();
489
490 $data_store = $product->get_data_store();
491
492 if ( ! empty( $request_data['change_weight'] ) && isset( $request_data['_weight'] ) ) {
493 $product->set_weight( wc_clean( wp_unslash( $request_data['_weight'] ) ) );
494 }
495
496 if ( ! empty( $request_data['change_dimensions'] ) ) {
497 if ( isset( $request_data['_length'] ) ) {
498 $product->set_length( wc_clean( wp_unslash( $request_data['_length'] ) ) );
499 }
500 if ( isset( $request_data['_width'] ) ) {
501 $product->set_width( wc_clean( wp_unslash( $request_data['_width'] ) ) );
502 }
503 if ( isset( $request_data['_height'] ) ) {
504 $product->set_height( wc_clean( wp_unslash( $request_data['_height'] ) ) );
505 }
506 }
507
508 if ( ! empty( $request_data['_tax_status'] ) ) {
509 $product->set_tax_status( wc_clean( $request_data['_tax_status'] ) );
510 }
511
512 if ( ! empty( $request_data['_tax_class'] ) ) {
513 $tax_class = sanitize_title( wp_unslash( $request_data['_tax_class'] ) );
514 if ( 'standard' === $tax_class ) {
515 $tax_class = '';
516 }
517 $product->set_tax_class( $tax_class );
518 }
519
520 if ( ! empty( $request_data['_shipping_class'] ) ) {
521 if ( '_no_shipping_class' === $request_data['_shipping_class'] ) {
522 $product->set_shipping_class_id( 0 );
523 } else {
524 $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $request_data['_shipping_class'] ) );
525 $product->set_shipping_class_id( $shipping_class_id );
526 }
527 }
528
529 if ( ! empty( $request_data['_visibility'] ) ) {
530 $product->set_catalog_visibility( wc_clean( $request_data['_visibility'] ) );
531 }
532
533 if ( ! empty( $request_data['_featured'] ) ) {
534 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
535 $product->set_featured( wp_unslash( $request_data['_featured'] ) );
536 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
537 }
538
539 if ( ! empty( $request_data['_sold_individually'] ) ) {
540 if ( 'yes' === $request_data['_sold_individually'] ) {
541 $product->set_sold_individually( 'yes' );
542 } else {
543 $product->set_sold_individually( '' );
544 }
545 }
546
547 // Handle price - remove dates and set to lowest.
548 $change_price_product_types = apply_filters( 'woocommerce_bulk_edit_save_price_product_types', array( 'simple', 'external' ) );
549 $can_product_type_change_price = false;
550 foreach ( $change_price_product_types as $product_type ) {
551 if ( $product->is_type( $product_type ) ) {
552 $can_product_type_change_price = true;
553 break;
554 }
555 }
556
557 if ( $can_product_type_change_price ) {
558 $regular_price_changed = $this->set_new_price( $product, 'regular' );
559 $sale_price_changed = $this->set_new_price( $product, 'sale' );
560
561 if ( $regular_price_changed || $sale_price_changed ) {
562 $product->set_date_on_sale_to( '' );
563 $product->set_date_on_sale_from( '' );
564
565 if ( $product->get_regular_price() < $product->get_sale_price() ) {
566 $product->set_sale_price( '' );
567 }
568 }
569 }
570
571 // Handle Stock Data.
572 $was_managing_stock = $product->get_manage_stock() ? 'yes' : 'no';
573 $backorders = $product->get_backorders();
574 $backorders = ! empty( $request_data['_backorders'] ) ? wc_clean( $request_data['_backorders'] ) : $backorders;
575
576 if ( ! empty( $request_data['_manage_stock'] ) ) {
577 $manage_stock = 'yes' === wc_clean( $request_data['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no';
578 } else {
579 $manage_stock = $was_managing_stock;
580 }
581
582 $stock_amount = 'yes' === $manage_stock && ! empty( $request_data['change_stock'] ) && isset( $request_data['_stock'] ) ? wc_stock_amount( $request_data['_stock'] ) : $product->get_stock_quantity();
583
584 $product->set_manage_stock( $manage_stock );
585
586 if ( 'external' !== $product->get_type() ) {
587 $product->set_backorders( $backorders );
588 }
589
590 if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
591 $change_stock = absint( $request_data['change_stock'] );
592 switch ( $change_stock ) {
593 case 2:
594 wc_update_product_stock( $product, $stock_amount, 'increase', true );
595 break;
596 case 3:
597 wc_update_product_stock( $product, $stock_amount, 'decrease', true );
598 break;
599 default:
600 wc_update_product_stock( $product, $stock_amount, 'set', true );
601 break;
602 }
603 } else {
604 // Reset values if WooCommerce Setting - Manage Stock status is disabled.
605 $product->set_stock_quantity( '' );
606 $product->set_manage_stock( 'no' );
607 }
608
609 $stock_status = empty( $request_data['_stock_status'] ) ? null : wc_clean( $request_data['_stock_status'] );
610 $product = $this->maybe_update_stock_status( $product, $stock_status );
611
612 $product->save();
613
614 do_action( 'woocommerce_product_bulk_edit_save', $product );
615
616 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
617 }
618
619 /**
620 * Disable the auto-save functionality for Orders.
621 */
622 public function disable_autosave() {
623 global $post;
624
625 if ( $post instanceof WP_Post && in_array( get_post_type( $post->ID ), wc_get_order_types( 'order-meta-boxes' ), true ) ) {
626 wp_dequeue_script( 'autosave' );
627 }
628 }
629
630 /**
631 * Output extra data on post forms.
632 *
633 * @param WP_Post $post Current post object.
634 */
635 public function edit_form_top( $post ) {
636 echo '<input type="hidden" id="original_post_title" name="original_post_title" value="' . esc_attr( $post->post_title ) . '" />';
637 }
638
639 /**
640 * Change title boxes in admin.
641 *
642 * @param string $text Text to shown.
643 * @param WP_Post $post Current post object.
644 * @return string
645 */
646 public function enter_title_here( $text, $post ) {
647 switch ( $post->post_type ) {
648 case 'product':
649 $text = esc_html__( 'Product name', 'woocommerce' );
650 break;
651 case 'shop_coupon':
652 $text = esc_html__( 'Coupon code', 'woocommerce' );
653 break;
654 }
655 return $text;
656 }
657
658 /**
659 * Print coupon description textarea field.
660 *
661 * @param WP_Post $post Current post object.
662 */
663 public function edit_form_after_title( $post ) {
664 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
665 if ( 'shop_coupon' === $post->post_type ) {
666 ?>
667 <textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo $post->post_excerpt; ?></textarea>
668 <?php
669 }
670 // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
671 }
672
673 /**
674 * Hidden default Meta-Boxes.
675 *
676 * @param array $hidden Hidden boxes.
677 * @param object $screen Current screen.
678 * @return array
679 */
680 public function hidden_meta_boxes( $hidden, $screen ) {
681 if ( 'product' === $screen->post_type && 'post' === $screen->base ) {
682 $hidden = array_merge( $hidden, array( 'postcustom' ) );
683 }
684
685 return $hidden;
686 }
687
688 /**
689 * Output product visibility options.
690 */
691 public function product_data_visibility() {
692 global $post, $thepostid, $product_object;
693
694 if ( 'product' !== $post->post_type ) {
695 return;
696 }
697
698 $thepostid = $post->ID;
699 $product_object = $thepostid ? wc_get_product( $thepostid ) : new WC_Product();
700 $current_visibility = $product_object->get_catalog_visibility();
701 $current_featured = wc_bool_to_string( $product_object->get_featured() );
702 $visibility_options = wc_get_product_visibility_options();
703 ?>
704 <div class="misc-pub-section" id="catalog-visibility">
705 <?php esc_html_e( 'Catalog visibility:', 'woocommerce' ); ?>
706 <strong id="catalog-visibility-display">
707 <?php
708
709 echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility );
710
711 if ( 'yes' === $current_featured ) {
712 echo ', ' . esc_html__( 'Featured', 'woocommerce' );
713 }
714 ?>
715 </strong>
716
717 <a href="#catalog-visibility" class="edit-catalog-visibility hide-if-no-js"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a>
718
719 <div id="catalog-visibility-select" class="hide-if-js">
720
721 <input type="hidden" name="current_visibility" id="current_visibility" value="<?php echo esc_attr( $current_visibility ); ?>" />
722 <input type="hidden" name="current_featured" id="current_featured" value="<?php echo esc_attr( $current_featured ); ?>" />
723
724 <?php
725 echo '<p>' . esc_html__( 'This setting determines which shop pages products will be listed on.', 'woocommerce' ) . '</p>';
726
727 foreach ( $visibility_options as $name => $label ) {
728 echo '<input type="radio" name="_visibility" id="_visibility_' . esc_attr( $name ) . '" value="' . esc_attr( $name ) . '" ' . checked( $current_visibility, $name, false ) . ' data-label="' . esc_attr( $label ) . '" /> <label for="_visibility_' . esc_attr( $name ) . '" class="selectit">' . esc_html( $label ) . '</label><br />';
729 }
730
731 echo '<br /><input type="checkbox" name="_featured" id="_featured" ' . checked( $current_featured, 'yes', false ) . ' /> <label for="_featured">' . esc_html__( 'This is a featured product', 'woocommerce' ) . '</label><br />';
732 ?>
733 <p>
734 <a href="#catalog-visibility" class="save-post-visibility hide-if-no-js button"><?php esc_html_e( 'OK', 'woocommerce' ); ?></a>
735 <a href="#catalog-visibility" class="cancel-post-visibility hide-if-no-js"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></a>
736 </p>
737 </div>
738 </div>
739 <?php
740 }
741
742 /**
743 * Grant downloadable file access to any newly added files on any existing.
744 * orders for this product that have previously been granted downloadable file access.
745 *
746 * @param int $product_id product identifier.
747 * @param int $variation_id optional product variation identifier.
748 * @param array $downloadable_files newly set files.
749 * @deprecated 3.3.0 and moved to post-data class.
750 */
751 public function process_product_file_download_paths( $product_id, $variation_id, $downloadable_files ) {
752 wc_deprecated_function( 'WC_Admin_Post_Types::process_product_file_download_paths', '3.3', '' );
753 WC_Post_Data::process_product_file_download_paths( $product_id, $variation_id, $downloadable_files );
754 }
755
756 /**
757 * When editing the shop page, we should hide templates.
758 *
759 * @param array $page_templates Templates array.
760 * @param string $theme Classname.
761 * @param WP_Post $post The current post object.
762 * @return array
763 */
764 public function hide_cpt_archive_templates( $page_templates, $theme, $post ) {
765 $shop_page_id = wc_get_page_id( 'shop' );
766
767 if ( $post && absint( $post->ID ) === $shop_page_id ) {
768 $page_templates = array();
769 }
770
771 return $page_templates;
772 }
773
774 /**
775 * Show a notice above the CPT archive.
776 *
777 * @param WP_Post $post The current post object.
778 */
779 public function show_cpt_archive_notice( $post ) {
780 $shop_page_id = wc_get_page_id( 'shop' );
781
782 if ( $post && absint( $post->ID ) === $shop_page_id ) {
783 echo '<div class="notice notice-info">';
784 /* translators: %s: URL to read more about the shop page. */
785 echo '<p>' . sprintf( wp_kses_post( __( 'This is the WooCommerce shop page. The shop page is a special archive that lists your products. <a href="%s">You can read more about this here</a>.', 'woocommerce' ) ), 'https://woocommerce.com/document/woocommerce-pages/#section-4' ) . '</p>';
786 echo '</div>';
787 }
788 }
789
790 /**
791 * Add a post display state for special WC pages in the page list table.
792 *
793 * @param array $post_states An array of post display states.
794 * @param WP_Post $post The current post object.
795 */
796 public function add_display_post_states( $post_states, $post ) {
797 if ( wc_get_page_id( 'shop' ) === $post->ID ) {
798 $post_states['wc_page_for_shop'] = __( 'Shop Page', 'woocommerce' );
799 }
800
801 if ( wc_get_page_id( 'cart' ) === $post->ID ) {
802 $post_states['wc_page_for_cart'] = __( 'Cart Page', 'woocommerce' );
803 }
804
805 if ( wc_get_page_id( 'checkout' ) === $post->ID ) {
806 $post_states['wc_page_for_checkout'] = __( 'Checkout Page', 'woocommerce' );
807 }
808
809 if ( wc_get_page_id( 'myaccount' ) === $post->ID ) {
810 $post_states['wc_page_for_myaccount'] = __( 'My Account Page', 'woocommerce' );
811 }
812
813 if ( wc_get_page_id( 'terms' ) === $post->ID ) {
814 $post_states['wc_page_for_terms'] = __( 'Terms and Conditions Page', 'woocommerce' );
815 }
816
817 return $post_states;
818 }
819
820 /**
821 * Apply product type constraints to stock status.
822 *
823 * @param WC_Product $product The product whose stock status will be adjusted.
824 * @param string|null $stock_status The stock status to use for adjustment, or null if no new stock status has been supplied in the request.
825 * @return WC_Product The supplied product, or the synced product if it was a variable product.
826 */
827 private function maybe_update_stock_status( $product, $stock_status ) {
828 if ( $product->is_type( 'external' ) ) {
829 // External products are always in stock.
830 $product->set_stock_status( 'instock' );
831 } elseif ( isset( $stock_status ) ) {
832 if ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) {
833 // Stock status is determined by children.
834 foreach ( $product->get_children() as $child_id ) {
835 $child = wc_get_product( $child_id );
836 if ( ! $product->get_manage_stock() ) {
837 $child->set_stock_status( $stock_status );
838 $child->save();
839 }
840 }
841 $product = WC_Product_Variable::sync( $product, false );
842 } else {
843 $product->set_stock_status( $stock_status );
844 }
845 }
846
847 return $product;
848 }
849
850 /**
851 * Set the new regular or sale price if requested.
852 *
853 * @param WC_Product $product The product to set the new price for.
854 * @param string $price_type 'regular' or 'sale'.
855 * @return bool true if a new price has been set, false otherwise.
856 */
857 private function set_new_price( $product, $price_type ) {
858 // phpcs:disable WordPress.Security.NonceVerification.Recommended
859
860 $request_data = $this->request_data();
861
862 if ( empty( $request_data[ "change_{$price_type}_price" ] ) || ! isset( $request_data[ "_{$price_type}_price" ] ) ) {
863 return false;
864 }
865
866 $old_price = (float) $product->{"get_{$price_type}_price"}();
867 $price_changed = false;
868
869 $change_price = absint( $request_data[ "change_{$price_type}_price" ] );
870 $raw_price = wc_clean( wp_unslash( $request_data[ "_{$price_type}_price" ] ) );
871 $is_percentage = (bool) strstr( $raw_price, '%' );
872 $price = wc_format_decimal( $raw_price );
873
874 switch ( $change_price ) {
875 case 1:
876 $new_price = $price;
877 break;
878 case 2:
879 if ( $is_percentage ) {
880 $percent = $price / 100;
881 $new_price = $old_price + ( $old_price * $percent );
882 } else {
883 $new_price = $old_price + $price;
884 }
885 break;
886 case 3:
887 if ( $is_percentage ) {
888 $percent = $price / 100;
889 $new_price = max( 0, $old_price - ( $old_price * $percent ) );
890 } else {
891 $new_price = max( 0, $old_price - $price );
892 }
893 break;
894 case 4:
895 if ( 'sale' !== $price_type ) {
896 break;
897 }
898 $regular_price = $product->get_regular_price();
899 if ( $is_percentage && is_numeric( $regular_price ) ) {
900 $percent = $price / 100;
901 $new_price = max( 0, $regular_price - ( NumberUtil::round( $regular_price * $percent, wc_get_price_decimals() ) ) );
902 } else {
903 $new_price = max( 0, (float) $regular_price - (float) $price );
904 }
905 break;
906
907 default:
908 break;
909 }
910
911 if ( isset( $new_price ) && $new_price !== $old_price ) {
912 $price_changed = true;
913 $new_price = NumberUtil::round( $new_price, wc_get_price_decimals() );
914 $product->{"set_{$price_type}_price"}( $new_price );
915 }
916
917 return $price_changed;
918
919 // phpcs:disable WordPress.Security.NonceVerification.Recommended
920 }
921
922 /**
923 * Get the current request data ($_REQUEST superglobal).
924 * This method is added to ease unit testing.
925 *
926 * @return array The $_REQUEST superglobal.
927 */
928 protected function request_data() {
929 return $_REQUEST;
930 }
931 }
932
933 new WC_Admin_Post_Types();
934