WC_Admin_Post_Types

WC_Admin_Post_Types Class.

Defined (1)

The class is defined in the following location(s).

/includes/admin/class-wc-admin-post-types.php  
  1. class WC_Admin_Post_Types { 
  2.  
  3. /** 
  4. * Constructor. 
  5. */ 
  6. public function __construct() { 
  7. add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) ); 
  8. add_filter( 'bulk_post_updated_messages', array( $this, 'bulk_post_updated_messages' ), 10, 2 ); 
  9.  
  10. // Disable Auto Save 
  11. add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) ); 
  12.  
  13. // Extra post data. 
  14. add_action( 'edit_form_top', array( $this, 'edit_form_top' ) ); 
  15.  
  16. // WP List table columns. Defined here so they are always available for events such as inline editing. 
  17. add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ) ); 
  18. add_filter( 'manage_shop_coupon_posts_columns', array( $this, 'shop_coupon_columns' ) ); 
  19. add_filter( 'manage_shop_order_posts_columns', array( $this, 'shop_order_columns' ) ); 
  20.  
  21. add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_columns' ), 2 ); 
  22. add_action( 'manage_shop_coupon_posts_custom_column', array( $this, 'render_shop_coupon_columns' ), 2 ); 
  23. add_action( 'manage_shop_order_posts_custom_column', array( $this, 'render_shop_order_columns' ), 2 ); 
  24.  
  25. add_filter( 'manage_edit-product_sortable_columns', array( $this, 'product_sortable_columns' ) ); 
  26. add_filter( 'manage_edit-shop_coupon_sortable_columns', array( $this, 'shop_coupon_sortable_columns' ) ); 
  27. add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'shop_order_sortable_columns' ) ); 
  28.  
  29. add_filter( 'list_table_primary_column', array( $this, 'list_table_primary_column' ), 10, 2 ); 
  30. add_filter( 'post_row_actions', array( $this, 'row_actions' ), 2, 100 ); 
  31.  
  32. // Views 
  33. add_filter( 'views_edit-product', array( $this, 'product_sorting_link' ) ); 
  34.  
  35. // Bulk / quick edit 
  36. add_action( 'bulk_edit_custom_box', array( $this, 'bulk_edit' ), 10, 2 ); 
  37. add_action( 'quick_edit_custom_box', array( $this, 'quick_edit' ), 10, 2 ); 
  38. add_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ), 10, 2 ); 
  39. add_action( 'woocommerce_product_bulk_and_quick_edit', array( $this, 'bulk_and_quick_edit_save_post' ), 10, 2 ); 
  40. add_filter( 'bulk_actions-edit-shop_order', array( $this, 'shop_order_bulk_actions' ) ); 
  41. add_filter( 'handle_bulk_actions-edit-shop_order', array( $this, 'handle_shop_order_bulk_actions' ), 10, 3 ); 
  42. add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) ); 
  43.  
  44. // Order Search 
  45. add_filter( 'get_search_query', array( $this, 'shop_order_search_label' ) ); 
  46. add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) ); 
  47. add_action( 'parse_query', array( $this, 'shop_order_search_custom_fields' ) ); 
  48.  
  49. // Filters 
  50. add_action( 'restrict_manage_posts', array( $this, 'restrict_manage_posts' ) ); 
  51. add_filter( 'request', array( $this, 'request_query' ) ); 
  52. add_filter( 'parse_query', array( $this, 'product_filters_query' ) ); 
  53. add_filter( 'posts_search', array( $this, 'product_search' ) ); 
  54.  
  55. // Edit post screens 
  56. add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 ); 
  57. add_action( 'edit_form_after_title', array( $this, 'edit_form_after_title' ) ); 
  58. add_filter( 'default_hidden_meta_boxes', array( $this, 'hidden_meta_boxes' ), 10, 2 ); 
  59. add_action( 'post_submitbox_misc_actions', array( $this, 'product_data_visibility' ) ); 
  60.  
  61. // Uploads 
  62. add_filter( 'upload_dir', array( $this, 'upload_dir' ) ); 
  63. add_action( 'media_upload_downloadable_product', array( $this, 'media_upload_downloadable_product' ) ); 
  64.  
  65. if ( ! function_exists( 'duplicate_post_plugin_activation' ) ) { 
  66. include( 'class-wc-admin-duplicate-product.php' ); 
  67.  
  68. include_once( dirname( __FILE__ ) . '/class-wc-admin-meta-boxes.php' ); 
  69.  
  70. // Disable DFW feature pointer 
  71. add_action( 'admin_footer', array( $this, 'disable_dfw_feature_pointer' ) ); 
  72.  
  73. // Disable post type view mode options 
  74. add_filter( 'view_mode_post_types', array( $this, 'disable_view_mode_options' ) ); 
  75.  
  76. // Update the screen options. 
  77. add_filter( 'default_hidden_columns', array( $this, 'adjust_shop_order_columns' ), 10, 2 ); 
  78.  
  79. // Show blank state 
  80. add_action( 'manage_posts_extra_tablenav', array( $this, 'maybe_render_blank_state' ) ); 
  81.  
  82. // Hide template for CPT archive. 
  83. add_filter( 'theme_page_templates', array( $this, 'hide_cpt_archive_templates' ), 10, 3 ); 
  84. add_action( 'edit_form_top', array( $this, 'show_cpt_archive_notice' ) ); 
  85.  
  86. /** 
  87. * Adjust shop order columns for the user on certain conditions. 
  88. */ 
  89. public function adjust_shop_order_columns( $hidden, $screen ) { 
  90. if ( isset( $screen->id ) && 'edit-shop_order' === $screen->id ) { 
  91. if ( 'disabled' === get_option( 'woocommerce_ship_to_countries' ) ) { 
  92. $hidden[] = 'shipping_address'; 
  93. } else { 
  94. $hidden[] = 'billing_address'; 
  95. return $hidden; 
  96.  
  97. /** 
  98. * Change messages when a post type is updated. 
  99. * @param array $messages 
  100. * @return array 
  101. */ 
  102. public function post_updated_messages( $messages ) { 
  103. global $post, $post_ID; 
  104.  
  105. $messages['product'] = array( 
  106. 0 => '', // Unused. Messages start at index 1. 
  107. 1 => sprintf( __( 'Product updated. <a href="%s">View Product</a>', 'woocommerce' ), esc_url( get_permalink( $post_ID ) ) ),  
  108. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  109. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  110. 4 => __( 'Product updated.', 'woocommerce' ),  
  111. /** translators: %s: revision title */ 
  112. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Product restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  113. /** translators: %s: product url */ 
  114. 6 => sprintf( __( 'Product published. <a href="%s">View Product</a>', 'woocommerce' ), esc_url( get_permalink( $post_ID ) ) ),  
  115. 7 => __( 'Product saved.', 'woocommerce' ),  
  116. /** translators: %s: product url */ 
  117. 8 => sprintf( __( 'Product submitted. <a target="_blank" href="%s">Preview product</a>', 'woocommerce' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),  
  118. /** translators: 1: date 2: product url */ 
  119. 9 => sprintf( __( 'Product scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview product</a>', 'woocommerce' ),  
  120. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post_ID ) ) ),  
  121. /** translators: %s: product url */ 
  122. 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 ) ) ) ),  
  123. ); 
  124.  
  125. $messages['shop_order'] = array( 
  126. 0 => '', // Unused. Messages start at index 1. 
  127. 1 => __( 'Order updated.', 'woocommerce' ),  
  128. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  129. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  130. 4 => __( 'Order updated.', 'woocommerce' ),  
  131. /** translators: %s: revision title */ 
  132. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Order restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  133. 6 => __( 'Order updated.', 'woocommerce' ),  
  134. 7 => __( 'Order saved.', 'woocommerce' ),  
  135. 8 => __( 'Order submitted.', 'woocommerce' ),  
  136. /** translators: %s: date */ 
  137. 9 => sprintf( __( 'Order scheduled for: <strong>%1$s</strong>.', 'woocommerce' ),  
  138. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) ),  
  139. 10 => __( 'Order draft updated.', 'woocommerce' ),  
  140. 11 => __( 'Order updated and email sent.', 'woocommerce' ),  
  141. ); 
  142.  
  143. $messages['shop_coupon'] = array( 
  144. 0 => '', // Unused. Messages start at index 1. 
  145. 1 => __( 'Coupon updated.', 'woocommerce' ),  
  146. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  147. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  148. 4 => __( 'Coupon updated.', 'woocommerce' ),  
  149. /** translators: %s: revision title */ 
  150. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Coupon restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  151. 6 => __( 'Coupon updated.', 'woocommerce' ),  
  152. 7 => __( 'Coupon saved.', 'woocommerce' ),  
  153. 8 => __( 'Coupon submitted.', 'woocommerce' ),  
  154. /** translators: %s: date */ 
  155. 9 => sprintf( __( 'Coupon scheduled for: <strong>%1$s</strong>.', 'woocommerce' ),  
  156. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) ),  
  157. 10 => __( 'Coupon draft updated.', 'woocommerce' ),  
  158. ); 
  159.  
  160. return $messages; 
  161.  
  162. /** 
  163. * Specify custom bulk actions messages for different post types. 
  164. * @param array $bulk_messages 
  165. * @param array $bulk_counts 
  166. * @return array 
  167. */ 
  168. public function bulk_post_updated_messages( $bulk_messages, $bulk_counts ) { 
  169.  
  170. $bulk_messages['product'] = array( 
  171. /** translators: %s: product count */ 
  172. 'updated' => _n( '%s product updated.', '%s products updated.', $bulk_counts['updated'], 'woocommerce' ),  
  173. /** translators: %s: product count */ 
  174. 'locked' => _n( '%s product not updated, somebody is editing it.', '%s products not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  175. /** translators: %s: product count */ 
  176. 'deleted' => _n( '%s product permanently deleted.', '%s products permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  177. /** translators: %s: product count */ 
  178. 'trashed' => _n( '%s product moved to the Trash.', '%s products moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  179. /** translators: %s: product count */ 
  180. 'untrashed' => _n( '%s product restored from the Trash.', '%s products restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  181. ); 
  182.  
  183. $bulk_messages['shop_order'] = array( 
  184. /** translators: %s: order count */ 
  185. 'updated' => _n( '%s order updated.', '%s orders updated.', $bulk_counts['updated'], 'woocommerce' ),  
  186. /** translators: %s: order count */ 
  187. 'locked' => _n( '%s order not updated, somebody is editing it.', '%s orders not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  188. /** translators: %s: order count */ 
  189. 'deleted' => _n( '%s order permanently deleted.', '%s orders permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  190. /** translators: %s: order count */ 
  191. 'trashed' => _n( '%s order moved to the Trash.', '%s orders moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  192. /** translators: %s: order count */ 
  193. 'untrashed' => _n( '%s order restored from the Trash.', '%s orders restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  194. ); 
  195.  
  196. $bulk_messages['shop_coupon'] = array( 
  197. /** translators: %s: coupon count */ 
  198. 'updated' => _n( '%s coupon updated.', '%s coupons updated.', $bulk_counts['updated'], 'woocommerce' ),  
  199. /** translators: %s: coupon count */ 
  200. 'locked' => _n( '%s coupon not updated, somebody is editing it.', '%s coupons not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  201. /** translators: %s: coupon count */ 
  202. 'deleted' => _n( '%s coupon permanently deleted.', '%s coupons permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  203. /** translators: %s: coupon count */ 
  204. 'trashed' => _n( '%s coupon moved to the Trash.', '%s coupons moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  205. /** translators: %s: coupon count */ 
  206. 'untrashed' => _n( '%s coupon restored from the Trash.', '%s coupons restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  207. ); 
  208.  
  209. return $bulk_messages; 
  210.  
  211. /** 
  212. * Define custom columns for products. 
  213. * @param array $existing_columns 
  214. * @return array 
  215. */ 
  216. public function product_columns( $existing_columns ) { 
  217. if ( empty( $existing_columns ) && ! is_array( $existing_columns ) ) { 
  218. $existing_columns = array(); 
  219.  
  220. unset( $existing_columns['title'], $existing_columns['comments'], $existing_columns['date'] ); 
  221.  
  222. $columns = array(); 
  223. $columns['cb'] = '<input type="checkbox" />'; 
  224. $columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>'; 
  225. $columns['name'] = __( 'Name', 'woocommerce' ); 
  226.  
  227. if ( wc_product_sku_enabled() ) { 
  228. $columns['sku'] = __( 'SKU', 'woocommerce' ); 
  229.  
  230. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  231. $columns['is_in_stock'] = __( 'Stock', 'woocommerce' ); 
  232.  
  233. $columns['price'] = __( 'Price', 'woocommerce' ); 
  234. $columns['product_cat'] = __( 'Categories', 'woocommerce' ); 
  235. $columns['product_tag'] = __( 'Tags', 'woocommerce' ); 
  236. $columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>'; 
  237. $columns['product_type'] = '<span class="wc-type parent-tips" data-tip="' . esc_attr__( 'Type', 'woocommerce' ) . '">' . __( 'Type', 'woocommerce' ) . '</span>'; 
  238. $columns['date'] = __( 'Date', 'woocommerce' ); 
  239.  
  240. return array_merge( $columns, $existing_columns ); 
  241.  
  242.  
  243. /** 
  244. * Define custom columns for coupons. 
  245. * @param array $existing_columns 
  246. * @return array 
  247. */ 
  248. public function shop_coupon_columns( $existing_columns ) { 
  249. $columns = array(); 
  250. $columns['cb'] = $existing_columns['cb']; 
  251. $columns['coupon_code'] = __( 'Code', 'woocommerce' ); 
  252. $columns['type'] = __( 'Coupon type', 'woocommerce' ); 
  253. $columns['amount'] = __( 'Coupon amount', 'woocommerce' ); 
  254. $columns['description'] = __( 'Description', 'woocommerce' ); 
  255. $columns['products'] = __( 'Product IDs', 'woocommerce' ); 
  256. $columns['usage'] = __( 'Usage / Limit', 'woocommerce' ); 
  257. $columns['expiry_date'] = __( 'Expiry date', 'woocommerce' ); 
  258.  
  259. return $columns; 
  260.  
  261. /** 
  262. * Define custom columns for orders. 
  263. * @param array $existing_columns 
  264. * @return array 
  265. */ 
  266. public function shop_order_columns( $existing_columns ) { 
  267. $columns = array(); 
  268. $columns['cb'] = $existing_columns['cb']; 
  269. $columns['order_status'] = '<span class="status_head tips" data-tip="' . esc_attr__( 'Status', 'woocommerce' ) . '">' . esc_attr__( 'Status', 'woocommerce' ) . '</span>'; 
  270. $columns['order_title'] = __( 'Order', 'woocommerce' ); 
  271. $columns['billing_address'] = __( 'Billing', 'woocommerce' ); 
  272. $columns['shipping_address'] = __( 'Ship to', 'woocommerce' ); 
  273. $columns['customer_message'] = '<span class="notes_head tips" data-tip="' . esc_attr__( 'Customer message', 'woocommerce' ) . '">' . esc_attr__( 'Customer message', 'woocommerce' ) . '</span>'; 
  274. $columns['order_notes'] = '<span class="order-notes_head tips" data-tip="' . esc_attr__( 'Order notes', 'woocommerce' ) . '">' . esc_attr__( 'Order notes', 'woocommerce' ) . '</span>'; 
  275. $columns['order_date'] = __( 'Date', 'woocommerce' ); 
  276. $columns['order_total'] = __( 'Total', 'woocommerce' ); 
  277. $columns['order_actions'] = __( 'Actions', 'woocommerce' ); 
  278.  
  279. return $columns; 
  280.  
  281. /** 
  282. * Ouput custom columns for products. 
  283. * @param string $column 
  284. */ 
  285. public function render_product_columns( $column ) { 
  286. global $post, $the_product; 
  287.  
  288. if ( empty( $the_product ) || $the_product->get_id() != $post->ID ) { 
  289. $the_product = wc_get_product( $post ); 
  290.  
  291. // Only continue if we have a product. 
  292. if ( empty( $the_product ) ) { 
  293. return; 
  294.  
  295. switch ( $column ) { 
  296. case 'thumb' : 
  297. echo '<a href="' . get_edit_post_link( $post->ID ) . '">' . $the_product->get_image( 'thumbnail' ) . '</a>'; 
  298. break; 
  299. case 'name' : 
  300. $edit_link = get_edit_post_link( $post->ID ); 
  301. $title = _draft_or_post_title(); 
  302.  
  303. echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>'; 
  304.  
  305. _post_states( $post ); 
  306.  
  307. echo '</strong>'; 
  308.  
  309. if ( $post->post_parent > 0 ) { 
  310. echo '  ← <a href="' . get_edit_post_link( $post->post_parent ) . '">' . get_the_title( $post->post_parent ) . '</a>'; 
  311.  
  312. // Excerpt view 
  313. if ( isset( $_GET['mode'] ) && 'excerpt' == $_GET['mode'] ) { 
  314. echo apply_filters( 'the_excerpt', $post->post_excerpt ); 
  315.  
  316. get_inline_data( $post ); 
  317.  
  318. /** Custom inline data for woocommerce. */ 
  319. echo ' 
  320. <div class="hidden" id="woocommerce_inline_' . absint( $post->ID ) . '"> 
  321. <div class="menu_order">' . absint( $the_product->get_menu_order() ) . '</div> 
  322. <div class="sku">' . esc_html( $the_product->get_sku() ) . '</div> 
  323. <div class="regular_price">' . esc_html( $the_product->get_regular_price() ) . '</div> 
  324. <div class="sale_price">' . esc_html( $the_product->get_sale_price() ) . '</div> 
  325. <div class="weight">' . esc_html( $the_product->get_weight() ) . '</div> 
  326. <div class="length">' . esc_html( $the_product->get_length() ) . '</div> 
  327. <div class="width">' . esc_html( $the_product->get_width() ) . '</div> 
  328. <div class="height">' . esc_html( $the_product->get_height() ) . '</div> 
  329. <div class="shipping_class">' . esc_html( $the_product->get_shipping_class() ) . '</div> 
  330. <div class="visibility">' . esc_html( $the_product->get_catalog_visibility() ) . '</div> 
  331. <div class="stock_status">' . esc_html( $the_product->get_stock_status() ) . '</div> 
  332. <div class="stock">' . esc_html( $the_product->get_stock_quantity() ) . '</div> 
  333. <div class="manage_stock">' . esc_html( wc_bool_to_string( $the_product->get_manage_stock() ) ) . '</div> 
  334. <div class="featured">' . esc_html( wc_bool_to_string( $the_product->get_featured() ) ) . '</div> 
  335. <div class="product_type">' . esc_html( $the_product->get_type() ) . '</div> 
  336. <div class="product_is_virtual">' . esc_html( wc_bool_to_string( $the_product->get_virtual() ) ) . '</div> 
  337. <div class="tax_status">' . esc_html( $the_product->get_tax_status() ) . '</div> 
  338. <div class="tax_class">' . esc_html( $the_product->get_tax_class() ) . '</div> 
  339. <div class="backorders">' . esc_html( $the_product->get_backorders() ) . '</div> 
  340. </div> 
  341. '; 
  342.  
  343. break; 
  344. case 'sku' : 
  345. echo $the_product->get_sku() ? esc_html( $the_product->get_sku() ) : '<span class="na">–</span>'; 
  346. break; 
  347. case 'product_type' : 
  348. if ( $the_product->is_type( 'grouped' ) ) { 
  349. echo '<span class="product-type tips grouped" data-tip="' . esc_attr__( 'Grouped', 'woocommerce' ) . '"></span>'; 
  350. } elseif ( $the_product->is_type( 'external' ) ) { 
  351. echo '<span class="product-type tips external" data-tip="' . esc_attr__( 'External/Affiliate', 'woocommerce' ) . '"></span>'; 
  352. } elseif ( $the_product->is_type( 'simple' ) ) { 
  353.  
  354. if ( $the_product->is_virtual() ) { 
  355. echo '<span class="product-type tips virtual" data-tip="' . esc_attr__( 'Virtual', 'woocommerce' ) . '"></span>'; 
  356. } elseif ( $the_product->is_downloadable() ) { 
  357. echo '<span class="product-type tips downloadable" data-tip="' . esc_attr__( 'Downloadable', 'woocommerce' ) . '"></span>'; 
  358. } else { 
  359. echo '<span class="product-type tips simple" data-tip="' . esc_attr__( 'Simple', 'woocommerce' ) . '"></span>'; 
  360. } elseif ( $the_product->is_type( 'variable' ) ) { 
  361. echo '<span class="product-type tips variable" data-tip="' . esc_attr__( 'Variable', 'woocommerce' ) . '"></span>'; 
  362. } else { 
  363. // Assuming that we have other types in future 
  364. echo '<span class="product-type tips ' . esc_attr( sanitize_html_class( $the_product->get_type() ) ) . '" data-tip="' . esc_attr( ucfirst( $the_product->get_type() ) ) . '"></span>'; 
  365. break; 
  366. case 'price' : 
  367. echo $the_product->get_price_html() ? $the_product->get_price_html() : '<span class="na">–</span>'; 
  368. break; 
  369. case 'product_cat' : 
  370. case 'product_tag' : 
  371. if ( ! $terms = get_the_terms( $post->ID, $column ) ) { 
  372. echo '<span class="na">–</span>'; 
  373. } else { 
  374. $termlist = array(); 
  375. foreach ( $terms as $term ) { 
  376. $termlist[] = '<a href="' . admin_url( 'edit.php?' . $column . '=' . $term->slug . '&post_type=product' ) . ' ">' . $term->name . '</a>'; 
  377.  
  378. echo implode( ', ', $termlist ); 
  379. break; 
  380. case 'featured' : 
  381. $url = wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_feature_product&product_id=' . $post->ID ), 'woocommerce-feature-product' ); 
  382. echo '<a href="' . esc_url( $url ) . '" aria-label="' . __( 'Toggle featured', 'woocommerce' ) . '">'; 
  383. if ( $the_product->is_featured() ) { 
  384. echo '<span class="wc-featured tips" data-tip="' . esc_attr__( 'Yes', 'woocommerce' ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  385. } else { 
  386. echo '<span class="wc-featured not-featured tips" data-tip="' . esc_attr__( 'No', 'woocommerce' ) . '">' . __( 'No', 'woocommerce' ) . '</span>'; 
  387. echo '</a>'; 
  388. break; 
  389. case 'is_in_stock' : 
  390.  
  391. if ( $the_product->is_in_stock() ) { 
  392. $stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>'; 
  393. } else { 
  394. $stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>'; 
  395.  
  396. if ( $the_product->managing_stock() ) { 
  397. $stock_html .= ' (' . wc_stock_amount( $the_product->get_stock_quantity() ) . ')'; 
  398.  
  399. echo apply_filters( 'woocommerce_admin_stock_html', $stock_html, $the_product ); 
  400.  
  401. break; 
  402. default : 
  403. break; 
  404.  
  405. /** 
  406. * Output custom columns for coupons. 
  407. * @param string $column 
  408. */ 
  409. public function render_shop_coupon_columns( $column ) { 
  410. global $post, $the_coupon; 
  411.  
  412. if ( empty( $the_coupon ) || $the_coupon->get_id() !== $post->ID ) { 
  413. $the_coupon = new WC_Coupon( $post->ID ); 
  414.  
  415. switch ( $column ) { 
  416. case 'coupon_code' : 
  417. $edit_link = get_edit_post_link( $post->ID ); 
  418. $title = $the_coupon->get_code(); 
  419.  
  420. echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>'; 
  421. _post_states( $post ); 
  422. echo '</strong>'; 
  423. break; 
  424. case 'type' : 
  425. echo esc_html( wc_get_coupon_type( $the_coupon->get_discount_type() ) ); 
  426. break; 
  427. case 'amount' : 
  428. echo esc_html( wc_format_localized_price( $the_coupon->get_amount() ) ); 
  429. break; 
  430. case 'products' : 
  431. $product_ids = $the_coupon->get_product_ids(); 
  432.  
  433. if ( sizeof( $product_ids ) > 0 ) { 
  434. echo esc_html( implode( ', ', $product_ids ) ); 
  435. } else { 
  436. echo '–'; 
  437. break; 
  438. case 'usage_limit' : 
  439. $usage_limit = $the_coupon->get_usage_limit(); 
  440.  
  441. if ( $usage_limit ) { 
  442. echo esc_html( $usage_limit ); 
  443. } else { 
  444. echo '–'; 
  445. break; 
  446. case 'usage' : 
  447. $usage_count = $the_coupon->get_usage_count(); 
  448. $usage_limit = $the_coupon->get_usage_limit(); 
  449.  
  450. /** translators: 1: count 2: limit */ 
  451. printf( 
  452. __( '%1$s / %2$s', 'woocommerce' ),  
  453. esc_html( $usage_count ),  
  454. esc_html( $usage_limit ? $usage_limit : '∞' ) 
  455. ); 
  456. break; 
  457. case 'expiry_date' : 
  458. $expiry_date = $the_coupon->get_date_expires(); 
  459.  
  460. if ( $expiry_date ) { 
  461. echo esc_html( $expiry_date->date_i18n( 'F j, Y' ) ); 
  462. } else { 
  463. echo '–'; 
  464. break; 
  465. case 'description' : 
  466. echo wp_kses_post( $the_coupon->get_description() ? $the_coupon->get_description() : '–' ); 
  467. break; 
  468.  
  469. /** 
  470. * Output custom columns for coupons. 
  471. * @param string $column 
  472. */ 
  473. public function render_shop_order_columns( $column ) { 
  474. global $post, $the_order; 
  475.  
  476. if ( empty( $the_order ) || $the_order->get_id() !== $post->ID ) { 
  477. $the_order = wc_get_order( $post->ID ); 
  478.  
  479. switch ( $column ) { 
  480. case 'order_status' : 
  481. printf( '<mark class="%s tips" data-tip="%s">%s</mark>', esc_attr( sanitize_html_class( $the_order->get_status() ) ), esc_attr( wc_get_order_status_name( $the_order->get_status() ) ), esc_html( wc_get_order_status_name( $the_order->get_status() ) ) ); 
  482. break; 
  483. case 'order_date' : 
  484. printf( '<time datetime="%s">%s</time>', esc_attr( $the_order->get_date_created()->date( 'c' ) ), esc_html( $the_order->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'Y-m-d', 'woocommerce' ) ) ) ) ); 
  485. break; 
  486. case 'customer_message' : 
  487. if ( $the_order->get_customer_note() ) { 
  488. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( $the_order->get_customer_note() ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  489. } else { 
  490. echo '<span class="na">–</span>'; 
  491.  
  492. break; 
  493. case 'billing_address' : 
  494.  
  495. if ( $address = $the_order->get_formatted_billing_address() ) { 
  496. echo esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) ); 
  497. } else { 
  498. echo '–'; 
  499.  
  500. if ( $the_order->get_billing_phone() ) { 
  501. echo '<small class="meta">' . __( 'Phone:', 'woocommerce' ) . ' ' . esc_html( $the_order->get_billing_phone() ) . '</small>'; 
  502.  
  503. break; 
  504. case 'shipping_address' : 
  505.  
  506. if ( $address = $the_order->get_formatted_shipping_address() ) { 
  507. echo '<a target="_blank" href="' . esc_url( $the_order->get_shipping_address_map_url() ) . '">' . esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) ) . '</a>'; 
  508. } else { 
  509. echo '–'; 
  510.  
  511. if ( $the_order->get_shipping_method() ) { 
  512. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->get_shipping_method() ) . '</small>'; 
  513.  
  514. break; 
  515. case 'order_notes' : 
  516.  
  517. if ( $post->comment_count ) { 
  518.  
  519. // check the status of the post 
  520. $status = ( 'trash' !== $post->post_status ) ? '' : 'post-trashed'; 
  521.  
  522. $latest_notes = get_comments( array( 
  523. 'post_id' => $post->ID,  
  524. 'number' => 1,  
  525. 'status' => $status,  
  526. ) ); 
  527.  
  528. $latest_note = current( $latest_notes ); 
  529.  
  530. if ( isset( $latest_note->comment_content ) && 1 == $post->comment_count ) { 
  531. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( $latest_note->comment_content ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  532. } elseif ( isset( $latest_note->comment_content ) ) { 
  533. /** translators: %d: notes count */ 
  534. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( $latest_note->comment_content . '<br/><small style="display:block">' . sprintf( _n( 'plus %d other note', 'plus %d other notes', ( $post->comment_count - 1 ), 'woocommerce' ), $post->comment_count - 1 ) . '</small>' ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  535. } else { 
  536. /** translators: %d: notes count */ 
  537. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( sprintf( _n( '%d note', '%d notes', $post->comment_count, 'woocommerce' ), $post->comment_count ) ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  538. } else { 
  539. echo '<span class="na">–</span>'; 
  540.  
  541. break; 
  542. case 'order_total' : 
  543. echo $the_order->get_formatted_order_total(); 
  544.  
  545. if ( $the_order->get_payment_method_title() ) { 
  546. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->get_payment_method_title() ) . '</small>'; 
  547. break; 
  548. case 'order_title' : 
  549.  
  550. if ( $the_order->get_customer_id() ) { 
  551. $user = get_user_by( 'id', $the_order->get_customer_id() ); 
  552. $username = '<a href="user-edit.php?user_id=' . absint( $the_order->get_customer_id() ) . '">'; 
  553. $username .= esc_html( ucwords( $user->display_name ) ); 
  554. $username .= '</a>'; 
  555. } elseif ( $the_order->get_billing_first_name() || $the_order->get_billing_last_name() ) { 
  556. /** translators: 1: first name 2: last name */ 
  557. $username = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $the_order->get_billing_first_name(), $the_order->get_billing_last_name() ) ); 
  558. } elseif ( $the_order->get_billing_company() ) { 
  559. $username = trim( $the_order->get_billing_company() ); 
  560. } else { 
  561. $username = __( 'Guest', 'woocommerce' ); 
  562.  
  563. /** translators: 1: order and number (i.e. Order #13) 2: user name */ 
  564. printf( 
  565. __( '%1$s by %2$s', 'woocommerce' ),  
  566. '<a href="' . admin_url( 'post.php?post=' . absint( $post->ID ) . '&action=edit' ) . '" class="row-title"><strong>#' . esc_attr( $the_order->get_order_number() ) . '</strong></a>',  
  567. $username 
  568. ); 
  569.  
  570. if ( $the_order->get_billing_email() ) { 
  571. echo '<small class="meta email"><a href="' . esc_url( 'mailto:' . $the_order->get_billing_email() ) . '">' . esc_html( $the_order->get_billing_email() ) . '</a></small>'; 
  572.  
  573. echo '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details', 'woocommerce' ) . '</span></button>'; 
  574.  
  575. break; 
  576. case 'order_actions' : 
  577.  
  578. ?><p> 
  579. <?php 
  580. do_action( 'woocommerce_admin_order_actions_start', $the_order ); 
  581.  
  582. $actions = array(); 
  583.  
  584. if ( $the_order->has_status( array( 'pending', 'on-hold' ) ) ) { 
  585. $actions['processing'] = array( 
  586. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),  
  587. 'name' => __( 'Processing', 'woocommerce' ),  
  588. 'action' => "processing",  
  589. ); 
  590.  
  591. if ( $the_order->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) { 
  592. $actions['complete'] = array( 
  593. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),  
  594. 'name' => __( 'Complete', 'woocommerce' ),  
  595. 'action' => "complete",  
  596. ); 
  597.  
  598. $actions['view'] = array( 
  599. 'url' => admin_url( 'post.php?post=' . $post->ID . '&action=edit' ),  
  600. 'name' => __( 'View', 'woocommerce' ),  
  601. 'action' => "view",  
  602. ); 
  603.  
  604. $actions = apply_filters( 'woocommerce_admin_order_actions', $actions, $the_order ); 
  605.  
  606. foreach ( $actions as $action ) { 
  607. printf( '<a class="button tips %s" href="%s" data-tip="%s">%s</a>', esc_attr( $action['action'] ), esc_url( $action['url'] ), esc_attr( $action['name'] ), esc_attr( $action['name'] ) ); 
  608.  
  609. do_action( 'woocommerce_admin_order_actions_end', $the_order ); 
  610. ?> 
  611. </p><?php 
  612.  
  613. break; 
  614.  
  615. /** 
  616. * Make columns sortable - https://gist.github.com/906872. 
  617. * @param array $columns 
  618. * @return array 
  619. */ 
  620. public function product_sortable_columns( $columns ) { 
  621. $custom = array( 
  622. 'price' => 'price',  
  623. 'sku' => 'sku',  
  624. 'name' => 'title',  
  625. ); 
  626. return wp_parse_args( $custom, $columns ); 
  627.  
  628. /** 
  629. * Make columns sortable - https://gist.github.com/906872. 
  630. * @param array $columns 
  631. * @return array 
  632. */ 
  633. public function shop_coupon_sortable_columns( $columns ) { 
  634. return $columns; 
  635.  
  636. /** 
  637. * Make columns sortable - https://gist.github.com/906872. 
  638. * @param array $columns 
  639. * @return array 
  640. */ 
  641. public function shop_order_sortable_columns( $columns ) { 
  642. $custom = array( 
  643. 'order_title' => 'ID',  
  644. 'order_total' => 'order_total',  
  645. 'order_date' => 'date',  
  646. ); 
  647. unset( $columns['comments'] ); 
  648.  
  649. return wp_parse_args( $custom, $columns ); 
  650.  
  651. /** 
  652. * Set list table primary column for products and orders. 
  653. * Support for WordPress 4.3. 
  654. * @param string $default 
  655. * @param string $screen_id 
  656. * @return string 
  657. */ 
  658. public function list_table_primary_column( $default, $screen_id ) { 
  659.  
  660. if ( 'edit-product' === $screen_id ) { 
  661. return 'name'; 
  662.  
  663. if ( 'edit-shop_order' === $screen_id ) { 
  664. return 'order_title'; 
  665.  
  666. if ( 'edit-shop_coupon' === $screen_id ) { 
  667. return 'coupon_code'; 
  668.  
  669. return $default; 
  670.  
  671. /** 
  672. * Set row actions for products and orders. 
  673. * @param array $actions 
  674. * @param WP_Post $post 
  675. * @return array 
  676. */ 
  677. public function row_actions( $actions, $post ) { 
  678. if ( 'product' === $post->post_type ) { 
  679. return array_merge( array( 'id' => 'ID: ' . $post->ID ), $actions ); 
  680.  
  681. if ( in_array( $post->post_type, array( 'shop_order', 'shop_coupon' ) ) ) { 
  682. if ( isset( $actions['inline hide-if-no-js'] ) ) { 
  683. unset( $actions['inline hide-if-no-js'] ); 
  684.  
  685. return $actions; 
  686.  
  687. /** 
  688. * Product sorting link. 
  689. * Based on Simple Page Ordering by 10up (https://wordpress.org/plugins/simple-page-ordering/). 
  690. * @param array $views 
  691. * @return array 
  692. */ 
  693. public function product_sorting_link( $views ) { 
  694. global $post_type, $wp_query; 
  695.  
  696. if ( ! current_user_can( 'edit_others_pages' ) ) { 
  697. return $views; 
  698.  
  699. $class = ( isset( $wp_query->query['orderby'] ) && 'menu_order title' === $wp_query->query['orderby'] ) ? 'current' : ''; 
  700. $query_string = remove_query_arg( array( 'orderby', 'order' ) ); 
  701. $query_string = add_query_arg( 'orderby', urlencode( 'menu_order title' ), $query_string ); 
  702. $query_string = add_query_arg( 'order', urlencode( 'ASC' ), $query_string ); 
  703. $views['byorder'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Sort products', 'woocommerce' ) . '</a>'; 
  704.  
  705. return $views; 
  706.  
  707. /** 
  708. * Custom bulk edit - form. 
  709. * @param mixed $column_name 
  710. * @param mixed $post_type 
  711. */ 
  712. public function bulk_edit( $column_name, $post_type ) { 
  713.  
  714. if ( 'price' != $column_name || 'product' != $post_type ) { 
  715. return; 
  716.  
  717. $shipping_class = get_terms( 'product_shipping_class', array( 
  718. 'hide_empty' => false,  
  719. ) ); 
  720.  
  721. include( WC()->plugin_path() . '/includes/admin/views/html-bulk-edit-product.php' ); 
  722.  
  723. /** 
  724. * Custom quick edit - form. 
  725. * @param mixed $column_name 
  726. * @param mixed $post_type 
  727. */ 
  728. public function quick_edit( $column_name, $post_type ) { 
  729.  
  730. if ( 'price' != $column_name || 'product' != $post_type ) { 
  731. return; 
  732.  
  733. $shipping_class = get_terms( 'product_shipping_class', array( 
  734. 'hide_empty' => false,  
  735. ) ); 
  736.  
  737. include( WC()->plugin_path() . '/includes/admin/views/html-quick-edit-product.php' ); 
  738.  
  739. /** 
  740. * Offers a way to hook into save post without causing an infinite loop 
  741. * when quick/bulk saving product info. 
  742. * @since 3.0.0 
  743. * @param int $post_id 
  744. * @param object $post 
  745. */ 
  746. public function bulk_and_quick_edit_hook( $post_id, $post ) { 
  747. remove_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ) ); 
  748. do_action( 'woocommerce_product_bulk_and_quick_edit', $post_id, $post ); 
  749. add_action( 'save_post', array( $this, 'bulk_and_quick_edit_hook' ), 10, 2 ); 
  750.  
  751. /** 
  752. * Quick and bulk edit saving. 
  753. * @param int $post_id 
  754. * @param WP_Post $post 
  755. * @return int 
  756. */ 
  757. public function bulk_and_quick_edit_save_post( $post_id, $post ) { 
  758. // If this is an autosave, our form has not been submitted, so we don't want to do anything. 
  759. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 
  760. return $post_id; 
  761.  
  762. // Don't save revisions and autosaves 
  763. if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) { 
  764. return $post_id; 
  765.  
  766. // Check post type is product 
  767. if ( 'product' != $post->post_type ) { 
  768. return $post_id; 
  769.  
  770. // Check user permission 
  771. if ( ! current_user_can( 'edit_post', $post_id ) ) { 
  772. return $post_id; 
  773.  
  774. // Check nonces 
  775. if ( ! isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) ) { 
  776. return $post_id; 
  777. if ( isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) { 
  778. return $post_id; 
  779. if ( isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_bulk_edit_nonce'], 'woocommerce_bulk_edit_nonce' ) ) { 
  780. return $post_id; 
  781.  
  782. // Get the product and save 
  783. $product = wc_get_product( $post ); 
  784.  
  785. if ( ! empty( $_REQUEST['woocommerce_quick_edit'] ) ) { 
  786. $this->quick_edit_save( $post_id, $product ); 
  787. } else { 
  788. $this->bulk_edit_save( $post_id, $product ); 
  789.  
  790. return $post_id; 
  791.  
  792. /** 
  793. * Quick edit. 
  794. * @param integer $post_id 
  795. * @param WC_Product $product 
  796. */ 
  797. private function quick_edit_save( $post_id, $product ) { 
  798. $data_store = $product->get_data_store(); 
  799. $old_regular_price = $product->get_regular_price(); 
  800. $old_sale_price = $product->get_sale_price(); 
  801.  
  802. // Save fields 
  803. if ( isset( $_REQUEST['_sku'] ) ) { 
  804. $sku = $product->get_sku(); 
  805. $new_sku = (string) wc_clean( $_REQUEST['_sku'] ); 
  806.  
  807. if ( $new_sku !== $sku ) { 
  808. if ( ! empty( $new_sku ) ) { 
  809. $unique_sku = wc_product_has_unique_sku( $post_id, $new_sku ); 
  810. if ( $unique_sku ) { 
  811. $product->set_sku( $new_sku ); 
  812. } else { 
  813. $product->set_sku( '' ); 
  814.  
  815. if ( isset( $_REQUEST['_weight'] ) ) { 
  816. $product->set_weight( wc_clean( $_REQUEST['_weight'] ) ); 
  817.  
  818. if ( isset( $_REQUEST['_length'] ) ) { 
  819. $product->set_length( wc_clean( $_REQUEST['_length'] ) ); 
  820.  
  821. if ( isset( $_REQUEST['_width'] ) ) { 
  822. $product->set_width( wc_clean( $_REQUEST['_width'] ) ); 
  823.  
  824. if ( isset( $_REQUEST['_height'] ) ) { 
  825. $product->set_height( wc_clean( $_REQUEST['_height'] ) ); 
  826.  
  827. if ( ! empty( $_REQUEST['_shipping_class'] ) ) { 
  828. if ( '_no_shipping_class' === $_REQUEST['_shipping_class'] ) { 
  829. $product->set_shipping_class_id( 0 ); 
  830. } else { 
  831. $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $_REQUEST['_shipping_class'] ) ); 
  832. $product->set_shipping_class_id( $shipping_class_id ); 
  833.  
  834. if ( isset( $_REQUEST['_visibility'] ) ) { 
  835. $product->set_catalog_visibility( wc_clean( $_REQUEST['_visibility'] ) ); 
  836.  
  837. if ( isset( $_REQUEST['_featured'] ) ) { 
  838. $product->set_featured( true ); 
  839. } else { 
  840. $product->set_featured( false ); 
  841.  
  842. if ( isset( $_REQUEST['_tax_status'] ) ) { 
  843. $product->set_tax_status( wc_clean( $_REQUEST['_tax_status'] ) ); 
  844.  
  845. if ( isset( $_REQUEST['_tax_class'] ) ) { 
  846. $product->set_tax_class( wc_clean( $_REQUEST['_tax_class'] ) ); 
  847.  
  848. if ( $product->is_type( 'simple' ) || $product->is_type( 'external' ) ) { 
  849.  
  850. if ( isset( $_REQUEST['_regular_price'] ) ) { 
  851. $new_regular_price = ( '' === $_REQUEST['_regular_price'] ) ? '' : wc_format_decimal( $_REQUEST['_regular_price'] ); 
  852. $product->set_regular_price( $new_regular_price ); 
  853. } else { 
  854. $new_regular_price = null; 
  855. if ( isset( $_REQUEST['_sale_price'] ) ) { 
  856. $new_sale_price = ( '' === $_REQUEST['_sale_price'] ) ? '' : wc_format_decimal( $_REQUEST['_sale_price'] ); 
  857. $product->set_sale_price( $new_sale_price ); 
  858. } else { 
  859. $new_sale_price = null; 
  860.  
  861. // Handle price - remove dates and set to lowest 
  862. $price_changed = false; 
  863.  
  864. if ( ! is_null( $new_regular_price ) && $new_regular_price != $old_regular_price ) { 
  865. $price_changed = true; 
  866. } elseif ( ! is_null( $new_sale_price ) && $new_sale_price != $old_sale_price ) { 
  867. $price_changed = true; 
  868.  
  869. if ( $price_changed ) { 
  870. $product->set_date_on_sale_to( '' ); 
  871. $product->set_date_on_sale_from( '' ); 
  872.  
  873. // Handle Stock Data 
  874. $manage_stock = ! empty( $_REQUEST['_manage_stock'] ) && 'grouped' !== $product->get_type() ? 'yes' : 'no'; 
  875. $backorders = ! empty( $_REQUEST['_backorders'] ) ? wc_clean( $_REQUEST['_backorders'] ) : 'no'; 
  876. $stock_status = ! empty( $_REQUEST['_stock_status'] ) ? wc_clean( $_REQUEST['_stock_status'] ) : 'instock'; 
  877. $stock_amount = 'yes' === $manage_stock ? wc_stock_amount( $_REQUEST['_stock'] ) : ''; 
  878.  
  879. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  880.  
  881. // Apply product type constraints to stock status 
  882. if ( $product->is_type( 'external' ) ) { 
  883. // External always in stock 
  884. $stock_status = 'instock'; 
  885. } elseif ( $product->is_type( 'variable' ) ) { 
  886. // Stock status is always determined by children 
  887. foreach ( $product->get_children() as $child_id ) { 
  888. $child = wc_get_product( $child_id ); 
  889. if ( ! $product->get_manage_stock() ) { 
  890. $child->set_stock_status( $stock_status ); 
  891. $child->save(); 
  892.  
  893. $product = WC_Product_Variable::sync( $product, false ); 
  894.  
  895. $product->set_manage_stock( $manage_stock ); 
  896. $product->set_backorders( $backorders ); 
  897. $product->save(); 
  898.  
  899. if ( ! $product->is_type( 'variable' ) ) { 
  900. wc_update_product_stock_status( $post_id, $stock_status ); 
  901.  
  902. wc_update_product_stock( $post_id, $stock_amount ); 
  903.  
  904. } else { 
  905. $product->save(); 
  906. wc_update_product_stock_status( $post_id, $stock_status ); 
  907.  
  908. do_action( 'woocommerce_product_quick_edit_save', $product ); 
  909.  
  910. /** 
  911. * Bulk edit. 
  912. * @param integer $post_id 
  913. * @param WC_Product $product 
  914. */ 
  915. public function bulk_edit_save( $post_id, $product ) { 
  916. $data_store = $product->get_data_store(); 
  917. $old_regular_price = $product->get_regular_price(); 
  918. $old_sale_price = $product->get_sale_price(); 
  919.  
  920. // Save fields 
  921. if ( ! empty( $_REQUEST['change_weight'] ) && isset( $_REQUEST['_weight'] ) ) { 
  922. $product->set_weight( wc_clean( stripslashes( $_REQUEST['_weight'] ) ) ); 
  923.  
  924. if ( ! empty( $_REQUEST['change_dimensions'] ) ) { 
  925. if ( isset( $_REQUEST['_length'] ) ) { 
  926. $product->set_length( wc_clean( stripslashes( $_REQUEST['_length'] ) ) ); 
  927. if ( isset( $_REQUEST['_width'] ) ) { 
  928. $product->set_width( wc_clean( stripslashes( $_REQUEST['_width'] ) ) ); 
  929. if ( isset( $_REQUEST['_height'] ) ) { 
  930. $product->set_height( wc_clean( stripslashes( $_REQUEST['_height'] ) ) ); 
  931.  
  932. if ( ! empty( $_REQUEST['_tax_status'] ) ) { 
  933. $product->set_tax_status( wc_clean( $_REQUEST['_tax_status'] ) ); 
  934.  
  935. if ( ! empty( $_REQUEST['_tax_class'] ) ) { 
  936. $tax_class = wc_clean( $_REQUEST['_tax_class'] ); 
  937. if ( 'standard' == $tax_class ) { 
  938. $tax_class = ''; 
  939. $product->set_tax_class( $tax_class ); 
  940.  
  941. if ( ! empty( $_REQUEST['_shipping_class'] ) ) { 
  942. if ( '_no_shipping_class' === $_REQUEST['_shipping_class'] ) { 
  943. $product->set_shipping_class_id( 0 ); 
  944. } else { 
  945. $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $_REQUEST['_shipping_class'] ) ); 
  946. $product->set_shipping_class_id( $shipping_class_id ); 
  947.  
  948. if ( ! empty( $_REQUEST['_visibility'] ) ) { 
  949. $product->set_catalog_visibility( wc_clean( $_REQUEST['_visibility'] ) ); 
  950.  
  951. if ( ! empty( $_REQUEST['_featured'] ) ) { 
  952. $product->set_featured( stripslashes( $_REQUEST['_featured'] ) ); 
  953.  
  954. // Sold Individually 
  955. if ( ! empty( $_REQUEST['_sold_individually'] ) ) { 
  956. if ( 'yes' === $_REQUEST['_sold_individually'] ) { 
  957. $product->set_sold_individually( 'yes' ); 
  958. } else { 
  959. $product->set_sold_individually( '' ); 
  960.  
  961. // Handle price - remove dates and set to lowest 
  962. $change_price_product_types = apply_filters( 'woocommerce_bulk_edit_save_price_product_types', array( 'simple', 'external' ) ); 
  963. $can_product_type_change_price = false; 
  964. foreach ( $change_price_product_types as $product_type ) { 
  965. if ( $product->is_type( $product_type ) ) { 
  966. $can_product_type_change_price = true; 
  967. break; 
  968.  
  969. if ( $can_product_type_change_price ) { 
  970.  
  971. $price_changed = false; 
  972.  
  973. if ( ! empty( $_REQUEST['change_regular_price'] ) ) { 
  974. $change_regular_price = absint( $_REQUEST['change_regular_price'] ); 
  975. $regular_price = esc_attr( stripslashes( $_REQUEST['_regular_price'] ) ); 
  976.  
  977. switch ( $change_regular_price ) { 
  978. case 1 : 
  979. $new_price = $regular_price; 
  980. break; 
  981. case 2 : 
  982. if ( strstr( $regular_price, '%' ) ) { 
  983. $percent = str_replace( '%', '', $regular_price ) / 100; 
  984. $new_price = $old_regular_price + ( round( $old_regular_price * $percent, wc_get_price_decimals() ) ); 
  985. } else { 
  986. $new_price = $old_regular_price + $regular_price; 
  987. break; 
  988. case 3 : 
  989. if ( strstr( $regular_price, '%' ) ) { 
  990. $percent = str_replace( '%', '', $regular_price ) / 100; 
  991. $new_price = max( 0, $old_regular_price - ( round( $old_regular_price * $percent, wc_get_price_decimals() ) ) ); 
  992. } else { 
  993. $new_price = max( 0, $old_regular_price - $regular_price ); 
  994. break; 
  995.  
  996. default : 
  997. break; 
  998.  
  999. if ( isset( $new_price ) && $new_price != $old_regular_price ) { 
  1000. $price_changed = true; 
  1001. $new_price = round( $new_price, wc_get_price_decimals() ); 
  1002. $product->set_regular_price( $new_price ); 
  1003.  
  1004. if ( ! empty( $_REQUEST['change_sale_price'] ) ) { 
  1005. $change_sale_price = absint( $_REQUEST['change_sale_price'] ); 
  1006. $sale_price = esc_attr( stripslashes( $_REQUEST['_sale_price'] ) ); 
  1007.  
  1008. switch ( $change_sale_price ) { 
  1009. case 1 : 
  1010. $new_price = $sale_price; 
  1011. break; 
  1012. case 2 : 
  1013. if ( strstr( $sale_price, '%' ) ) { 
  1014. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1015. $new_price = $old_sale_price + ( $old_sale_price * $percent ); 
  1016. } else { 
  1017. $new_price = $old_sale_price + $sale_price; 
  1018. break; 
  1019. case 3 : 
  1020. if ( strstr( $sale_price, '%' ) ) { 
  1021. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1022. $new_price = max( 0, $old_sale_price - ( $old_sale_price * $percent ) ); 
  1023. } else { 
  1024. $new_price = max( 0, $old_sale_price - $sale_price ); 
  1025. break; 
  1026. case 4 : 
  1027. if ( strstr( $sale_price, '%' ) ) { 
  1028. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1029. $new_price = max( 0, $product->regular_price - ( $product->regular_price * $percent ) ); 
  1030. } else { 
  1031. $new_price = max( 0, $product->regular_price - $sale_price ); 
  1032. break; 
  1033.  
  1034. default : 
  1035. break; 
  1036.  
  1037. if ( isset( $new_price ) && $new_price != $old_sale_price ) { 
  1038. $price_changed = true; 
  1039. $new_price = ! empty( $new_price ) || '0' === $new_price ? round( $new_price, wc_get_price_decimals() ) : ''; 
  1040. $product->set_sale_price( $new_price ); 
  1041.  
  1042. if ( $price_changed ) { 
  1043. $product->set_date_on_sale_to( '' ); 
  1044. $product->set_date_on_sale_from( '' ); 
  1045.  
  1046. if ( $product->get_regular_price() < $product->get_sale_price() ) { 
  1047. $product->set_sale_price( '' ); 
  1048.  
  1049. // Handle Stock Data 
  1050. $was_managing_stock = $product->get_manage_stock() ? 'yes' : 'no'; 
  1051. $stock_status = $product->get_stock_status(); 
  1052. $backorders = $product->get_backorders(); 
  1053.  
  1054. $backorders = ! empty( $_REQUEST['_backorders'] ) ? wc_clean( $_REQUEST['_backorders'] ) : $backorders; 
  1055. $stock_status = ! empty( $_REQUEST['_stock_status'] ) ? wc_clean( $_REQUEST['_stock_status'] ) : $stock_status; 
  1056.  
  1057. if ( ! empty( $_REQUEST['_manage_stock'] ) ) { 
  1058. $manage_stock = 'yes' === wc_clean( $_REQUEST['_manage_stock'] ) && 'grouped' !== $product->product_type ? 'yes' : 'no'; 
  1059. } else { 
  1060. $manage_stock = $was_managing_stock; 
  1061.  
  1062. $stock_amount = 'yes' === $manage_stock && ! empty( $_REQUEST['change_stock'] ) ? wc_stock_amount( $_REQUEST['_stock'] ) : $product->get_stock_quantity(); 
  1063.  
  1064. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  1065.  
  1066. // Apply product type constraints to stock status 
  1067. if ( $product->is_type( 'external' ) ) { 
  1068. // External always in stock 
  1069. $stock_status = 'instock'; 
  1070. } elseif ( $product->is_type( 'variable' ) ) { 
  1071. // Stock status is always determined by children 
  1072. foreach ( $product->get_children() as $child_id ) { 
  1073. $child = wc_get_product( $child_id ); 
  1074. if ( ! $product->get_manage_stock() ) { 
  1075. $child->set_stock_status( $stock_status ); 
  1076. $child->save(); 
  1077.  
  1078. $product = WC_Product_Variable::sync( $product, false ); 
  1079.  
  1080. $product->set_manage_stock( $manage_stock ); 
  1081. $product->set_backorders( $backorders ); 
  1082. $product->save(); 
  1083.  
  1084. if ( ! $product->is_type( 'variable' ) ) { 
  1085. wc_update_product_stock_status( $post_id, $stock_status ); 
  1086.  
  1087. wc_update_product_stock( $post_id, $stock_amount ); 
  1088.  
  1089. } else { 
  1090. $product->save(); 
  1091. wc_update_product_stock_status( $post_id, $stock_status ); 
  1092.  
  1093. do_action( 'woocommerce_product_bulk_edit_save', $product ); 
  1094.  
  1095. /** 
  1096. * Manipulate shop order bulk actions. 
  1097. * @param array $actions List of actions. 
  1098. * @return array 
  1099. */ 
  1100. public function shop_order_bulk_actions( $actions ) { 
  1101. if ( isset( $actions['edit'] ) ) { 
  1102. unset( $actions['edit'] ); 
  1103.  
  1104. $actions['mark_processing'] = __( 'Mark processing', 'woocommerce' ); 
  1105. $actions['mark_on-hold'] = __( 'Mark on-hold', 'woocommerce' ); 
  1106. $actions['mark_completed'] = __( 'Mark complete', 'woocommerce' ); 
  1107.  
  1108. return $actions; 
  1109.  
  1110. /** 
  1111. * Handle shop order bulk actions. 
  1112. * @since 3.0.0 
  1113. * @param string $redirect_to URL to redirect to. 
  1114. * @param string $action Action name. 
  1115. * @param array $ids List of ids. 
  1116. * @return string 
  1117. */ 
  1118. public function handle_shop_order_bulk_actions( $redirect_to, $action, $ids ) { 
  1119. // Bail out if this is not a status-changing action. 
  1120. if ( false === strpos( $action, 'mark_' ) ) { 
  1121. return $redirect_to; 
  1122.  
  1123. $order_statuses = wc_get_order_statuses(); 
  1124. $new_status = substr( $action, 5 ); // Get the status name from action. 
  1125. $report_action = 'marked_' . $new_status; 
  1126.  
  1127. // Sanity check: bail out if this is actually not a status, or is 
  1128. // not a registered status. 
  1129. if ( ! isset( $order_statuses[ 'wc-' . $new_status ] ) ) { 
  1130. return $redirect_to; 
  1131.  
  1132. $changed = 0; 
  1133. $ids = array_map( 'absint', $ids ); 
  1134.  
  1135. foreach ( $ids as $id ) { 
  1136. $order = wc_get_order( $id ); 
  1137. $order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true ); 
  1138. do_action( 'woocommerce_order_edit_status', $id, $new_status ); 
  1139. $changed++; 
  1140.  
  1141. $redirect_to = add_query_arg( array( 
  1142. 'post_type' => 'shop_order',  
  1143. $report_action => true,  
  1144. 'changed' => $changed,  
  1145. 'ids' => join( ', ', $ids ),  
  1146. ), $redirect_to ); 
  1147.  
  1148. return esc_url_raw( $redirect_to ); 
  1149.  
  1150. /** 
  1151. * Show confirmation message that order status changed for number of orders. 
  1152. */ 
  1153. public function bulk_admin_notices() { 
  1154. global $post_type, $pagenow; 
  1155.  
  1156. // Bail out if not on shop order list page 
  1157. if ( 'edit.php' !== $pagenow || 'shop_order' !== $post_type ) { 
  1158. return; 
  1159.  
  1160. $order_statuses = wc_get_order_statuses(); 
  1161.  
  1162. // Check if any status changes happened 
  1163. foreach ( $order_statuses as $slug => $name ) { 
  1164.  
  1165. if ( isset( $_REQUEST[ 'marked_' . str_replace( 'wc-', '', $slug ) ] ) ) { 
  1166.  
  1167. $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; 
  1168. /** translators: %s: orders count */ 
  1169. $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) ); 
  1170. echo '<div class="updated"><p>' . $message . '</p></div>'; 
  1171.  
  1172. break; 
  1173.  
  1174. /** 
  1175. * Search custom fields as well as content. 
  1176. * @param WP_Query $wp 
  1177. */ 
  1178. public function shop_order_search_custom_fields( $wp ) { 
  1179. global $pagenow; 
  1180.  
  1181. if ( 'edit.php' != $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] ) { 
  1182. return; 
  1183.  
  1184. $post_ids = wc_order_search( $_GET['s'] ); 
  1185.  
  1186. if ( ! empty( $post_ids ) ) { 
  1187. // Remove "s" - we don't want to search order name. 
  1188. unset( $wp->query_vars['s'] ); 
  1189.  
  1190. // so we know we're doing this. 
  1191. $wp->query_vars['shop_order_search'] = true; 
  1192.  
  1193. // Search by found posts. 
  1194. $wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) ); 
  1195.  
  1196. /** 
  1197. * Change the label when searching orders. 
  1198. * @param mixed $query 
  1199. * @return string 
  1200. */ 
  1201. public function shop_order_search_label( $query ) { 
  1202. global $pagenow, $typenow; 
  1203.  
  1204. if ( 'edit.php' != $pagenow ) { 
  1205. return $query; 
  1206.  
  1207. if ( 'shop_order' !== $typenow ) { 
  1208. return $query; 
  1209.  
  1210. if ( ! get_query_var( 'shop_order_search' ) ) { 
  1211. return $query; 
  1212.  
  1213. return wp_unslash( $_GET['s'] ); 
  1214.  
  1215. /** 
  1216. * Query vars for custom searches. 
  1217. * @param mixed $public_query_vars 
  1218. * @return array 
  1219. */ 
  1220. public function add_custom_query_var( $public_query_vars ) { 
  1221. $public_query_vars[] = 'sku'; 
  1222. $public_query_vars[] = 'shop_order_search'; 
  1223.  
  1224. return $public_query_vars; 
  1225.  
  1226. /** 
  1227. * Filters for post types. 
  1228. */ 
  1229. public function restrict_manage_posts() { 
  1230. global $typenow; 
  1231.  
  1232. if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1233. $this->shop_order_filters(); 
  1234. } elseif ( 'product' == $typenow ) { 
  1235. $this->product_filters(); 
  1236. } elseif ( 'shop_coupon' == $typenow ) { 
  1237. $this->shop_coupon_filters(); 
  1238.  
  1239. /** 
  1240. * Show a category filter box. 
  1241. */ 
  1242. public function product_filters() { 
  1243. global $wp_query; 
  1244.  
  1245. // Category Filtering 
  1246. wc_product_dropdown_categories(); 
  1247.  
  1248. // Type filtering 
  1249. $terms = get_terms( 'product_type' ); 
  1250. $output = '<select name="product_type" id="dropdown_product_type">'; 
  1251. $output .= '<option value="">' . __( 'Show all product types', 'woocommerce' ) . '</option>'; 
  1252.  
  1253. foreach ( $terms as $term ) { 
  1254. $output .= '<option value="' . sanitize_title( $term->name ) . '" '; 
  1255.  
  1256. if ( isset( $wp_query->query['product_type'] ) ) { 
  1257. $output .= selected( $term->slug, $wp_query->query['product_type'], false ); 
  1258.  
  1259. $output .= '>'; 
  1260.  
  1261. switch ( $term->name ) { 
  1262. case 'grouped' : 
  1263. $output .= __( 'Grouped product', 'woocommerce' ); 
  1264. break; 
  1265. case 'external' : 
  1266. $output .= __( 'External/Affiliate product', 'woocommerce' ); 
  1267. break; 
  1268. case 'variable' : 
  1269. $output .= __( 'Variable product', 'woocommerce' ); 
  1270. break; 
  1271. case 'simple' : 
  1272. $output .= __( 'Simple product', 'woocommerce' ); 
  1273. break; 
  1274. default : 
  1275. // Assuming that we have other types in future 
  1276. $output .= ucfirst( $term->name ); 
  1277. break; 
  1278.  
  1279. $output .= '</option>'; 
  1280.  
  1281. if ( 'simple' == $term->name ) { 
  1282.  
  1283. $output .= '<option value="downloadable" '; 
  1284.  
  1285. if ( isset( $wp_query->query['product_type'] ) ) { 
  1286. $output .= selected( 'downloadable', $wp_query->query['product_type'], false ); 
  1287.  
  1288. $output .= '> ' . ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Downloadable', 'woocommerce' ) . '</option>'; 
  1289.  
  1290. $output .= '<option value="virtual" '; 
  1291.  
  1292. if ( isset( $wp_query->query['product_type'] ) ) { 
  1293. $output .= selected( 'virtual', $wp_query->query['product_type'], false ); 
  1294.  
  1295. $output .= '> ' . ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Virtual', 'woocommerce' ) . '</option>'; 
  1296.  
  1297. $output .= '</select>'; 
  1298.  
  1299. echo apply_filters( 'woocommerce_product_filters', $output ); 
  1300.  
  1301. /** 
  1302. * Show custom filters to filter coupons by type. 
  1303. */ 
  1304. public function shop_coupon_filters() { 
  1305. ?> 
  1306. <select name="coupon_type" id="dropdown_shop_coupon_type"> 
  1307. <option value=""><?php _e( 'Show all types', 'woocommerce' ); ?></option> 
  1308. <?php 
  1309. $types = wc_get_coupon_types(); 
  1310.  
  1311. foreach ( $types as $name => $type ) { 
  1312. echo '<option value="' . esc_attr( $name ) . '"'; 
  1313.  
  1314. if ( isset( $_GET['coupon_type'] ) ) { 
  1315. selected( $name, $_GET['coupon_type'] ); 
  1316.  
  1317. echo '>' . esc_html( $type ) . '</option>'; 
  1318. ?> 
  1319. </select> 
  1320. <?php 
  1321.  
  1322. /** 
  1323. * Show custom filters to filter orders by status/customer. 
  1324. */ 
  1325. public function shop_order_filters() { 
  1326. $user_string = ''; 
  1327. $user_id = ''; 
  1328. if ( ! empty( $_GET['_customer_user'] ) ) { 
  1329. $user_id = absint( $_GET['_customer_user'] ); 
  1330. $user = get_user_by( 'id', $user_id ); 
  1331. /** translators: 1: user display name 2: user ID 3: user email */ 
  1332. $user_string = sprintf( 
  1333. esc_html__( '%1$s (#%2$s – %3$s)', 'woocommerce' ),  
  1334. $user->display_name,  
  1335. absint( $user->ID ),  
  1336. $user->user_email 
  1337. ); 
  1338. ?> 
  1339. <select class="wc-customer-search" name="_customer_user" data-placeholder="<?php esc_attr_e( 'Search for a customer…', 'woocommerce' ); ?>" data-allow_clear="true"> 
  1340. <option value="<?php echo esc_attr( $user_id ); ?>" selected="selected"><?php echo htmlspecialchars( $user_string ); ?><option> 
  1341. </select> 
  1342. <?php 
  1343.  
  1344. /** 
  1345. * Filters and sorting handler. 
  1346. * @param array $vars 
  1347. * @return array 
  1348. */ 
  1349. public function request_query( $vars ) { 
  1350. global $typenow, $wp_query, $wp_post_statuses; 
  1351.  
  1352. if ( 'product' === $typenow ) { 
  1353. // Sorting 
  1354. if ( isset( $vars['orderby'] ) ) { 
  1355. if ( 'price' == $vars['orderby'] ) { 
  1356. $vars = array_merge( $vars, array( 
  1357. 'meta_key' => '_price',  
  1358. 'orderby' => 'meta_value_num',  
  1359. ) ); 
  1360. if ( 'sku' == $vars['orderby'] ) { 
  1361. $vars = array_merge( $vars, array( 
  1362. 'meta_key' => '_sku',  
  1363. 'orderby' => 'meta_value',  
  1364. ) ); 
  1365. } elseif ( 'shop_coupon' === $typenow ) { 
  1366.  
  1367. if ( ! empty( $_GET['coupon_type'] ) ) { 
  1368. $vars['meta_key'] = 'discount_type'; 
  1369. $vars['meta_value'] = wc_clean( $_GET['coupon_type'] ); 
  1370. } elseif ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1371.  
  1372. // Filter the orders by the posted customer. 
  1373. if ( isset( $_GET['_customer_user'] ) && $_GET['_customer_user'] > 0 ) { 
  1374. $vars['meta_query'] = array( 
  1375. array( 
  1376. 'key' => '_customer_user',  
  1377. 'value' => (int) $_GET['_customer_user'],  
  1378. 'compare' => '=',  
  1379. ),  
  1380. ); 
  1381.  
  1382. // Sorting 
  1383. if ( isset( $vars['orderby'] ) ) { 
  1384. if ( 'order_total' == $vars['orderby'] ) { 
  1385. $vars = array_merge( $vars, array( 
  1386. 'meta_key' => '_order_total',  
  1387. 'orderby' => 'meta_value_num',  
  1388. ) ); 
  1389.  
  1390. // Status 
  1391. if ( ! isset( $vars['post_status'] ) ) { 
  1392. $post_statuses = wc_get_order_statuses(); 
  1393.  
  1394. foreach ( $post_statuses as $status => $value ) { 
  1395. if ( isset( $wp_post_statuses[ $status ] ) && false === $wp_post_statuses[ $status ]->show_in_admin_all_list ) { 
  1396. unset( $post_statuses[ $status ] ); 
  1397.  
  1398. $vars['post_status'] = array_keys( $post_statuses ); 
  1399.  
  1400. return $vars; 
  1401.  
  1402. /** 
  1403. * Filter the products in admin based on options. 
  1404. * @param mixed $query 
  1405. */ 
  1406. public function product_filters_query( $query ) { 
  1407. global $typenow, $wp_query; 
  1408.  
  1409. if ( 'product' == $typenow ) { 
  1410.  
  1411. if ( isset( $query->query_vars['product_type'] ) ) { 
  1412. // Subtypes 
  1413. if ( 'downloadable' == $query->query_vars['product_type'] ) { 
  1414. $query->query_vars['product_type'] = ''; 
  1415. $query->is_tax = false; 
  1416. $query->query_vars['meta_value'] = 'yes'; 
  1417. $query->query_vars['meta_key'] = '_downloadable'; 
  1418. } elseif ( 'virtual' == $query->query_vars['product_type'] ) { 
  1419. $query->query_vars['product_type'] = ''; 
  1420. $query->is_tax = false; 
  1421. $query->query_vars['meta_value'] = 'yes'; 
  1422. $query->query_vars['meta_key'] = '_virtual'; 
  1423.  
  1424. // Categories 
  1425. if ( isset( $_GET['product_cat'] ) && '0' === $_GET['product_cat'] ) { 
  1426. $query->query_vars['tax_query'][] = array( 
  1427. 'taxonomy' => 'product_cat',  
  1428. 'field' => 'id',  
  1429. 'terms' => get_terms( 'product_cat', array( 'fields' => 'ids' ) ),  
  1430. 'operator' => 'NOT IN',  
  1431. ); 
  1432.  
  1433. // Shipping classes 
  1434. if ( isset( $_GET['product_shipping_class'] ) && '0' === $_GET['product_shipping_class'] ) { 
  1435. $query->query_vars['tax_query'][] = array( 
  1436. 'taxonomy' => 'product_shipping_class',  
  1437. 'field' => 'id',  
  1438. 'terms' => get_terms( 'product_shipping_class', array( 'fields' => 'ids' ) ),  
  1439. 'operator' => 'NOT IN',  
  1440. ); 
  1441.  
  1442. /** 
  1443. * Search by SKU or ID for products. 
  1444. * @param string $where 
  1445. * @return string 
  1446. */ 
  1447. public function product_search( $where ) { 
  1448. global $pagenow, $wpdb, $wp; 
  1449.  
  1450. if ( 'edit.php' != $pagenow || ! is_search() || ! isset( $wp->query_vars['s'] ) || 'product' != $wp->query_vars['post_type'] ) { 
  1451. return $where; 
  1452.  
  1453. $search_ids = array(); 
  1454. $terms = explode( ', ', $wp->query_vars['s'] ); 
  1455.  
  1456. foreach ( $terms as $term ) { 
  1457. if ( is_numeric( $term ) ) { 
  1458. $search_ids[] = $term; 
  1459.  
  1460. // Attempt to get a SKU 
  1461. $sku_to_id = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent FROM {$wpdb->posts} LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id WHERE meta_key='_sku' AND meta_value LIKE %s;", '%' . $wpdb->esc_like( wc_clean( $term ) ) . '%' ) ); 
  1462. $sku_to_id = array_merge( wp_list_pluck( $sku_to_id, 'ID' ), wp_list_pluck( $sku_to_id, 'post_parent' ) ); 
  1463.  
  1464. if ( sizeof( $sku_to_id ) > 0 ) { 
  1465. $search_ids = array_merge( $search_ids, $sku_to_id ); 
  1466.  
  1467. $search_ids = array_filter( array_unique( array_map( 'absint', $search_ids ) ) ); 
  1468.  
  1469. if ( sizeof( $search_ids ) > 0 ) { 
  1470. $where = str_replace( 'AND (((', "AND ( ({$wpdb->posts}.ID IN (" . implode( ', ', $search_ids ) . ")) OR ((", $where ); 
  1471.  
  1472. return $where; 
  1473.  
  1474. /** 
  1475. * Disable the auto-save functionality for Orders. 
  1476. */ 
  1477. public function disable_autosave() { 
  1478. global $post; 
  1479.  
  1480. if ( $post && in_array( get_post_type( $post->ID ), wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1481. wp_dequeue_script( 'autosave' ); 
  1482.  
  1483. /** 
  1484. * Output extra data on post forms. 
  1485. * @param WP_Post $post 
  1486. */ 
  1487. public function edit_form_top( $post ) { 
  1488. echo '<input type="hidden" id="original_post_title" name="original_post_title" value="' . esc_attr( $post->post_title ) . '" />'; 
  1489.  
  1490. /** 
  1491. * Change title boxes in admin. 
  1492. * @param string $text 
  1493. * @param object $post 
  1494. * @return string 
  1495. */ 
  1496. public function enter_title_here( $text, $post ) { 
  1497. switch ( $post->post_type ) { 
  1498. case 'product' : 
  1499. $text = __( 'Product name', 'woocommerce' ); 
  1500. break; 
  1501. case 'shop_coupon' : 
  1502. $text = __( 'Coupon code', 'woocommerce' ); 
  1503. break; 
  1504.  
  1505. return $text; 
  1506.  
  1507. /** 
  1508. * Print coupon description textarea field. 
  1509. * @param WP_Post $post 
  1510. */ 
  1511. public function edit_form_after_title( $post ) { 
  1512. if ( 'shop_coupon' === $post->post_type ) { 
  1513. ?> 
  1514. <textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo $post->post_excerpt; // This is already escaped in core ?></textarea> 
  1515. <?php 
  1516.  
  1517. /** 
  1518. * Hidden default Meta-Boxes. 
  1519. * @param array $hidden 
  1520. * @param object $screen 
  1521. * @return array 
  1522. */ 
  1523. public function hidden_meta_boxes( $hidden, $screen ) { 
  1524. if ( 'product' === $screen->post_type && 'post' === $screen->base ) { 
  1525. $hidden = array_merge( $hidden, array( 'postcustom' ) ); 
  1526.  
  1527. return $hidden; 
  1528.  
  1529. /** 
  1530. * Output product visibility options. 
  1531. */ 
  1532. public function product_data_visibility() { 
  1533. global $post, $thepostid, $product_object; 
  1534.  
  1535. if ( 'product' !== $post->post_type ) { 
  1536. return; 
  1537.  
  1538. $thepostid = $post->ID; 
  1539. $product_object = $thepostid ? wc_get_product( $thepostid ) : new WC_Product; 
  1540. $current_visibility = $product_object->get_catalog_visibility(); 
  1541. $current_featured = wc_bool_to_string( $product_object->get_featured() ); 
  1542. $visibility_options = wc_get_product_visibility_options(); 
  1543. ?> 
  1544. <div class="misc-pub-section" id="catalog-visibility"> 
  1545. <?php _e( 'Catalog visibility:', 'woocommerce' ); ?> <strong id="catalog-visibility-display"><?php 
  1546. echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility ); 
  1547.  
  1548. if ( 'yes' === $current_featured ) { 
  1549. echo ', ' . __( 'Featured', 'woocommerce' ); 
  1550. ?></strong> 
  1551.  
  1552. <a href="#catalog-visibility" class="edit-catalog-visibility hide-if-no-js"><?php _e( 'Edit', 'woocommerce' ); ?></a> 
  1553.  
  1554. <div id="catalog-visibility-select" class="hide-if-js"> 
  1555.  
  1556. <input type="hidden" name="current_visibility" id="current_visibility" value="<?php echo esc_attr( $current_visibility ); ?>" /> 
  1557. <input type="hidden" name="current_featured" id="current_featured" value="<?php echo esc_attr( $current_featured ); ?>" /> 
  1558.  
  1559. <?php 
  1560. echo '<p>' . __( 'Choose where this product should be displayed in your catalog. The product will always be accessible directly.', 'woocommerce' ) . '</p>'; 
  1561.  
  1562. foreach ( $visibility_options as $name => $label ) { 
  1563. 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 />'; 
  1564.  
  1565. echo '<p>' . __( 'Enable this option to feature this product.', 'woocommerce' ) . '</p>'; 
  1566.  
  1567. echo '<input type="checkbox" name="_featured" id="_featured" ' . checked( $current_featured, 'yes', false ) . ' /> <label for="_featured">' . __( 'Featured product', 'woocommerce' ) . '</label><br />'; 
  1568. ?> 
  1569. <p> 
  1570. <a href="#catalog-visibility" class="save-post-visibility hide-if-no-js button"><?php _e( 'OK', 'woocommerce' ); ?></a> 
  1571. <a href="#catalog-visibility" class="cancel-post-visibility hide-if-no-js"><?php _e( 'Cancel', 'woocommerce' ); ?></a> 
  1572. </p> 
  1573. </div> 
  1574. </div> 
  1575. <?php 
  1576.  
  1577. /** 
  1578. * Filter the directory for uploads. 
  1579. * @param array $pathdata 
  1580. * @return array 
  1581. */ 
  1582. public function upload_dir( $pathdata ) { 
  1583.  
  1584. // Change upload dir for downloadable files 
  1585. if ( isset( $_POST['type'] ) && 'downloadable_product' == $_POST['type'] ) { 
  1586.  
  1587. if ( empty( $pathdata['subdir'] ) ) { 
  1588. $pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads'; 
  1589. $pathdata['url'] = $pathdata['url'] . '/woocommerce_uploads'; 
  1590. $pathdata['subdir'] = '/woocommerce_uploads'; 
  1591. } else { 
  1592. $new_subdir = '/woocommerce_uploads' . $pathdata['subdir']; 
  1593.  
  1594. $pathdata['path'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] ); 
  1595. $pathdata['url'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] ); 
  1596. $pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] ); 
  1597.  
  1598. return $pathdata; 
  1599.  
  1600. /** 
  1601. * Run a filter when uploading a downloadable product. 
  1602. */ 
  1603. public function woocommerce_media_upload_downloadable_product() { 
  1604. do_action( 'media_upload_file' ); 
  1605.  
  1606. /** 
  1607. * Grant downloadable file access to any newly added files on any existing. 
  1608. * orders for this product that have previously been granted downloadable file access. 
  1609. * @param int $product_id product identifier 
  1610. * @param int $variation_id optional product variation identifier 
  1611. * @param array $downloadable_files newly set files 
  1612. * @deprecated and moved to post-data class. 
  1613. */ 
  1614. public function process_product_file_download_paths( $product_id, $variation_id, $downloadable_files ) { 
  1615. WC_Post_Data::process_product_file_download_paths( $product_id, $variation_id, $downloadable_files ); 
  1616.  
  1617. /** 
  1618. * Disable DFW feature pointer. 
  1619. */ 
  1620. public function disable_dfw_feature_pointer() { 
  1621. $screen = get_current_screen(); 
  1622.  
  1623. if ( $screen && 'product' === $screen->id && 'post' === $screen->base ) { 
  1624. remove_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_wp410_dfw' ) ); 
  1625.  
  1626. /** 
  1627. * Removes products, orders, and coupons from the list of post types that support "View Mode" switching. 
  1628. * View mode is seen on posts where you can switch between list or excerpt. Our post types don't support 
  1629. * it, so we want to hide the useless UI from the screen options tab. 
  1630. * @since 2.6 
  1631. * @param array $post_types Array of post types supporting view mode 
  1632. * @return array Array of post types supporting view mode, without products, orders, and coupons 
  1633. */ 
  1634. public function disable_view_mode_options( $post_types ) { 
  1635. unset( $post_types['product'], $post_types['shop_order'], $post_types['shop_coupon'] ); 
  1636. return $post_types; 
  1637.  
  1638. /** 
  1639. * Show blank slate. 
  1640. */ 
  1641. public function maybe_render_blank_state( $which ) { 
  1642. global $post_type; 
  1643.  
  1644. if ( in_array( $post_type, array( 'shop_order', 'product', 'shop_coupon' ) ) && 'bottom' === $which ) { 
  1645. $counts = (array) wp_count_posts( $post_type ); 
  1646. unset( $counts['auto-draft'] ); 
  1647. $count = array_sum( $counts ); 
  1648.  
  1649. if ( 0 < $count ) { 
  1650. return; 
  1651.  
  1652. echo '<div class="woocommerce-BlankState">'; 
  1653.  
  1654. switch ( $post_type ) { 
  1655. case 'shop_order' : 
  1656. ?> 
  1657. <h2 class="woocommerce-BlankState-message"><?php _e( 'When you receive a new order, it will appear here.', 'woocommerce' ); ?></h2> 
  1658. <a class="woocommerce-BlankState-cta button-primary button" target="_blank" href="https://docs.woocommerce.com/document/managing-orders/?utm_source=blankslate&utm_medium=product&utm_content=ordersdoc&utm_campaign=woocommerceplugin"><?php _e( 'Learn more about orders', 'woocommerce' ); ?></a> 
  1659. <?php 
  1660. break; 
  1661. case 'shop_coupon' : 
  1662. ?> 
  1663. <h2 class="woocommerce-BlankState-message"><?php _e( 'Coupons are a great way to offer discounts and rewards to your customers. They will appear here once created.', 'woocommerce' ); ?></h2> 
  1664. <a class="woocommerce-BlankState-cta button-primary button" target="_blank" href="https://docs.woocommerce.com/document/coupon-management/?utm_source=blankslate&utm_medium=product&utm_content=couponsdoc&utm_campaign=woocommerceplugin"><?php _e( 'Learn more about coupons', 'woocommerce' ); ?></a> 
  1665. <?php 
  1666. break; 
  1667. case 'product' : 
  1668. ?> 
  1669. <h2 class="woocommerce-BlankState-message"><?php _e( 'Ready to start selling something awesome?', 'woocommerce' ); ?></h2> 
  1670. <a class="woocommerce-BlankState-cta button-primary button" href="<?php echo esc_url( admin_url( 'post-new.php?post_type=product&tutorial=true' ) ); ?>"><?php _e( 'Create your first product!', 'woocommerce' ); ?></a> 
  1671. <?php 
  1672. break; 
  1673.  
  1674. echo '<style type="text/css">#posts-filter .wp-list-table, #posts-filter .tablenav.top, .tablenav.bottom .actions, .wrap .subsubsub { display: none; } </style></div>'; 
  1675.  
  1676. /** 
  1677. * When editing the shop page, we should hide templates. 
  1678. * @return array 
  1679. */ 
  1680. public function hide_cpt_archive_templates( $page_templates, $class, $post ) { 
  1681. $shop_page_id = wc_get_page_id( 'shop' ); 
  1682.  
  1683. if ( $post && absint( $shop_page_id ) === absint( $post->ID ) ) { 
  1684. $page_templates = array(); 
  1685.  
  1686. return $page_templates; 
  1687.  
  1688. /** 
  1689. * Show a notice above the CPT archive. 
  1690. */ 
  1691. public function show_cpt_archive_notice( $post ) { 
  1692. $shop_page_id = wc_get_page_id( 'shop' ); 
  1693.  
  1694. if ( $post && absint( $shop_page_id ) === absint( $post->ID ) ) { 
  1695. ?> 
  1696. <div class="notice notice-info"> 
  1697. <p><?php printf( __( '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> 
  1698. </div> 
  1699. <?php