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