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. // WP List table columns. Defined here so they are always available for events such as inline editing. 
  14. add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ) ); 
  15. add_filter( 'manage_shop_coupon_posts_columns', array( $this, 'shop_coupon_columns' ) ); 
  16. add_filter( 'manage_shop_order_posts_columns', array( $this, 'shop_order_columns' ) ); 
  17.  
  18. add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_columns' ), 2 ); 
  19. add_action( 'manage_shop_coupon_posts_custom_column', array( $this, 'render_shop_coupon_columns' ), 2 ); 
  20. add_action( 'manage_shop_order_posts_custom_column', array( $this, 'render_shop_order_columns' ), 2 ); 
  21.  
  22. add_filter( 'manage_edit-product_sortable_columns', array( $this, 'product_sortable_columns' ) ); 
  23. add_filter( 'manage_edit-shop_coupon_sortable_columns', array( $this, 'shop_coupon_sortable_columns' ) ); 
  24. add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'shop_order_sortable_columns' ) ); 
  25.  
  26. add_filter( 'bulk_actions-edit-shop_order', array( $this, 'shop_order_bulk_actions' ) ); 
  27.  
  28. add_filter( 'list_table_primary_column', array( $this, 'list_table_primary_column' ), 10, 2 ); 
  29. add_filter( 'post_row_actions', array( $this, 'row_actions' ), 2, 100 ); 
  30.  
  31. // Views 
  32. add_filter( 'views_edit-product', array( $this, 'product_sorting_link' ) ); 
  33.  
  34. // Bulk / quick edit 
  35. add_action( 'bulk_edit_custom_box', array( $this, 'bulk_edit' ), 10, 2 ); 
  36. add_action( 'quick_edit_custom_box', array( $this, 'quick_edit' ), 10, 2 ); 
  37. add_action( 'save_post', array( $this, 'bulk_and_quick_edit_save_post' ), 10, 2 ); 
  38. add_action( 'admin_footer', array( $this, 'bulk_admin_footer' ), 10 ); 
  39. add_action( 'load-edit.php', array( $this, 'bulk_action' ) ); 
  40. add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) ); 
  41.  
  42. // Order Search 
  43. add_filter( 'get_search_query', array( $this, 'shop_order_search_label' ) ); 
  44. add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) ); 
  45. add_action( 'parse_query', array( $this, 'shop_order_search_custom_fields' ) ); 
  46.  
  47. // Filters 
  48. add_action( 'restrict_manage_posts', array( $this, 'restrict_manage_posts' ) ); 
  49. add_filter( 'request', array( $this, 'request_query' ) ); 
  50. add_filter( 'parse_query', array( $this, 'product_filters_query' ) ); 
  51. add_filter( 'posts_search', array( $this, 'product_search' ) ); 
  52.  
  53. // Status transitions 
  54. add_action( 'delete_post', array( $this, 'delete_post' ) ); 
  55. add_action( 'wp_trash_post', array( $this, 'trash_post' ) ); 
  56. add_action( 'untrash_post', array( $this, 'untrash_post' ) ); 
  57. add_action( 'before_delete_post', array( $this, 'delete_order_items' ) ); 
  58. add_action( 'before_delete_post', array( $this, 'delete_order_downloadable_permissions' ) ); 
  59.  
  60. // Edit post screens 
  61. add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 ); 
  62. add_action( 'edit_form_after_title', array( $this, 'edit_form_after_title' ) ); 
  63. add_filter( 'default_hidden_meta_boxes', array( $this, 'hidden_meta_boxes' ), 10, 2 ); 
  64. add_action( 'post_submitbox_misc_actions', array( $this, 'product_data_visibility' ) ); 
  65.  
  66. // Uploads 
  67. add_filter( 'upload_dir', array( $this, 'upload_dir' ) ); 
  68. add_action( 'media_upload_downloadable_product', array( $this, 'media_upload_downloadable_product' ) ); 
  69.  
  70. if ( ! function_exists( 'duplicate_post_plugin_activation' ) ) { 
  71. include( 'class-wc-admin-duplicate-product.php' ); 
  72.  
  73. // Meta-Box Class 
  74. include_once( 'class-wc-admin-meta-boxes.php' ); 
  75.  
  76. // Download permissions 
  77. add_action( 'woocommerce_process_product_file_download_paths', array( $this, 'process_product_file_download_paths' ), 10, 3 ); 
  78.  
  79. // Disable DFW feature pointer 
  80. add_action( 'admin_footer', array( $this, 'disable_dfw_feature_pointer' ) ); 
  81.  
  82. // Disable post type view mode options 
  83. add_filter( 'view_mode_post_types', array( $this, 'disable_view_mode_options' ) ); 
  84.  
  85. // If first time editing, disable columns by default. 
  86. if ( false === get_user_option( 'manageedit-shop_ordercolumnshidden' ) ) { 
  87. $user = wp_get_current_user(); 
  88. update_user_option( $user->ID, 'manageedit-shop_ordercolumnshidden', array( 0 => 'billing_address' ), true ); 
  89.  
  90. // Show blank state 
  91. add_action( 'manage_posts_extra_tablenav', array( $this, 'maybe_render_blank_state' ) ); 
  92.  
  93. /** 
  94. * Change messages when a post type is updated. 
  95. * @param array $messages 
  96. * @return array 
  97. */ 
  98. public function post_updated_messages( $messages ) { 
  99. global $post, $post_ID; 
  100.  
  101. $messages['product'] = array( 
  102. 0 => '', // Unused. Messages start at index 1. 
  103. 1 => sprintf( __( 'Product updated. <a href="%s">View Product</a>', 'woocommerce' ), esc_url( get_permalink( $post_ID ) ) ),  
  104. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  105. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  106. 4 => __( 'Product updated.', 'woocommerce' ),  
  107. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Product restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  108. 6 => sprintf( __( 'Product published. <a href="%s">View Product</a>', 'woocommerce' ), esc_url( get_permalink( $post_ID ) ) ),  
  109. 7 => __( 'Product saved.', 'woocommerce' ),  
  110. 8 => sprintf( __( 'Product submitted. <a target="_blank" href="%s">Preview Product</a>', 'woocommerce' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),  
  111. 9 => sprintf( __( 'Product scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview Product</a>', 'woocommerce' ),  
  112. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post_ID ) ) ),  
  113. 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 ) ) ) ),  
  114. ); 
  115.  
  116. $messages['shop_order'] = array( 
  117. 0 => '', // Unused. Messages start at index 1. 
  118. 1 => __( 'Order updated.', 'woocommerce' ),  
  119. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  120. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  121. 4 => __( 'Order updated.', 'woocommerce' ),  
  122. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Order restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  123. 6 => __( 'Order updated.', 'woocommerce' ),  
  124. 7 => __( 'Order saved.', 'woocommerce' ),  
  125. 8 => __( 'Order submitted.', 'woocommerce' ),  
  126. 9 => sprintf( __( 'Order scheduled for: <strong>%1$s</strong>.', 'woocommerce' ),  
  127. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) ),  
  128. 10 => __( 'Order draft updated.', 'woocommerce' ),  
  129. 11 => __( 'Order updated and email sent.', 'woocommerce' ) 
  130. ); 
  131.  
  132. $messages['shop_coupon'] = array( 
  133. 0 => '', // Unused. Messages start at index 1. 
  134. 1 => __( 'Coupon updated.', 'woocommerce' ),  
  135. 2 => __( 'Custom field updated.', 'woocommerce' ),  
  136. 3 => __( 'Custom field deleted.', 'woocommerce' ),  
  137. 4 => __( 'Coupon updated.', 'woocommerce' ),  
  138. 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Coupon restored to revision from %s', 'woocommerce' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  139. 6 => __( 'Coupon updated.', 'woocommerce' ),  
  140. 7 => __( 'Coupon saved.', 'woocommerce' ),  
  141. 8 => __( 'Coupon submitted.', 'woocommerce' ),  
  142. 9 => sprintf( __( 'Coupon scheduled for: <strong>%1$s</strong>.', 'woocommerce' ),  
  143. date_i18n( __( 'M j, Y @ G:i', 'woocommerce' ), strtotime( $post->post_date ) ) ),  
  144. 10 => __( 'Coupon draft updated.', 'woocommerce' ) 
  145. ); 
  146.  
  147. return $messages; 
  148.  
  149. /** 
  150. * Specify custom bulk actions messages for different post types. 
  151. * @param array $bulk_messages 
  152. * @param array $bulk_counts 
  153. * @return array 
  154. */ 
  155. public function bulk_post_updated_messages( $bulk_messages, $bulk_counts ) { 
  156.  
  157. $bulk_messages['product'] = array( 
  158. 'updated' => _n( '%s product updated.', '%s products updated.', $bulk_counts['updated'], 'woocommerce' ),  
  159. 'locked' => _n( '%s product not updated, somebody is editing it.', '%s products not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  160. 'deleted' => _n( '%s product permanently deleted.', '%s products permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  161. 'trashed' => _n( '%s product moved to the Trash.', '%s products moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  162. 'untrashed' => _n( '%s product restored from the Trash.', '%s products restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  163. ); 
  164.  
  165. $bulk_messages['shop_order'] = array( 
  166. 'updated' => _n( '%s order updated.', '%s orders updated.', $bulk_counts['updated'], 'woocommerce' ),  
  167. 'locked' => _n( '%s order not updated, somebody is editing it.', '%s orders not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  168. 'deleted' => _n( '%s order permanently deleted.', '%s orders permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  169. 'trashed' => _n( '%s order moved to the Trash.', '%s orders moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  170. 'untrashed' => _n( '%s order restored from the Trash.', '%s orders restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  171. ); 
  172.  
  173. $bulk_messages['shop_coupon'] = array( 
  174. 'updated' => _n( '%s coupon updated.', '%s coupons updated.', $bulk_counts['updated'], 'woocommerce' ),  
  175. 'locked' => _n( '%s coupon not updated, somebody is editing it.', '%s coupons not updated, somebody is editing them.', $bulk_counts['locked'], 'woocommerce' ),  
  176. 'deleted' => _n( '%s coupon permanently deleted.', '%s coupons permanently deleted.', $bulk_counts['deleted'], 'woocommerce' ),  
  177. 'trashed' => _n( '%s coupon moved to the Trash.', '%s coupons moved to the Trash.', $bulk_counts['trashed'], 'woocommerce' ),  
  178. 'untrashed' => _n( '%s coupon restored from the Trash.', '%s coupons restored from the Trash.', $bulk_counts['untrashed'], 'woocommerce' ),  
  179. ); 
  180.  
  181. return $bulk_messages; 
  182.  
  183. /** 
  184. * Define custom columns for products. 
  185. * @param array $existing_columns 
  186. * @return array 
  187. */ 
  188. public function product_columns( $existing_columns ) { 
  189. if ( empty( $existing_columns ) && ! is_array( $existing_columns ) ) { 
  190. $existing_columns = array(); 
  191.  
  192. unset( $existing_columns['title'], $existing_columns['comments'], $existing_columns['date'] ); 
  193.  
  194. $columns = array(); 
  195. $columns['cb'] = '<input type="checkbox" />'; 
  196. $columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>'; 
  197. $columns['name'] = __( 'Name', 'woocommerce' ); 
  198.  
  199. if ( wc_product_sku_enabled() ) { 
  200. $columns['sku'] = __( 'SKU', 'woocommerce' ); 
  201.  
  202. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  203. $columns['is_in_stock'] = __( 'Stock', 'woocommerce' ); 
  204.  
  205. $columns['price'] = __( 'Price', 'woocommerce' ); 
  206. $columns['product_cat'] = __( 'Categories', 'woocommerce' ); 
  207. $columns['product_tag'] = __( 'Tags', 'woocommerce' ); 
  208. $columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>'; 
  209. $columns['product_type'] = '<span class="wc-type parent-tips" data-tip="' . esc_attr__( 'Type', 'woocommerce' ) . '">' . __( 'Type', 'woocommerce' ) . '</span>'; 
  210. $columns['date'] = __( 'Date', 'woocommerce' ); 
  211.  
  212. return array_merge( $columns, $existing_columns ); 
  213.  
  214.  
  215. /** 
  216. * Define custom columns for coupons. 
  217. * @param array $existing_columns 
  218. * @return array 
  219. */ 
  220. public function shop_coupon_columns( $existing_columns ) { 
  221. $columns = array(); 
  222. $columns['cb'] = $existing_columns['cb']; 
  223. $columns['coupon_code'] = __( 'Code', 'woocommerce' ); 
  224. $columns['type'] = __( 'Coupon type', 'woocommerce' ); 
  225. $columns['amount'] = __( 'Coupon amount', 'woocommerce' ); 
  226. $columns['description'] = __( 'Description', 'woocommerce' ); 
  227. $columns['products'] = __( 'Product IDs', 'woocommerce' ); 
  228. $columns['usage'] = __( 'Usage / Limit', 'woocommerce' ); 
  229. $columns['expiry_date'] = __( 'Expiry date', 'woocommerce' ); 
  230.  
  231. return $columns; 
  232.  
  233. /** 
  234. * Define custom columns for orders. 
  235. * @param array $existing_columns 
  236. * @return array 
  237. */ 
  238. public function shop_order_columns( $existing_columns ) { 
  239. $columns = array(); 
  240. $columns['cb'] = $existing_columns['cb']; 
  241. $columns['order_status'] = '<span class="status_head tips" data-tip="' . esc_attr__( 'Status', 'woocommerce' ) . '">' . esc_attr__( 'Status', 'woocommerce' ) . '</span>'; 
  242. $columns['order_title'] = __( 'Order', 'woocommerce' ); 
  243. $columns['order_items'] = __( 'Purchased', 'woocommerce' ); 
  244. $columns['billing_address'] = __( 'Billing', 'woocommerce' ); 
  245. $columns['shipping_address'] = __( 'Ship to', 'woocommerce' ); 
  246. $columns['customer_message'] = '<span class="notes_head tips" data-tip="' . esc_attr__( 'Customer Message', 'woocommerce' ) . '">' . esc_attr__( 'Customer Message', 'woocommerce' ) . '</span>'; 
  247. $columns['order_notes'] = '<span class="order-notes_head tips" data-tip="' . esc_attr__( 'Order Notes', 'woocommerce' ) . '">' . esc_attr__( 'Order Notes', 'woocommerce' ) . '</span>'; 
  248. $columns['order_date'] = __( 'Date', 'woocommerce' ); 
  249. $columns['order_total'] = __( 'Total', 'woocommerce' ); 
  250. $columns['order_actions'] = __( 'Actions', 'woocommerce' ); 
  251.  
  252. return $columns; 
  253.  
  254. /** 
  255. * Ouput custom columns for products. 
  256. * @param string $column 
  257. */ 
  258. public function render_product_columns( $column ) { 
  259. global $post, $the_product; 
  260.  
  261. if ( empty( $the_product ) || $the_product->id != $post->ID ) { 
  262. $the_product = wc_get_product( $post ); 
  263.  
  264. switch ( $column ) { 
  265. case 'thumb' : 
  266. echo '<a href="' . get_edit_post_link( $post->ID ) . '">' . $the_product->get_image( 'thumbnail' ) . '</a>'; 
  267. break; 
  268. case 'name' : 
  269. $edit_link = get_edit_post_link( $post->ID ); 
  270. $title = _draft_or_post_title(); 
  271.  
  272. echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>'; 
  273.  
  274. _post_states( $post ); 
  275.  
  276. echo '</strong>'; 
  277.  
  278. if ( $post->post_parent > 0 ) { 
  279. echo '  ← <a href="'. get_edit_post_link( $post->post_parent ) .'">'. get_the_title( $post->post_parent ) .'</a>'; 
  280.  
  281. // Excerpt view 
  282. if ( isset( $_GET['mode'] ) && 'excerpt' == $_GET['mode'] ) { 
  283. echo apply_filters( 'the_excerpt', $post->post_excerpt ); 
  284.  
  285. $this->_render_product_row_actions( $post, $title ); 
  286.  
  287. get_inline_data( $post ); 
  288.  
  289. /** Custom inline data for woocommerce. */ 
  290. echo ' 
  291. <div class="hidden" id="woocommerce_inline_' . $post->ID . '"> 
  292. <div class="menu_order">' . $post->menu_order . '</div> 
  293. <div class="sku">' . $the_product->sku . '</div> 
  294. <div class="regular_price">' . $the_product->regular_price . '</div> 
  295. <div class="sale_price">' . $the_product->sale_price . '</div> 
  296. <div class="weight">' . $the_product->weight . '</div> 
  297. <div class="length">' . $the_product->length . '</div> 
  298. <div class="width">' . $the_product->width . '</div> 
  299. <div class="height">' . $the_product->height . '</div> 
  300. <div class="shipping_class">' . $the_product->get_shipping_class() . '</div> 
  301. <div class="visibility">' . $the_product->visibility . '</div> 
  302. <div class="stock_status">' . $the_product->stock_status . '</div> 
  303. <div class="stock">' . $the_product->stock . '</div> 
  304. <div class="manage_stock">' . $the_product->manage_stock . '</div> 
  305. <div class="featured">' . $the_product->featured . '</div> 
  306. <div class="product_type">' . $the_product->product_type . '</div> 
  307. <div class="product_is_virtual">' . $the_product->virtual . '</div> 
  308. <div class="tax_status">' . $the_product->tax_status . '</div> 
  309. <div class="tax_class">' . $the_product->tax_class . '</div> 
  310. <div class="backorders">' . $the_product->backorders . '</div> 
  311. </div> 
  312. '; 
  313.  
  314. break; 
  315. case 'sku' : 
  316. echo $the_product->get_sku() ? $the_product->get_sku() : '<span class="na">–</span>'; 
  317. break; 
  318. case 'product_type' : 
  319. if ( 'grouped' == $the_product->product_type ) { 
  320. echo '<span class="product-type tips grouped" data-tip="' . esc_attr__( 'Grouped', 'woocommerce' ) . '"></span>'; 
  321. } elseif ( 'external' == $the_product->product_type ) { 
  322. echo '<span class="product-type tips external" data-tip="' . esc_attr__( 'External/Affiliate', 'woocommerce' ) . '"></span>'; 
  323. } elseif ( 'simple' == $the_product->product_type ) { 
  324.  
  325. if ( $the_product->is_virtual() ) { 
  326. echo '<span class="product-type tips virtual" data-tip="' . esc_attr__( 'Virtual', 'woocommerce' ) . '"></span>'; 
  327. } elseif ( $the_product->is_downloadable() ) { 
  328. echo '<span class="product-type tips downloadable" data-tip="' . esc_attr__( 'Downloadable', 'woocommerce' ) . '"></span>'; 
  329. } else { 
  330. echo '<span class="product-type tips simple" data-tip="' . esc_attr__( 'Simple', 'woocommerce' ) . '"></span>'; 
  331.  
  332. } elseif ( 'variable' == $the_product->product_type ) { 
  333. echo '<span class="product-type tips variable" data-tip="' . esc_attr__( 'Variable', 'woocommerce' ) . '"></span>'; 
  334. } else { 
  335. // Assuming that we have other types in future 
  336. echo '<span class="product-type tips ' . $the_product->product_type . '" data-tip="' . ucfirst( $the_product->product_type ) . '"></span>'; 
  337. break; 
  338. case 'price' : 
  339. echo $the_product->get_price_html() ? $the_product->get_price_html() : '<span class="na">–</span>'; 
  340. break; 
  341. case 'product_cat' : 
  342. case 'product_tag' : 
  343. if ( ! $terms = get_the_terms( $post->ID, $column ) ) { 
  344. echo '<span class="na">–</span>'; 
  345. } else { 
  346. $termlist = array(); 
  347. foreach ( $terms as $term ) { 
  348. $termlist[] = '<a href="' . admin_url( 'edit.php?' . $column . '=' . $term->slug . '&post_type=product' ) . ' ">' . $term->name . '</a>'; 
  349.  
  350. echo implode( ', ', $termlist ); 
  351. break; 
  352. case 'featured' : 
  353. $url = wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_feature_product&product_id=' . $post->ID ), 'woocommerce-feature-product' ); 
  354. echo '<a href="' . esc_url( $url ) . '" title="'. __( 'Toggle featured', 'woocommerce' ) . '">'; 
  355. if ( $the_product->is_featured() ) { 
  356. echo '<span class="wc-featured tips" data-tip="' . esc_attr__( 'Yes', 'woocommerce' ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  357. } else { 
  358. echo '<span class="wc-featured not-featured tips" data-tip="' . esc_attr__( 'No', 'woocommerce' ) . '">' . __( 'No', 'woocommerce' ) . '</span>'; 
  359. echo '</a>'; 
  360. break; 
  361. case 'is_in_stock' : 
  362.  
  363. if ( $the_product->is_in_stock() ) { 
  364. $stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>'; 
  365. } else { 
  366. $stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>'; 
  367.  
  368. // If the product has children, a single stock level would be misleading as some could be -ve and some +ve, some managed/some unmanaged etc so hide stock level in this case. 
  369. if ( $the_product->managing_stock() && ! sizeof( $the_product->get_children() ) ) { 
  370. $stock_html .= ' (' . $the_product->get_total_stock() . ')'; 
  371.  
  372. echo apply_filters( 'woocommerce_admin_stock_html', $stock_html, $the_product ); 
  373.  
  374. break; 
  375. default : 
  376. break; 
  377.  
  378. /** 
  379. * Render product row actions for old version of WordPress. 
  380. * Since WordPress 4.3 we don't have to build the row actions. 
  381. * @param WP_Post $post 
  382. * @param string $title 
  383. */ 
  384. private function _render_product_row_actions( $post, $title ) { 
  385. global $wp_version; 
  386.  
  387. if ( version_compare( $wp_version, '4.3-beta', '>=' ) ) { 
  388. return; 
  389.  
  390. $post_type_object = get_post_type_object( $post->post_type ); 
  391. $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID ); 
  392.  
  393. // Get actions 
  394. $actions = array(); 
  395.  
  396. $actions['id'] = 'ID: ' . $post->ID; 
  397.  
  398. if ( $can_edit_post && 'trash' != $post->post_status ) { 
  399. $actions['edit'] = '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this item', 'woocommerce' ) ) . '">' . __( 'Edit', 'woocommerce' ) . '</a>'; 
  400. $actions['inline hide-if-no-js'] = '<a href="#" class="editinline" title="' . esc_attr( __( 'Edit this item inline', 'woocommerce' ) ) . '">' . __( 'Quick Edit', 'woocommerce' ) . '</a>'; 
  401. if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) { 
  402. if ( 'trash' == $post->post_status ) { 
  403. $actions['untrash'] = '<a title="' . esc_attr( __( 'Restore this item from the Trash', 'woocommerce' ) ) . '" href="' . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . '">' . __( 'Restore', 'woocommerce' ) . '</a>'; 
  404. } elseif ( EMPTY_TRASH_DAYS ) { 
  405. $actions['trash'] = '<a class="submitdelete" title="' . esc_attr( __( 'Move this item to the Trash', 'woocommerce' ) ) . '" href="' . get_delete_post_link( $post->ID ) . '">' . __( 'Trash', 'woocommerce' ) . '</a>'; 
  406.  
  407. if ( 'trash' == $post->post_status || ! EMPTY_TRASH_DAYS ) { 
  408. $actions['delete'] = '<a class="submitdelete" title="' . esc_attr( __( 'Delete this item permanently', 'woocommerce' ) ) . '" href="' . get_delete_post_link( $post->ID, '', true ) . '">' . __( 'Delete Permanently', 'woocommerce' ) . '</a>'; 
  409. if ( $post_type_object->public ) { 
  410. if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) { 
  411. if ( $can_edit_post ) 
  412. $actions['view'] = '<a href="' . esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) . '" title="' . esc_attr( sprintf( __( 'Preview “%s”', 'woocommerce' ), $title ) ) . '" rel="permalink">' . __( 'Preview', 'woocommerce' ) . '</a>'; 
  413. } elseif ( 'trash' != $post->post_status ) { 
  414. $actions['view'] = '<a href="' . get_permalink( $post->ID ) . '" title="' . esc_attr( sprintf( __( 'View “%s”', 'woocommerce' ), $title ) ) . '" rel="permalink">' . __( 'View', 'woocommerce' ) . '</a>'; 
  415.  
  416. $actions = apply_filters( 'post_row_actions', $actions, $post ); 
  417.  
  418. echo '<div class="row-actions">'; 
  419.  
  420. $i = 0; 
  421. $action_count = sizeof( $actions ); 
  422.  
  423. foreach ( $actions as $action => $link ) { 
  424. ++$i; 
  425. ( $i == $action_count ) ? $sep = '' : $sep = ' | '; 
  426. echo '<span class="' . $action . '">' . $link . $sep . '</span>'; 
  427. echo '</div>'; 
  428.  
  429. /** 
  430. * Output custom columns for coupons. 
  431. * @param string $column 
  432. */ 
  433. public function render_shop_coupon_columns( $column ) { 
  434. global $post, $woocommerce; 
  435.  
  436. switch ( $column ) { 
  437. case 'coupon_code' : 
  438. $edit_link = get_edit_post_link( $post->ID ); 
  439. $title = _draft_or_post_title(); 
  440.  
  441. echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>'; 
  442.  
  443. _post_states( $post ); 
  444.  
  445. echo '</strong>'; 
  446.  
  447. $this->_render_shop_coupon_row_actions( $post, $title ); 
  448. break; 
  449. case 'type' : 
  450. echo esc_html( wc_get_coupon_type( get_post_meta( $post->ID, 'discount_type', true ) ) ); 
  451. break; 
  452. case 'amount' : 
  453. echo esc_html( get_post_meta( $post->ID, 'coupon_amount', true ) ); 
  454. break; 
  455. case 'products' : 
  456. $product_ids = get_post_meta( $post->ID, 'product_ids', true ); 
  457. $product_ids = $product_ids ? array_map( 'absint', explode( ', ', $product_ids ) ) : array(); 
  458.  
  459. if ( sizeof( $product_ids ) > 0 ) { 
  460. echo esc_html( implode( ', ', $product_ids ) ); 
  461. } else { 
  462. echo '–'; 
  463. break; 
  464. case 'usage_limit' : 
  465. $usage_limit = get_post_meta( $post->ID, 'usage_limit', true ); 
  466.  
  467. if ( $usage_limit ) { 
  468. echo esc_html( $usage_limit ); 
  469. } else { 
  470. echo '–'; 
  471. break; 
  472. case 'usage' : 
  473. $usage_count = absint( get_post_meta( $post->ID, 'usage_count', true ) ); 
  474. $usage_limit = esc_html( get_post_meta( $post->ID, 'usage_limit', true ) ); 
  475. $usage_url = sprintf( '<a href="%s">%s</a>', admin_url( sprintf( 'edit.php?s=%s&post_status=all&post_type=shop_order', esc_html( $post->post_title ) ) ), $usage_count ); 
  476.  
  477. printf( _x( '%1$s / %2$s', 'Count / Limit', 'woocommerce' ), $usage_url, $usage_limit ? $usage_limit : '∞' ); 
  478. break; 
  479. case 'expiry_date' : 
  480. $expiry_date = get_post_meta( $post->ID, 'expiry_date', true ); 
  481.  
  482. if ( $expiry_date ) { 
  483. echo esc_html( date_i18n( 'F j, Y', strtotime( $expiry_date ) ) ); 
  484. } else { 
  485. echo '–'; 
  486. break; 
  487. case 'description' : 
  488. echo wp_kses_post( $post->post_excerpt ); 
  489. break; 
  490.  
  491. /** 
  492. * Render shop_coupon row actions for old version of WordPress. 
  493. * Since WordPress 4.3 we don't have to build the row actions. 
  494. * @param WP_Post $post 
  495. * @param string $title 
  496. */ 
  497. private function _render_shop_coupon_row_actions( $post, $title ) { 
  498. global $wp_version; 
  499.  
  500. if ( version_compare( $wp_version, '4.3-beta', '>=' ) ) { 
  501. return; 
  502.  
  503. $post_type_object = get_post_type_object( $post->post_type ); 
  504.  
  505. // Get actions 
  506. $actions = array(); 
  507.  
  508. if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) ) { 
  509. $actions['edit'] = '<a href="' . admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', $post->ID ) ) . '">' . __( 'Edit', 'woocommerce' ) . '</a>'; 
  510.  
  511. if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) { 
  512.  
  513. if ( 'trash' == $post->post_status ) { 
  514. $actions['untrash'] = "<a title='" . esc_attr( __( 'Restore this item from the Trash', 'woocommerce' ) ) . "' href='" . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore', 'woocommerce' ) . "</a>"; 
  515. } elseif ( EMPTY_TRASH_DAYS ) { 
  516. $actions['trash'] = "<a class='submitdelete' title='" . esc_attr( __( 'Move this item to the Trash', 'woocommerce' ) ) . "' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Trash', 'woocommerce' ) . "</a>"; 
  517.  
  518. if ( 'trash' == $post->post_status || ! EMPTY_TRASH_DAYS ) { 
  519. $actions['delete'] = "<a class='submitdelete' title='" . esc_attr( __( 'Delete this item permanently', 'woocommerce' ) ) . "' href='" . get_delete_post_link( $post->ID, '', true ) . "'>" . __( 'Delete Permanently', 'woocommerce' ) . "</a>"; 
  520.  
  521. $actions = apply_filters( 'post_row_actions', $actions, $post ); 
  522.  
  523. echo '<div class="row-actions">'; 
  524.  
  525. $i = 0; 
  526. $action_count = sizeof( $actions ); 
  527.  
  528. foreach ( $actions as $action => $link ) { 
  529. ++$i; 
  530. ( $i == $action_count ) ? $sep = '' : $sep = ' | '; 
  531. echo "<span class='$action'>$link$sep</span>"; 
  532. echo '</div>'; 
  533.  
  534. /** 
  535. * Output custom columns for coupons. 
  536. * @param string $column 
  537. */ 
  538. public function render_shop_order_columns( $column ) { 
  539. global $post, $woocommerce, $the_order; 
  540.  
  541. if ( empty( $the_order ) || $the_order->id != $post->ID ) { 
  542. $the_order = wc_get_order( $post->ID ); 
  543.  
  544. switch ( $column ) { 
  545. case 'order_status' : 
  546.  
  547. printf( '<mark class="%s tips" data-tip="%s">%s</mark>', sanitize_title( $the_order->get_status() ), wc_get_order_status_name( $the_order->get_status() ), wc_get_order_status_name( $the_order->get_status() ) ); 
  548.  
  549. break; 
  550. case 'order_date' : 
  551.  
  552. if ( '0000-00-00 00:00:00' == $post->post_date ) { 
  553. $t_time = $h_time = __( 'Unpublished', 'woocommerce' ); 
  554. } else { 
  555. $t_time = get_the_time( __( 'Y/m/d g:i:s A', 'woocommerce' ), $post ); 
  556. $h_time = get_the_time( __( 'Y/m/d', 'woocommerce' ), $post ); 
  557.  
  558. echo '<abbr title="' . esc_attr( $t_time ) . '">' . esc_html( apply_filters( 'post_date_column_time', $h_time, $post ) ) . '</abbr>'; 
  559.  
  560. break; 
  561. case 'customer_message' : 
  562. if ( $the_order->customer_message ) { 
  563. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( $the_order->customer_message ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  564. } else { 
  565. echo '<span class="na">–</span>'; 
  566.  
  567. break; 
  568. case 'order_items' : 
  569.  
  570. echo '<a href="#" class="show_order_items">' . apply_filters( 'woocommerce_admin_order_item_count', sprintf( _n( '%d item', '%d items', $the_order->get_item_count(), 'woocommerce' ), $the_order->get_item_count() ), $the_order ) . '</a>'; 
  571.  
  572. if ( sizeof( $the_order->get_items() ) > 0 ) { 
  573.  
  574. echo '<table class="order_items" cellspacing="0">'; 
  575.  
  576. foreach ( $the_order->get_items() as $item ) { 
  577. $product = apply_filters( 'woocommerce_order_item_product', $the_order->get_product_from_item( $item ), $item ); 
  578. $item_meta = new WC_Order_Item_Meta( $item, $product ); 
  579. $item_meta_html = $item_meta->display( true, true ); 
  580. ?> 
  581. <tr class="<?php echo apply_filters( 'woocommerce_admin_order_item_class', '', $item, $the_order ); ?>"> 
  582. <td class="qty"><?php echo absint( $item['qty'] ); ?></td> 
  583. <td class="name"> 
  584. <?php if ( $product ) : ?> 
  585. <?php echo ( wc_product_sku_enabled() && $product->get_sku() ) ? $product->get_sku() . ' - ' : ''; ?><a href="<?php echo get_edit_post_link( $product->id ); ?>" title="<?php echo apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false ); ?>"><?php echo apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false ); ?></a> 
  586. <?php else : ?> 
  587. <?php echo apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false ); ?> 
  588. <?php endif; ?> 
  589. <?php if ( ! empty( $item_meta_html ) ) : ?> 
  590. <?php echo wc_help_tip( $item_meta_html ); ?> 
  591. <?php endif; ?> 
  592. </td> 
  593. </tr> 
  594. <?php 
  595.  
  596. echo '</table>'; 
  597.  
  598. } else echo '–'; 
  599. break; 
  600. case 'billing_address' : 
  601.  
  602. if ( $address = $the_order->get_formatted_billing_address() ) { 
  603. echo esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) ); 
  604. } else { 
  605. echo '–'; 
  606.  
  607. if ( $the_order->billing_phone ) { 
  608. echo '<small class="meta">' . __( 'Tel:', 'woocommerce' ) . ' ' . esc_html( $the_order->billing_phone ) . '</small>'; 
  609.  
  610. break; 
  611. case 'shipping_address' : 
  612.  
  613. if ( $address = $the_order->get_formatted_shipping_address() ) { 
  614. echo '<a target="_blank" href="' . esc_url( $the_order->get_shipping_address_map_url() ) . '">'. esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) ) .'</a>'; 
  615. } else { 
  616. echo '–'; 
  617.  
  618. if ( $the_order->get_shipping_method() ) { 
  619. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->get_shipping_method() ) . '</small>'; 
  620.  
  621. break; 
  622. case 'order_notes' : 
  623.  
  624. if ( $post->comment_count ) { 
  625.  
  626. // check the status of the post 
  627. $status = ( 'trash' !== $post->post_status ) ? '' : 'post-trashed'; 
  628.  
  629. $latest_notes = get_comments( array( 
  630. 'post_id' => $post->ID,  
  631. 'number' => 1,  
  632. 'status' => $status 
  633. ) ); 
  634.  
  635. $latest_note = current( $latest_notes ); 
  636.  
  637. if ( isset( $latest_note->comment_content ) && $post->comment_count == 1 ) { 
  638. echo '<span class="note-on tips" data-tip="' . wc_sanitize_tooltip( $latest_note->comment_content ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>'; 
  639. } elseif ( isset( $latest_note->comment_content ) ) { 
  640. 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>'; 
  641. } else { 
  642. 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>'; 
  643.  
  644. } else { 
  645. echo '<span class="na">–</span>'; 
  646.  
  647. break; 
  648. case 'order_total' : 
  649. echo $the_order->get_formatted_order_total(); 
  650.  
  651. if ( $the_order->payment_method_title ) { 
  652. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->payment_method_title ) . '</small>'; 
  653. break; 
  654. case 'order_title' : 
  655.  
  656. if ( $the_order->user_id ) { 
  657. $user_info = get_userdata( $the_order->user_id ); 
  658.  
  659. if ( ! empty( $user_info ) ) { 
  660.  
  661. $username = '<a href="user-edit.php?user_id=' . absint( $user_info->ID ) . '">'; 
  662.  
  663. if ( $user_info->first_name || $user_info->last_name ) { 
  664. $username .= esc_html( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), ucfirst( $user_info->first_name ), ucfirst( $user_info->last_name ) ) ); 
  665. } else { 
  666. $username .= esc_html( ucfirst( $user_info->display_name ) ); 
  667.  
  668. $username .= '</a>'; 
  669.  
  670. } else { 
  671. if ( $the_order->billing_first_name || $the_order->billing_last_name ) { 
  672. $username = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $the_order->billing_first_name, $the_order->billing_last_name ) ); 
  673. } else if ( $the_order->billing_company ) { 
  674. $username = trim( $the_order->billing_company ); 
  675. } else { 
  676. $username = __( 'Guest', 'woocommerce' ); 
  677.  
  678. printf( _x( '%s by %s', 'Order number by X', 'woocommerce' ), '<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>', $username ); 
  679.  
  680. if ( $the_order->billing_email ) { 
  681. echo '<small class="meta email"><a href="' . esc_url( 'mailto:' . $the_order->billing_email ) . '">' . esc_html( $the_order->billing_email ) . '</a></small>'; 
  682.  
  683. echo '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details', 'woocommerce' ) . '</span></button>'; 
  684.  
  685. break; 
  686. case 'order_actions' : 
  687.  
  688. ?><p> 
  689. <?php 
  690. do_action( 'woocommerce_admin_order_actions_start', $the_order ); 
  691.  
  692. $actions = array(); 
  693.  
  694. if ( $the_order->has_status( array( 'pending', 'on-hold' ) ) ) { 
  695. $actions['processing'] = array( 
  696. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),  
  697. 'name' => __( 'Processing', 'woocommerce' ),  
  698. 'action' => "processing" 
  699. ); 
  700.  
  701. if ( $the_order->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) { 
  702. $actions['complete'] = array( 
  703. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),  
  704. 'name' => __( 'Complete', 'woocommerce' ),  
  705. 'action' => "complete" 
  706. ); 
  707.  
  708. $actions['view'] = array( 
  709. 'url' => admin_url( 'post.php?post=' . $post->ID . '&action=edit' ),  
  710. 'name' => __( 'View', 'woocommerce' ),  
  711. 'action' => "view" 
  712. ); 
  713.  
  714. $actions = apply_filters( 'woocommerce_admin_order_actions', $actions, $the_order ); 
  715.  
  716. foreach ( $actions as $action ) { 
  717. 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'] ) ); 
  718.  
  719. do_action( 'woocommerce_admin_order_actions_end', $the_order ); 
  720. ?> 
  721. </p><?php 
  722.  
  723. break; 
  724.  
  725. /** 
  726. * Make columns sortable - https://gist.github.com/906872. 
  727. * @param array $columns 
  728. * @return array 
  729. */ 
  730. public function product_sortable_columns( $columns ) { 
  731. $custom = array( 
  732. 'price' => 'price',  
  733. 'featured' => array( 'featured', 1 ),  
  734. 'sku' => 'sku',  
  735. 'name' => 'title' 
  736. ); 
  737. return wp_parse_args( $custom, $columns ); 
  738.  
  739. /** 
  740. * Make columns sortable - https://gist.github.com/906872. 
  741. * @param array $columns 
  742. * @return array 
  743. */ 
  744. public function shop_coupon_sortable_columns( $columns ) { 
  745. return $columns; 
  746.  
  747. /** 
  748. * Make columns sortable - https://gist.github.com/906872. 
  749. * @param array $columns 
  750. * @return array 
  751. */ 
  752. public function shop_order_sortable_columns( $columns ) { 
  753. $custom = array( 
  754. 'order_title' => 'ID',  
  755. 'order_total' => 'order_total',  
  756. 'order_date' => 'date' 
  757. ); 
  758. unset( $columns['comments'] ); 
  759.  
  760. return wp_parse_args( $custom, $columns ); 
  761.  
  762. /** 
  763. * Remove edit from the bulk actions. 
  764. * @param array $actions 
  765. * @return array 
  766. */ 
  767. public function shop_order_bulk_actions( $actions ) { 
  768.  
  769. if ( isset( $actions['edit'] ) ) { 
  770. unset( $actions['edit'] ); 
  771.  
  772. return $actions; 
  773.  
  774. /** 
  775. * Set list table primary column for products and orders. 
  776. * Support for WordPress 4.3. 
  777. * @param string $default 
  778. * @param string $screen_id 
  779. * @return string 
  780. */ 
  781. public function list_table_primary_column( $default, $screen_id ) { 
  782.  
  783. if ( 'edit-product' === $screen_id ) { 
  784. return 'name'; 
  785.  
  786. if ( 'edit-shop_order' === $screen_id ) { 
  787. return 'order_title'; 
  788.  
  789. if ( 'edit-shop_coupon' === $screen_id ) { 
  790. return 'coupon_code'; 
  791.  
  792. return $default; 
  793.  
  794. /** 
  795. * Set row actions for products and orders. 
  796. * @param array $actions 
  797. * @param WP_Post $post 
  798. * @return array 
  799. */ 
  800. public function row_actions( $actions, $post ) { 
  801. if ( 'product' === $post->post_type ) { 
  802. return array_merge( array( 'id' => 'ID: ' . $post->ID ), $actions ); 
  803.  
  804. if ( in_array( $post->post_type, array( 'shop_order', 'shop_coupon' ) ) ) { 
  805. if ( isset( $actions['inline hide-if-no-js'] ) ) { 
  806. unset( $actions['inline hide-if-no-js'] ); 
  807.  
  808. return $actions; 
  809.  
  810. /** 
  811. * Product sorting link. 
  812. * Based on Simple Page Ordering by 10up (https://wordpress.org/extend/plugins/simple-page-ordering/). 
  813. * @param array $views 
  814. * @return array 
  815. */ 
  816. public function product_sorting_link( $views ) { 
  817. global $post_type, $wp_query; 
  818.  
  819. if ( ! current_user_can( 'edit_others_pages' ) ) { 
  820. return $views; 
  821.  
  822. $class = ( isset( $wp_query->query['orderby'] ) && $wp_query->query['orderby'] == 'menu_order title' ) ? 'current' : ''; 
  823. $query_string = remove_query_arg(array( 'orderby', 'order' )); 
  824. $query_string = add_query_arg( 'orderby', urlencode('menu_order title'), $query_string ); 
  825. $query_string = add_query_arg( 'order', urlencode('ASC'), $query_string ); 
  826. $views['byorder'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Sort Products', 'woocommerce' ) . '</a>'; 
  827.  
  828. return $views; 
  829.  
  830. /** 
  831. * Custom bulk edit - form. 
  832. * @param mixed $column_name 
  833. * @param mixed $post_type 
  834. */ 
  835. public function bulk_edit( $column_name, $post_type ) { 
  836.  
  837. if ( 'price' != $column_name || 'product' != $post_type ) { 
  838. return; 
  839.  
  840. $shipping_class = get_terms( 'product_shipping_class', array( 
  841. 'hide_empty' => false,  
  842. ) ); 
  843.  
  844. include( WC()->plugin_path() . '/includes/admin/views/html-bulk-edit-product.php' ); 
  845.  
  846. /** 
  847. * Custom quick edit - form. 
  848. * @param mixed $column_name 
  849. * @param mixed $post_type 
  850. */ 
  851. public function quick_edit( $column_name, $post_type ) { 
  852.  
  853. if ( 'price' != $column_name || 'product' != $post_type ) { 
  854. return; 
  855.  
  856. $shipping_class = get_terms( 'product_shipping_class', array( 
  857. 'hide_empty' => false,  
  858. ) ); 
  859.  
  860. include( WC()->plugin_path() . '/includes/admin/views/html-quick-edit-product.php' ); 
  861.  
  862. /** 
  863. * Quick and bulk edit saving. 
  864. * @param int $post_id 
  865. * @param WP_Post $post 
  866. * @return int 
  867. */ 
  868. public function bulk_and_quick_edit_save_post( $post_id, $post ) { 
  869.  
  870. // If this is an autosave, our form has not been submitted, so we don't want to do anything. 
  871. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 
  872. return $post_id; 
  873.  
  874. // Don't save revisions and autosaves 
  875. if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) { 
  876. return $post_id; 
  877.  
  878. // Check post type is product 
  879. if ( 'product' != $post->post_type ) { 
  880. return $post_id; 
  881.  
  882. // Check user permission 
  883. if ( ! current_user_can( 'edit_post', $post_id ) ) { 
  884. return $post_id; 
  885.  
  886. // Check nonces 
  887. if ( ! isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) ) { 
  888. return $post_id; 
  889. if ( isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) { 
  890. return $post_id; 
  891. if ( isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_bulk_edit_nonce'], 'woocommerce_bulk_edit_nonce' ) ) { 
  892. return $post_id; 
  893.  
  894. // Get the product and save 
  895. $product = wc_get_product( $post ); 
  896.  
  897. if ( ! empty( $_REQUEST['woocommerce_quick_edit'] ) ) { 
  898. $this->quick_edit_save( $post_id, $product ); 
  899. } else { 
  900. $this->bulk_edit_save( $post_id, $product ); 
  901.  
  902. // Clear transient 
  903. wc_delete_product_transients( $post_id ); 
  904.  
  905. return $post_id; 
  906.  
  907. /** 
  908. * Quick edit. 
  909. * @param integer $post_id 
  910. * @param WC_Product $product 
  911. */ 
  912. private function quick_edit_save( $post_id, $product ) { 
  913. global $wpdb; 
  914.  
  915. $old_regular_price = $product->regular_price; 
  916. $old_sale_price = $product->sale_price; 
  917.  
  918. // Save fields 
  919. if ( isset( $_REQUEST['_sku'] ) ) { 
  920. $sku = get_post_meta( $post_id, '_sku', true ); 
  921. $new_sku = (string) wc_clean( $_REQUEST['_sku'] ); 
  922.  
  923. if ( $new_sku !== $sku ) { 
  924. if ( ! empty( $new_sku ) ) { 
  925. $unique_sku = wc_product_has_unique_sku( $post_id, $new_sku ); 
  926. if ( $unique_sku ) { 
  927. update_post_meta( $post_id, '_sku', $new_sku ); 
  928. } else { 
  929. update_post_meta( $post_id, '_sku', '' ); 
  930.  
  931. if ( isset( $_REQUEST['_weight'] ) ) { 
  932. update_post_meta( $post_id, '_weight', wc_clean( $_REQUEST['_weight'] ) ); 
  933.  
  934. if ( isset( $_REQUEST['_length'] ) ) { 
  935. update_post_meta( $post_id, '_length', wc_clean( $_REQUEST['_length'] ) ); 
  936.  
  937. if ( isset( $_REQUEST['_width'] ) ) { 
  938. update_post_meta( $post_id, '_width', wc_clean( $_REQUEST['_width'] ) ); 
  939.  
  940. if ( isset( $_REQUEST['_height'] ) ) { 
  941. update_post_meta( $post_id, '_height', wc_clean( $_REQUEST['_height'] ) ); 
  942.  
  943. if ( ! empty( $_REQUEST['_shipping_class'] ) ) { 
  944. $shipping_class = '_no_shipping_class' == $_REQUEST['_shipping_class'] ? '' : wc_clean( $_REQUEST['_shipping_class'] ); 
  945.  
  946. wp_set_object_terms( $post_id, $shipping_class, 'product_shipping_class' ); 
  947.  
  948. if ( isset( $_REQUEST['_visibility'] ) ) { 
  949. if ( update_post_meta( $post_id, '_visibility', wc_clean( $_REQUEST['_visibility'] ) ) ) { 
  950. do_action( 'woocommerce_product_set_visibility', $post_id, wc_clean( $_REQUEST['_visibility'] ) ); 
  951.  
  952. if ( isset( $_REQUEST['_featured'] ) ) { 
  953. if ( update_post_meta( $post_id, '_featured', 'yes' ) ) { 
  954. delete_transient( 'wc_featured_products' ); 
  955. } else { 
  956. if ( update_post_meta( $post_id, '_featured', 'no' ) ) { 
  957. delete_transient( 'wc_featured_products' ); 
  958.  
  959. if ( isset( $_REQUEST['_tax_status'] ) ) { 
  960. update_post_meta( $post_id, '_tax_status', wc_clean( $_REQUEST['_tax_status'] ) ); 
  961.  
  962. if ( isset( $_REQUEST['_tax_class'] ) ) { 
  963. update_post_meta( $post_id, '_tax_class', wc_clean( $_REQUEST['_tax_class'] ) ); 
  964.  
  965. if ( $product->is_type('simple') || $product->is_type('external') ) { 
  966.  
  967. if ( isset( $_REQUEST['_regular_price'] ) ) { 
  968. $new_regular_price = $_REQUEST['_regular_price'] === '' ? '' : wc_format_decimal( $_REQUEST['_regular_price'] ); 
  969. update_post_meta( $post_id, '_regular_price', $new_regular_price ); 
  970. } else { 
  971. $new_regular_price = null; 
  972. if ( isset( $_REQUEST['_sale_price'] ) ) { 
  973. $new_sale_price = $_REQUEST['_sale_price'] === '' ? '' : wc_format_decimal( $_REQUEST['_sale_price'] ); 
  974. update_post_meta( $post_id, '_sale_price', $new_sale_price ); 
  975. } else { 
  976. $new_sale_price = null; 
  977.  
  978. // Handle price - remove dates and set to lowest 
  979. $price_changed = false; 
  980.  
  981. if ( ! is_null( $new_regular_price ) && $new_regular_price != $old_regular_price ) { 
  982. $price_changed = true; 
  983. } elseif ( ! is_null( $new_sale_price ) && $new_sale_price != $old_sale_price ) { 
  984. $price_changed = true; 
  985.  
  986. if ( $price_changed ) { 
  987. update_post_meta( $post_id, '_sale_price_dates_from', '' ); 
  988. update_post_meta( $post_id, '_sale_price_dates_to', '' ); 
  989.  
  990. if ( ! is_null( $new_sale_price ) && $new_sale_price !== '' ) { 
  991. update_post_meta( $post_id, '_price', $new_sale_price ); 
  992. } else { 
  993. update_post_meta( $post_id, '_price', $new_regular_price ); 
  994.  
  995. // Handle stock status 
  996. if ( isset( $_REQUEST['_stock_status'] ) ) { 
  997. $stock_status = wc_clean( $_REQUEST['_stock_status'] ); 
  998.  
  999. if ( $product->is_type( 'variable' ) ) { 
  1000. foreach ( $product->get_children() as $child_id ) { 
  1001. if ( 'yes' !== get_post_meta( $child_id, '_manage_stock', true ) ) { 
  1002. wc_update_product_stock_status( $child_id, $stock_status ); 
  1003.  
  1004. WC_Product_Variable::sync_stock_status( $post_id ); 
  1005. } else { 
  1006. wc_update_product_stock_status( $post_id, $stock_status ); 
  1007.  
  1008. // Handle stock 
  1009. if ( ! $product->is_type('grouped') ) { 
  1010. if ( isset( $_REQUEST['_manage_stock'] ) ) { 
  1011. update_post_meta( $post_id, '_manage_stock', 'yes' ); 
  1012. wc_update_product_stock( $post_id, wc_stock_amount( $_REQUEST['_stock'] ) ); 
  1013. } else { 
  1014. update_post_meta( $post_id, '_manage_stock', 'no' ); 
  1015. wc_update_product_stock( $post_id, 0 ); 
  1016.  
  1017. if ( ! empty( $_REQUEST['_backorders'] ) ) { 
  1018. update_post_meta( $post_id, '_backorders', wc_clean( $_REQUEST['_backorders'] ) ); 
  1019.  
  1020. do_action( 'woocommerce_product_quick_edit_save', $product ); 
  1021.  
  1022. /** 
  1023. * Bulk edit. 
  1024. * @param integer $post_id 
  1025. * @param WC_Product $product 
  1026. */ 
  1027. public function bulk_edit_save( $post_id, $product ) { 
  1028.  
  1029. $old_regular_price = $product->regular_price; 
  1030. $old_sale_price = $product->sale_price; 
  1031.  
  1032. // Save fields 
  1033. if ( ! empty( $_REQUEST['change_weight'] ) && isset( $_REQUEST['_weight'] ) ) { 
  1034. update_post_meta( $post_id, '_weight', wc_clean( stripslashes( $_REQUEST['_weight'] ) ) ); 
  1035.  
  1036. if ( ! empty( $_REQUEST['change_dimensions'] ) ) { 
  1037. if ( isset( $_REQUEST['_length'] ) ) { 
  1038. update_post_meta( $post_id, '_length', wc_clean( stripslashes( $_REQUEST['_length'] ) ) ); 
  1039. if ( isset( $_REQUEST['_width'] ) ) { 
  1040. update_post_meta( $post_id, '_width', wc_clean( stripslashes( $_REQUEST['_width'] ) ) ); 
  1041. if ( isset( $_REQUEST['_height'] ) ) { 
  1042. update_post_meta( $post_id, '_height', wc_clean( stripslashes( $_REQUEST['_height'] ) ) ); 
  1043.  
  1044. if ( ! empty( $_REQUEST['_tax_status'] ) ) { 
  1045. update_post_meta( $post_id, '_tax_status', wc_clean( $_REQUEST['_tax_status'] ) ); 
  1046.  
  1047. if ( ! empty( $_REQUEST['_tax_class'] ) ) { 
  1048. $tax_class = wc_clean( $_REQUEST['_tax_class'] ); 
  1049. if ( 'standard' == $tax_class ) { 
  1050. $tax_class = ''; 
  1051. update_post_meta( $post_id, '_tax_class', $tax_class ); 
  1052.  
  1053. if ( ! empty( $_REQUEST['_stock_status'] ) ) { 
  1054. $stock_status = wc_clean( $_REQUEST['_stock_status'] ); 
  1055.  
  1056. if ( $product->is_type( 'variable' ) ) { 
  1057. foreach ( $product->get_children() as $child_id ) { 
  1058. if ( 'yes' !== get_post_meta( $child_id, '_manage_stock', true ) ) { 
  1059. wc_update_product_stock_status( $child_id, $stock_status ); 
  1060.  
  1061. WC_Product_Variable::sync_stock_status( $post_id ); 
  1062. } else { 
  1063. wc_update_product_stock_status( $post_id, $stock_status ); 
  1064.  
  1065. if ( ! empty( $_REQUEST['_shipping_class'] ) ) { 
  1066. $shipping_class = '_no_shipping_class' == $_REQUEST['_shipping_class'] ? '' : wc_clean( $_REQUEST['_shipping_class'] ); 
  1067.  
  1068. wp_set_object_terms( $post_id, $shipping_class, 'product_shipping_class' ); 
  1069.  
  1070. if ( ! empty( $_REQUEST['_visibility'] ) ) { 
  1071. if ( update_post_meta( $post_id, '_visibility', wc_clean( $_REQUEST['_visibility'] ) ) ) { 
  1072. do_action( 'woocommerce_product_set_visibility', $post_id, wc_clean( $_REQUEST['_visibility'] ) ); 
  1073.  
  1074. if ( ! empty( $_REQUEST['_featured'] ) ) { 
  1075. if ( update_post_meta( $post_id, '_featured', stripslashes( $_REQUEST['_featured'] ) ) ) { 
  1076. delete_transient( 'wc_featured_products' ); 
  1077.  
  1078. // Sold Individually 
  1079. if ( ! empty( $_REQUEST['_sold_individually'] ) ) { 
  1080. if ( $_REQUEST['_sold_individually'] == 'yes' ) { 
  1081. update_post_meta( $post_id, '_sold_individually', 'yes' ); 
  1082. else { 
  1083. update_post_meta( $post_id, '_sold_individually', '' ); 
  1084.  
  1085. // Handle price - remove dates and set to lowest 
  1086. $change_price_product_types = apply_filters( 'woocommerce_bulk_edit_save_price_product_types', array( 'simple', 'external' ) ); 
  1087. $can_product_type_change_price = false; 
  1088. foreach ( $change_price_product_types as $product_type ) { 
  1089. if ( $product->is_type( $product_type ) ) { 
  1090. $can_product_type_change_price = true; 
  1091. break; 
  1092.  
  1093. if ( $can_product_type_change_price ) { 
  1094.  
  1095. $price_changed = false; 
  1096.  
  1097. if ( ! empty( $_REQUEST['change_regular_price'] ) ) { 
  1098.  
  1099. $change_regular_price = absint( $_REQUEST['change_regular_price'] ); 
  1100. $regular_price = esc_attr( stripslashes( $_REQUEST['_regular_price'] ) ); 
  1101.  
  1102. switch ( $change_regular_price ) { 
  1103. case 1 : 
  1104. $new_price = $regular_price; 
  1105. break; 
  1106. case 2 : 
  1107. if ( strstr( $regular_price, '%' ) ) { 
  1108. $percent = str_replace( '%', '', $regular_price ) / 100; 
  1109. $new_price = $old_regular_price + ( round( $old_regular_price * $percent, wc_get_price_decimals() ) ); 
  1110. } else { 
  1111. $new_price = $old_regular_price + $regular_price; 
  1112. break; 
  1113. case 3 : 
  1114. if ( strstr( $regular_price, '%' ) ) { 
  1115. $percent = str_replace( '%', '', $regular_price ) / 100; 
  1116. $new_price = max( 0, $old_regular_price - ( round ( $old_regular_price * $percent, wc_get_price_decimals() ) ) ); 
  1117. } else { 
  1118. $new_price = max( 0, $old_regular_price - $regular_price ); 
  1119. break; 
  1120.  
  1121. default : 
  1122. break; 
  1123.  
  1124. if ( isset( $new_price ) && $new_price != $old_regular_price ) { 
  1125. $price_changed = true; 
  1126. $new_price = round( $new_price, wc_get_price_decimals() ); 
  1127. update_post_meta( $post_id, '_regular_price', $new_price ); 
  1128. $product->regular_price = $new_price; 
  1129.  
  1130. if ( ! empty( $_REQUEST['change_sale_price'] ) ) { 
  1131.  
  1132. $change_sale_price = absint( $_REQUEST['change_sale_price'] ); 
  1133. $sale_price = esc_attr( stripslashes( $_REQUEST['_sale_price'] ) ); 
  1134.  
  1135. switch ( $change_sale_price ) { 
  1136. case 1 : 
  1137. $new_price = $sale_price; 
  1138. break; 
  1139. case 2 : 
  1140. if ( strstr( $sale_price, '%' ) ) { 
  1141. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1142. $new_price = $old_sale_price + ( $old_sale_price * $percent ); 
  1143. } else { 
  1144. $new_price = $old_sale_price + $sale_price; 
  1145. break; 
  1146. case 3 : 
  1147. if ( strstr( $sale_price, '%' ) ) { 
  1148. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1149. $new_price = max( 0, $old_sale_price - ( $old_sale_price * $percent ) ); 
  1150. } else { 
  1151. $new_price = max( 0, $old_sale_price - $sale_price ); 
  1152. break; 
  1153. case 4 : 
  1154. if ( strstr( $sale_price, '%' ) ) { 
  1155. $percent = str_replace( '%', '', $sale_price ) / 100; 
  1156. $new_price = max( 0, $product->regular_price - ( $product->regular_price * $percent ) ); 
  1157. } else { 
  1158. $new_price = max( 0, $product->regular_price - $sale_price ); 
  1159. break; 
  1160.  
  1161. default : 
  1162. break; 
  1163.  
  1164. if ( isset( $new_price ) && $new_price != $old_sale_price ) { 
  1165. $price_changed = true; 
  1166. $new_price = ! empty( $new_price ) || '0' === $new_price ? round( $new_price, wc_get_price_decimals() ) : ''; 
  1167. update_post_meta( $post_id, '_sale_price', $new_price ); 
  1168. $product->sale_price = $new_price; 
  1169.  
  1170. if ( $price_changed ) { 
  1171. update_post_meta( $post_id, '_sale_price_dates_from', '' ); 
  1172. update_post_meta( $post_id, '_sale_price_dates_to', '' ); 
  1173.  
  1174. if ( $product->regular_price < $product->sale_price ) { 
  1175. $product->sale_price = ''; 
  1176. update_post_meta( $post_id, '_sale_price', '' ); 
  1177.  
  1178. if ( $product->sale_price ) { 
  1179. update_post_meta( $post_id, '_price', $product->sale_price ); 
  1180. } else { 
  1181. update_post_meta( $post_id, '_price', $product->regular_price ); 
  1182.  
  1183. // Handle stock 
  1184. if ( ! $product->is_type( 'grouped' ) ) { 
  1185.  
  1186. if ( ! empty( $_REQUEST['change_stock'] ) ) { 
  1187. update_post_meta( $post_id, '_manage_stock', 'yes' ); 
  1188. wc_update_product_stock( $post_id, wc_stock_amount( $_REQUEST['_stock'] ) ); 
  1189.  
  1190. if ( ! empty( $_REQUEST['_manage_stock'] ) ) { 
  1191.  
  1192. if ( $_REQUEST['_manage_stock'] == 'yes' ) { 
  1193. update_post_meta( $post_id, '_manage_stock', 'yes' ); 
  1194. } else { 
  1195. update_post_meta( $post_id, '_manage_stock', 'no' ); 
  1196. wc_update_product_stock( $post_id, 0 ); 
  1197.  
  1198. if ( ! empty( $_REQUEST['_backorders'] ) ) { 
  1199. update_post_meta( $post_id, '_backorders', wc_clean( $_REQUEST['_backorders'] ) ); 
  1200.  
  1201.  
  1202. do_action( 'woocommerce_product_bulk_edit_save', $product ); 
  1203.  
  1204. /** 
  1205. * Add extra bulk action options to mark orders as complete or processing. 
  1206. * Using Javascript until WordPress core fixes: https://core.trac.wordpress.org/ticket/16031. 
  1207. */ 
  1208. public function bulk_admin_footer() { 
  1209. global $post_type; 
  1210.  
  1211. if ( 'shop_order' == $post_type ) { 
  1212. ?> 
  1213. <script type="text/javascript"> 
  1214. jQuery(function() { 
  1215. jQuery('<option>').val('mark_processing').text('<?php _e( 'Mark processing', 'woocommerce' )?>').appendTo('select[name="action"]'); 
  1216. jQuery('<option>').val('mark_processing').text('<?php _e( 'Mark processing', 'woocommerce' )?>').appendTo('select[name="action2"]'); 
  1217.  
  1218. jQuery('<option>').val('mark_on-hold').text('<?php _e( 'Mark on-hold', 'woocommerce' )?>').appendTo('select[name="action"]'); 
  1219. jQuery('<option>').val('mark_on-hold').text('<?php _e( 'Mark on-hold', 'woocommerce' )?>').appendTo('select[name="action2"]'); 
  1220.  
  1221. jQuery('<option>').val('mark_completed').text('<?php _e( 'Mark complete', 'woocommerce' )?>').appendTo('select[name="action"]'); 
  1222. jQuery('<option>').val('mark_completed').text('<?php _e( 'Mark complete', 'woocommerce' )?>').appendTo('select[name="action2"]'); 
  1223. }); 
  1224. </script> 
  1225. <?php 
  1226.  
  1227. /** 
  1228. * Process the new bulk actions for changing order status. 
  1229. */ 
  1230. public function bulk_action() { 
  1231. $wp_list_table = _get_list_table( 'WP_Posts_List_Table' ); 
  1232. $action = $wp_list_table->current_action(); 
  1233.  
  1234. // Bail out if this is not a status-changing action 
  1235. if ( strpos( $action, 'mark_' ) === false ) { 
  1236. return; 
  1237.  
  1238. $order_statuses = wc_get_order_statuses(); 
  1239.  
  1240. $new_status = substr( $action, 5 ); // get the status name from action 
  1241. $report_action = 'marked_' . $new_status; 
  1242.  
  1243. // Sanity check: bail out if this is actually not a status, or is 
  1244. // not a registered status 
  1245. if ( ! isset( $order_statuses[ 'wc-' . $new_status ] ) ) { 
  1246. return; 
  1247.  
  1248. $changed = 0; 
  1249.  
  1250. $post_ids = array_map( 'absint', (array) $_REQUEST['post'] ); 
  1251.  
  1252. foreach ( $post_ids as $post_id ) { 
  1253. $order = wc_get_order( $post_id ); 
  1254. $order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true ); 
  1255. do_action( 'woocommerce_order_edit_status', $post_id, $new_status ); 
  1256. $changed++; 
  1257.  
  1258. $sendback = add_query_arg( array( 'post_type' => 'shop_order', $report_action => true, 'changed' => $changed, 'ids' => join( ', ', $post_ids ) ), '' ); 
  1259.  
  1260. if ( isset( $_GET['post_status'] ) ) { 
  1261. $sendback = add_query_arg( 'post_status', sanitize_text_field( $_GET['post_status'] ), $sendback ); 
  1262.  
  1263. wp_redirect( esc_url_raw( $sendback ) ); 
  1264. exit(); 
  1265.  
  1266. /** 
  1267. * Show confirmation message that order status changed for number of orders. 
  1268. */ 
  1269. public function bulk_admin_notices() { 
  1270. global $post_type, $pagenow; 
  1271.  
  1272. // Bail out if not on shop order list page 
  1273. if ( 'edit.php' !== $pagenow || 'shop_order' !== $post_type ) { 
  1274. return; 
  1275.  
  1276. $order_statuses = wc_get_order_statuses(); 
  1277.  
  1278. // Check if any status changes happened 
  1279. foreach ( $order_statuses as $slug => $name ) { 
  1280.  
  1281. if ( isset( $_REQUEST[ 'marked_' . str_replace( 'wc-', '', $slug ) ] ) ) { 
  1282.  
  1283. $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; 
  1284. $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) ); 
  1285. echo '<div class="updated"><p>' . $message . '</p></div>'; 
  1286.  
  1287. break; 
  1288.  
  1289. /** 
  1290. * Search custom fields as well as content. 
  1291. * @param WP_Query $wp 
  1292. */ 
  1293. public function shop_order_search_custom_fields( $wp ) { 
  1294. global $pagenow; 
  1295.  
  1296. if ( 'edit.php' != $pagenow || empty( $wp->query_vars['s'] ) || $wp->query_vars['post_type'] != 'shop_order' ) { 
  1297. return; 
  1298.  
  1299. $post_ids = wc_order_search( $_GET['s'] ); 
  1300.  
  1301. if ( ! empty( $post_ids ) ) { 
  1302. // Remove "s" - we don't want to search order name. 
  1303. unset( $wp->query_vars['s'] ); 
  1304.  
  1305. // so we know we're doing this. 
  1306. $wp->query_vars['shop_order_search'] = true; 
  1307.  
  1308. // Search by found posts. 
  1309. $wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) ); 
  1310.  
  1311. /** 
  1312. * Change the label when searching orders. 
  1313. * @param mixed $query 
  1314. * @return string 
  1315. */ 
  1316. public function shop_order_search_label( $query ) { 
  1317. global $pagenow, $typenow; 
  1318.  
  1319. if ( 'edit.php' != $pagenow ) { 
  1320. return $query; 
  1321.  
  1322. if ( $typenow != 'shop_order' ) { 
  1323. return $query; 
  1324.  
  1325. if ( ! get_query_var( 'shop_order_search' ) ) { 
  1326. return $query; 
  1327.  
  1328. return wp_unslash( $_GET['s'] ); 
  1329.  
  1330. /** 
  1331. * Query vars for custom searches. 
  1332. * @param mixed $public_query_vars 
  1333. * @return array 
  1334. */ 
  1335. public function add_custom_query_var( $public_query_vars ) { 
  1336. $public_query_vars[] = 'sku'; 
  1337. $public_query_vars[] = 'shop_order_search'; 
  1338.  
  1339. return $public_query_vars; 
  1340.  
  1341. /** 
  1342. * Filters for post types. 
  1343. */ 
  1344. public function restrict_manage_posts() { 
  1345. global $typenow; 
  1346.  
  1347. if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1348. $this->shop_order_filters(); 
  1349. } elseif ( 'product' == $typenow ) { 
  1350. $this->product_filters(); 
  1351. } elseif( 'shop_coupon' == $typenow ) { 
  1352. $this->shop_coupon_filters(); 
  1353.  
  1354. /** 
  1355. * Show a category filter box. 
  1356. */ 
  1357. public function product_filters() { 
  1358. global $wp_query; 
  1359.  
  1360. // Category Filtering 
  1361. wc_product_dropdown_categories(); 
  1362.  
  1363. // Type filtering 
  1364. $terms = get_terms( 'product_type' ); 
  1365. $output = '<select name="product_type" id="dropdown_product_type">'; 
  1366. $output .= '<option value="">' . __( 'Show all product types', 'woocommerce' ) . '</option>'; 
  1367.  
  1368. foreach ( $terms as $term ) { 
  1369. $output .= '<option value="' . sanitize_title( $term->name ) . '" '; 
  1370.  
  1371. if ( isset( $wp_query->query['product_type'] ) ) { 
  1372. $output .= selected( $term->slug, $wp_query->query['product_type'], false ); 
  1373.  
  1374. $output .= '>'; 
  1375.  
  1376. switch ( $term->name ) { 
  1377. case 'grouped' : 
  1378. $output .= __( 'Grouped product', 'woocommerce' ); 
  1379. break; 
  1380. case 'external' : 
  1381. $output .= __( 'External/Affiliate product', 'woocommerce' ); 
  1382. break; 
  1383. case 'variable' : 
  1384. $output .= __( 'Variable product', 'woocommerce' ); 
  1385. break; 
  1386. case 'simple' : 
  1387. $output .= __( 'Simple product', 'woocommerce' ); 
  1388. break; 
  1389. default : 
  1390. // Assuming that we have other types in future 
  1391. $output .= ucfirst( $term->name ); 
  1392. break; 
  1393.  
  1394. $output .= '</option>'; 
  1395.  
  1396. if ( 'simple' == $term->name ) { 
  1397.  
  1398. $output .= '<option value="downloadable" '; 
  1399.  
  1400. if ( isset( $wp_query->query['product_type'] ) ) { 
  1401. $output .= selected( 'downloadable', $wp_query->query['product_type'], false ); 
  1402.  
  1403. $output .= '> → ' . __( 'Downloadable', 'woocommerce' ) . '</option>'; 
  1404.  
  1405. $output .= '<option value="virtual" '; 
  1406.  
  1407. if ( isset( $wp_query->query['product_type'] ) ) { 
  1408. $output .= selected( 'virtual', $wp_query->query['product_type'], false ); 
  1409.  
  1410. $output .= '> → ' . __( 'Virtual', 'woocommerce' ) . '</option>'; 
  1411.  
  1412. $output .= '</select>'; 
  1413.  
  1414. echo apply_filters( 'woocommerce_product_filters', $output ); 
  1415.  
  1416. /** 
  1417. * Show custom filters to filter coupons by type. 
  1418. */ 
  1419. public function shop_coupon_filters() { 
  1420. ?> 
  1421. <select name="coupon_type" id="dropdown_shop_coupon_type"> 
  1422. <option value=""><?php _e( 'Show all types', 'woocommerce' ); ?></option> 
  1423. <?php 
  1424. $types = wc_get_coupon_types(); 
  1425.  
  1426. foreach ( $types as $name => $type ) { 
  1427. echo '<option value="' . esc_attr( $name ) . '"'; 
  1428.  
  1429. if ( isset( $_GET['coupon_type'] ) ) 
  1430. selected( $name, $_GET['coupon_type'] ); 
  1431.  
  1432. echo '>' . esc_html__( $type, 'woocommerce' ) . '</option>'; 
  1433. ?> 
  1434. </select> 
  1435. <?php 
  1436.  
  1437. /** 
  1438. * Show custom filters to filter orders by status/customer. 
  1439. */ 
  1440. public function shop_order_filters() { 
  1441. $user_string = ''; 
  1442. $user_id = ''; 
  1443. if ( ! empty( $_GET['_customer_user'] ) ) { 
  1444. $user_id = absint( $_GET['_customer_user'] ); 
  1445. $user = get_user_by( 'id', $user_id ); 
  1446. $user_string = esc_html( $user->display_name ) . ' (#' . absint( $user->ID ) . ' – ' . esc_html( $user->user_email ) . ')'; 
  1447. ?> 
  1448. <input type="hidden" class="wc-customer-search" name="_customer_user" data-placeholder="<?php esc_attr_e( 'Search for a customer…', 'woocommerce' ); ?>" data-selected="<?php echo htmlspecialchars( $user_string ); ?>" value="<?php echo $user_id; ?>" data-allow_clear="true" /> 
  1449. <?php 
  1450.  
  1451. /** 
  1452. * Filters and sorting handler. 
  1453. * @param array $vars 
  1454. * @return array 
  1455. */ 
  1456. public function request_query( $vars ) { 
  1457. global $typenow, $wp_query, $wp_post_statuses; 
  1458.  
  1459. if ( 'product' === $typenow ) { 
  1460. // Sorting 
  1461. if ( isset( $vars['orderby'] ) ) { 
  1462. if ( 'price' == $vars['orderby'] ) { 
  1463. $vars = array_merge( $vars, array( 
  1464. 'meta_key' => '_price',  
  1465. 'orderby' => 'meta_value_num' 
  1466. ) ); 
  1467. if ( 'featured' == $vars['orderby'] ) { 
  1468. $vars = array_merge( $vars, array( 
  1469. 'meta_key' => '_featured',  
  1470. 'orderby' => 'meta_value' 
  1471. ) ); 
  1472. if ( 'sku' == $vars['orderby'] ) { 
  1473. $vars = array_merge( $vars, array( 
  1474. 'meta_key' => '_sku',  
  1475. 'orderby' => 'meta_value' 
  1476. ) ); 
  1477.  
  1478. } elseif ( 'shop_coupon' === $typenow ) { 
  1479.  
  1480. if ( ! empty( $_GET['coupon_type'] ) ) { 
  1481. $vars['meta_key'] = 'discount_type'; 
  1482. $vars['meta_value'] = wc_clean( $_GET['coupon_type'] ); 
  1483.  
  1484. } elseif ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1485.  
  1486. // Filter the orders by the posted customer. 
  1487. if ( isset( $_GET['_customer_user'] ) && $_GET['_customer_user'] > 0 ) { 
  1488. $vars['meta_query'] = array( 
  1489. array( 
  1490. 'key' => '_customer_user',  
  1491. 'value' => (int) $_GET['_customer_user'],  
  1492. 'compare' => '=' 
  1493. ); 
  1494.  
  1495. // Sorting 
  1496. if ( isset( $vars['orderby'] ) ) { 
  1497. if ( 'order_total' == $vars['orderby'] ) { 
  1498. $vars = array_merge( $vars, array( 
  1499. 'meta_key' => '_order_total',  
  1500. 'orderby' => 'meta_value_num' 
  1501. ) ); 
  1502.  
  1503. // Status 
  1504. if ( ! isset( $vars['post_status'] ) ) { 
  1505. $post_statuses = wc_get_order_statuses(); 
  1506.  
  1507. foreach ( $post_statuses as $status => $value ) { 
  1508. if ( isset( $wp_post_statuses[ $status ] ) && false === $wp_post_statuses[ $status ]->show_in_admin_all_list ) { 
  1509. unset( $post_statuses[ $status ] ); 
  1510.  
  1511. $vars['post_status'] = array_keys( $post_statuses ); 
  1512.  
  1513. return $vars; 
  1514.  
  1515. /** 
  1516. * Filter the products in admin based on options. 
  1517. * @param mixed $query 
  1518. */ 
  1519. public function product_filters_query( $query ) { 
  1520. global $typenow, $wp_query; 
  1521.  
  1522. if ( 'product' == $typenow ) { 
  1523.  
  1524. if ( isset( $query->query_vars['product_type'] ) ) { 
  1525. // Subtypes 
  1526. if ( 'downloadable' == $query->query_vars['product_type'] ) { 
  1527. $query->query_vars['product_type'] = ''; 
  1528. $query->is_tax = false; 
  1529. $query->query_vars['meta_value'] = 'yes'; 
  1530. $query->query_vars['meta_key'] = '_downloadable'; 
  1531. } elseif ( 'virtual' == $query->query_vars['product_type'] ) { 
  1532. $query->query_vars['product_type'] = ''; 
  1533. $query->is_tax = false; 
  1534. $query->query_vars['meta_value'] = 'yes'; 
  1535. $query->query_vars['meta_key'] = '_virtual'; 
  1536.  
  1537. // Categories 
  1538. if ( isset( $_GET['product_cat'] ) && '0' === $_GET['product_cat'] ) { 
  1539. $query->query_vars['tax_query'][] = array( 
  1540. 'taxonomy' => 'product_cat',  
  1541. 'field' => 'id',  
  1542. 'terms' => get_terms( 'product_cat', array( 'fields' => 'ids' ) ),  
  1543. 'operator' => 'NOT IN' 
  1544. ); 
  1545.  
  1546. // Shipping classes 
  1547. if ( isset( $_GET['product_shipping_class'] ) && '0' === $_GET['product_shipping_class'] ) { 
  1548. $query->query_vars['tax_query'][] = array( 
  1549. 'taxonomy' => 'product_shipping_class',  
  1550. 'field' => 'id',  
  1551. 'terms' => get_terms( 'product_shipping_class', array( 'fields' => 'ids' ) ),  
  1552. 'operator' => 'NOT IN' 
  1553. ); 
  1554.  
  1555. /** 
  1556. * Search by SKU or ID for products. 
  1557. * @param string $where 
  1558. * @return string 
  1559. */ 
  1560. public function product_search( $where ) { 
  1561. global $pagenow, $wpdb, $wp; 
  1562.  
  1563. if ( 'edit.php' != $pagenow || ! is_search() || ! isset( $wp->query_vars['s'] ) || 'product' != $wp->query_vars['post_type'] ) { 
  1564. return $where; 
  1565.  
  1566. $search_ids = array(); 
  1567. $terms = explode( ', ', $wp->query_vars['s'] ); 
  1568.  
  1569. foreach ( $terms as $term ) { 
  1570. if ( is_numeric( $term ) ) { 
  1571. $search_ids[] = $term; 
  1572.  
  1573. // Attempt to get a SKU 
  1574. $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 ) ) . '%' ) ); 
  1575. $sku_to_id = array_merge( wp_list_pluck( $sku_to_id, 'ID' ), wp_list_pluck( $sku_to_id, 'post_parent' ) ); 
  1576.  
  1577. if ( sizeof( $sku_to_id ) > 0 ) { 
  1578. $search_ids = array_merge( $search_ids, $sku_to_id ); 
  1579.  
  1580. $search_ids = array_filter( array_unique( array_map( 'absint', $search_ids ) ) ); 
  1581.  
  1582. if ( sizeof( $search_ids ) > 0 ) { 
  1583. $where = str_replace( 'AND (((', "AND ( ({$wpdb->posts}.ID IN (" . implode( ', ', $search_ids ) . ")) OR ((", $where ); 
  1584.  
  1585. return $where; 
  1586.  
  1587. /** 
  1588. * Disable the auto-save functionality for Orders. 
  1589. */ 
  1590. public function disable_autosave() { 
  1591. global $post; 
  1592.  
  1593. if ( $post && in_array( get_post_type( $post->ID ), wc_get_order_types( 'order-meta-boxes' ) ) ) { 
  1594. wp_dequeue_script( 'autosave' ); 
  1595.  
  1596. /** 
  1597. * Removes variations etc belonging to a deleted post, and clears transients. 
  1598. * @param mixed $id ID of post being deleted 
  1599. */ 
  1600. public function delete_post( $id ) { 
  1601. global $woocommerce, $wpdb; 
  1602.  
  1603. if ( ! current_user_can( 'delete_posts' ) ) { 
  1604. return; 
  1605.  
  1606. if ( $id > 0 ) { 
  1607.  
  1608. $post_type = get_post_type( $id ); 
  1609.  
  1610. switch ( $post_type ) { 
  1611. case 'product' : 
  1612.  
  1613. $child_product_variations = get_children( 'post_parent=' . $id . '&post_type=product_variation' ); 
  1614.  
  1615. if ( ! empty( $child_product_variations ) ) { 
  1616. foreach ( $child_product_variations as $child ) { 
  1617. wp_delete_post( $child->ID, true ); 
  1618.  
  1619. $child_products = get_children( 'post_parent=' . $id . '&post_type=product' ); 
  1620.  
  1621. if ( ! empty( $child_products ) ) { 
  1622. foreach ( $child_products as $child ) { 
  1623. $child_post = array(); 
  1624. $child_post['ID'] = $child->ID; 
  1625. $child_post['post_parent'] = 0; 
  1626. wp_update_post( $child_post ); 
  1627.  
  1628. if ( $parent_id = wp_get_post_parent_id( $id ) ) { 
  1629. wc_delete_product_transients( $parent_id ); 
  1630.  
  1631. break; 
  1632. case 'product_variation' : 
  1633. wc_delete_product_transients( wp_get_post_parent_id( $id ) ); 
  1634. break; 
  1635. case 'shop_order' : 
  1636. $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) ); 
  1637.  
  1638. if ( ! is_null( $refunds ) ) { 
  1639. foreach ( $refunds as $refund ) { 
  1640. wp_delete_post( $refund->ID, true ); 
  1641. break; 
  1642.  
  1643. /** 
  1644. * woocommerce_trash_post function. 
  1645. * @param mixed $id 
  1646. */ 
  1647. public function trash_post( $id ) { 
  1648. global $wpdb; 
  1649.  
  1650. if ( $id > 0 ) { 
  1651.  
  1652. $post_type = get_post_type( $id ); 
  1653.  
  1654. if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) { 
  1655.  
  1656. // Delete count - meta doesn't work on trashed posts 
  1657. $user_id = get_post_meta( $id, '_customer_user', true ); 
  1658.  
  1659. if ( $user_id > 0 ) { 
  1660. delete_user_meta( $user_id, '_money_spent' ); 
  1661. delete_user_meta( $user_id, '_order_count' ); 
  1662.  
  1663. $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) ); 
  1664.  
  1665. foreach ( $refunds as $refund ) { 
  1666. $wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'ID' => $refund->ID ) ); 
  1667.  
  1668. delete_transient( 'woocommerce_processing_order_count' ); 
  1669. wc_delete_shop_order_transients( $id ); 
  1670.  
  1671.  
  1672. /** 
  1673. * woocommerce_untrash_post function. 
  1674. * @param mixed $id 
  1675. */ 
  1676. public function untrash_post( $id ) { 
  1677. global $wpdb; 
  1678.  
  1679. if ( $id > 0 ) { 
  1680.  
  1681. $post_type = get_post_type( $id ); 
  1682.  
  1683. if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) { 
  1684.  
  1685. // Delete count - meta doesn't work on trashed posts 
  1686. $user_id = get_post_meta( $id, '_customer_user', true ); 
  1687.  
  1688. if ( $user_id > 0 ) { 
  1689. delete_user_meta( $user_id, '_money_spent' ); 
  1690. delete_user_meta( $user_id, '_order_count' ); 
  1691.  
  1692. $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) ); 
  1693.  
  1694. foreach ( $refunds as $refund ) { 
  1695. $wpdb->update( $wpdb->posts, array( 'post_status' => 'wc-completed' ), array( 'ID' => $refund->ID ) ); 
  1696.  
  1697. delete_transient( 'woocommerce_processing_order_count' ); 
  1698. wc_delete_shop_order_transients( $id ); 
  1699. } else if ( 'product' === $post_type ) { 
  1700. // Check if SKU is valid before untrash the product. 
  1701. $sku = get_post_meta( $id, '_sku', true ); 
  1702.  
  1703. if ( ! empty( $sku ) ) { 
  1704. if ( ! wc_product_has_unique_sku( $id, $sku ) ) { 
  1705. update_post_meta( $id, '_sku', '' ); 
  1706.  
  1707. /** 
  1708. * Remove item meta on permanent deletion. 
  1709. */ 
  1710. public function delete_order_items( $postid ) { 
  1711. global $wpdb; 
  1712.  
  1713. if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) { 
  1714. do_action( 'woocommerce_delete_order_items', $postid ); 
  1715.  
  1716. $wpdb->query( " 
  1717. DELETE {$wpdb->prefix}woocommerce_order_items, {$wpdb->prefix}woocommerce_order_itemmeta 
  1718. FROM {$wpdb->prefix}woocommerce_order_items 
  1719. JOIN {$wpdb->prefix}woocommerce_order_itemmeta ON {$wpdb->prefix}woocommerce_order_items.order_item_id = {$wpdb->prefix}woocommerce_order_itemmeta.order_item_id 
  1720. WHERE {$wpdb->prefix}woocommerce_order_items.order_id = '{$postid}'; 
  1721. " ); 
  1722.  
  1723. do_action( 'woocommerce_deleted_order_items', $postid ); 
  1724.  
  1725. /** 
  1726. * Remove downloadable permissions on permanent order deletion. 
  1727. */ 
  1728. public function delete_order_downloadable_permissions( $postid ) { 
  1729. global $wpdb; 
  1730.  
  1731. if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) { 
  1732. do_action( 'woocommerce_delete_order_downloadable_permissions', $postid ); 
  1733.  
  1734. $wpdb->query( $wpdb->prepare( " 
  1735. DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions 
  1736. WHERE order_id = %d 
  1737. ", $postid ) ); 
  1738.  
  1739. do_action( 'woocommerce_deleted_order_downloadable_permissions', $postid ); 
  1740.  
  1741. /** 
  1742. * Change title boxes in admin. 
  1743. * @param string $text 
  1744. * @param object $post 
  1745. * @return string 
  1746. */ 
  1747. public function enter_title_here( $text, $post ) { 
  1748. switch ( $post->post_type ) { 
  1749. case 'product' : 
  1750. $text = __( 'Product name', 'woocommerce' ); 
  1751. break; 
  1752. case 'shop_coupon' : 
  1753. $text = __( 'Coupon code', 'woocommerce' ); 
  1754. break; 
  1755.  
  1756. return $text; 
  1757.  
  1758. /** 
  1759. * Print coupon description textarea field. 
  1760. * @param WP_Post $post 
  1761. */ 
  1762. public function edit_form_after_title( $post ) { 
  1763. if ( 'shop_coupon' === $post->post_type ) { 
  1764. ?> 
  1765. <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> 
  1766. <?php 
  1767.  
  1768. /** 
  1769. * Hidden default Meta-Boxes. 
  1770. * @param array $hidden 
  1771. * @param object $screen 
  1772. * @return array 
  1773. */ 
  1774. public function hidden_meta_boxes( $hidden, $screen ) { 
  1775. if ( 'product' === $screen->post_type && 'post' === $screen->base ) { 
  1776. $hidden = array_merge( $hidden, array( 'postcustom' ) ); 
  1777.  
  1778. return $hidden; 
  1779.  
  1780. /** 
  1781. * Output product visibility options. 
  1782. */ 
  1783. public function product_data_visibility() { 
  1784. global $post; 
  1785.  
  1786. if ( 'product' != $post->post_type ) { 
  1787. return; 
  1788.  
  1789. $current_visibility = ( $current_visibility = get_post_meta( $post->ID, '_visibility', true ) ) ? $current_visibility : apply_filters( 'woocommerce_product_visibility_default' , 'visible' ); 
  1790. $current_featured = ( $current_featured = get_post_meta( $post->ID, '_featured', true ) ) ? $current_featured : 'no'; 
  1791.  
  1792. $visibility_options = apply_filters( 'woocommerce_product_visibility_options', array( 
  1793. 'visible' => __( 'Catalog/search', 'woocommerce' ),  
  1794. 'catalog' => __( 'Catalog', 'woocommerce' ),  
  1795. 'search' => __( 'Search', 'woocommerce' ),  
  1796. 'hidden' => __( 'Hidden', 'woocommerce' ) 
  1797. ) ); 
  1798. ?> 
  1799. <div class="misc-pub-section" id="catalog-visibility"> 
  1800. <?php _e( 'Catalog visibility:', 'woocommerce' ); ?> <strong id="catalog-visibility-display"><?php 
  1801. echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility ); 
  1802.  
  1803. if ( 'yes' == $current_featured ) { 
  1804. echo ', ' . __( 'Featured', 'woocommerce' ); 
  1805. ?></strong> 
  1806.  
  1807. <a href="#catalog-visibility" class="edit-catalog-visibility hide-if-no-js"><?php _e( 'Edit', 'woocommerce' ); ?></a> 
  1808.  
  1809. <div id="catalog-visibility-select" class="hide-if-js"> 
  1810.  
  1811. <input type="hidden" name="current_visibility" id="current_visibility" value="<?php echo esc_attr( $current_visibility ); ?>" /> 
  1812. <input type="hidden" name="current_featured" id="current_featured" value="<?php echo esc_attr( $current_featured ); ?>" /> 
  1813.  
  1814. <?php 
  1815. echo '<p>' . __( 'Choose where this product should be displayed in your catalog. The product will always be accessible directly.', 'woocommerce' ) . '</p>'; 
  1816.  
  1817. foreach ( $visibility_options as $name => $label ) { 
  1818. 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 />'; 
  1819.  
  1820. echo '<p>' . __( 'Enable this option to feature this product.', 'woocommerce' ) . '</p>'; 
  1821.  
  1822. echo '<input type="checkbox" name="_featured" id="_featured" ' . checked( $current_featured, 'yes', false ) . ' /> <label for="_featured">' . __( 'Featured Product', 'woocommerce' ) . '</label><br />'; 
  1823. ?> 
  1824. <p> 
  1825. <a href="#catalog-visibility" class="save-post-visibility hide-if-no-js button"><?php _e( 'OK', 'woocommerce' ); ?></a> 
  1826. <a href="#catalog-visibility" class="cancel-post-visibility hide-if-no-js"><?php _e( 'Cancel', 'woocommerce' ); ?></a> 
  1827. </p> 
  1828. </div> 
  1829. </div> 
  1830. <?php 
  1831.  
  1832. /** 
  1833. * Filter the directory for uploads. 
  1834. * @param array $pathdata 
  1835. * @return array 
  1836. */ 
  1837. public function upload_dir( $pathdata ) { 
  1838.  
  1839. // Change upload dir for downloadable files 
  1840. if ( isset( $_POST['type'] ) && 'downloadable_product' == $_POST['type'] ) { 
  1841.  
  1842. if ( empty( $pathdata['subdir'] ) ) { 
  1843. $pathdata['path'] = $pathdata['path'] . '/woocommerce_uploads'; 
  1844. $pathdata['url'] = $pathdata['url']. '/woocommerce_uploads'; 
  1845. $pathdata['subdir'] = '/woocommerce_uploads'; 
  1846. } else { 
  1847. $new_subdir = '/woocommerce_uploads' . $pathdata['subdir']; 
  1848.  
  1849. $pathdata['path'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] ); 
  1850. $pathdata['url'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] ); 
  1851. $pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] ); 
  1852.  
  1853. return $pathdata; 
  1854.  
  1855. /** 
  1856. * Run a filter when uploading a downloadable product. 
  1857. */ 
  1858. public function woocommerce_media_upload_downloadable_product() { 
  1859. do_action( 'media_upload_file' ); 
  1860.  
  1861. /** 
  1862. * Grant downloadable file access to any newly added files on any existing. 
  1863. * orders for this product that have previously been granted downloadable file access. 
  1864. * @param int $product_id product identifier 
  1865. * @param int $variation_id optional product variation identifier 
  1866. * @param array $downloadable_files newly set files 
  1867. */ 
  1868. public function process_product_file_download_paths( $product_id, $variation_id, $downloadable_files ) { 
  1869. global $wpdb; 
  1870.  
  1871. if ( $variation_id ) { 
  1872. $product_id = $variation_id; 
  1873.  
  1874. $product = wc_get_product( $product_id ); 
  1875. $existing_download_ids = array_keys( (array) $product->get_files() ); 
  1876. $updated_download_ids = array_keys( (array) $downloadable_files ); 
  1877.  
  1878. $new_download_ids = array_filter( array_diff( $updated_download_ids, $existing_download_ids ) ); 
  1879. $removed_download_ids = array_filter( array_diff( $existing_download_ids, $updated_download_ids ) ); 
  1880.  
  1881. if ( ! empty( $new_download_ids ) || ! empty( $removed_download_ids ) ) { 
  1882. // determine whether downloadable file access has been granted via the typical order completion, or via the admin ajax method 
  1883. $existing_permissions = $wpdb->get_results( $wpdb->prepare( "SELECT * from {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE product_id = %d GROUP BY order_id", $product_id ) ); 
  1884.  
  1885. foreach ( $existing_permissions as $existing_permission ) { 
  1886. $order = wc_get_order( $existing_permission->order_id ); 
  1887.  
  1888. if ( ! empty( $order->id ) ) { 
  1889. // Remove permissions 
  1890. if ( ! empty( $removed_download_ids ) ) { 
  1891. foreach ( $removed_download_ids as $download_id ) { 
  1892. if ( apply_filters( 'woocommerce_process_product_file_download_paths_remove_access_to_old_file', true, $download_id, $product_id, $order ) ) { 
  1893. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE order_id = %d AND product_id = %d AND download_id = %s", $order->id, $product_id, $download_id ) ); 
  1894. // Add permissions 
  1895. if ( ! empty( $new_download_ids ) ) { 
  1896.  
  1897. foreach ( $new_download_ids as $download_id ) { 
  1898.  
  1899. if ( apply_filters( 'woocommerce_process_product_file_download_paths_grant_access_to_new_file', true, $download_id, $product_id, $order ) ) { 
  1900. // grant permission if it doesn't already exist 
  1901. if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT 1=1 FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE order_id = %d AND product_id = %d AND download_id = %s", $order->id, $product_id, $download_id ) ) ) { 
  1902. wc_downloadable_file_permission( $download_id, $product_id, $order ); 
  1903.  
  1904. /** 
  1905. * Disable DFW feature pointer. 
  1906. */ 
  1907. public function disable_dfw_feature_pointer() { 
  1908. $screen = get_current_screen(); 
  1909.  
  1910. if ( $screen && 'product' === $screen->id && 'post' === $screen->base ) { 
  1911. remove_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_wp410_dfw' ) ); 
  1912.  
  1913. /** 
  1914. * Removes products, orders, and coupons from the list of post types that support "View Mode" switching. 
  1915. * View mode is seen on posts where you can switch between list or excerpt. Our post types don't support 
  1916. * it, so we want to hide the useless UI from the screen options tab. 
  1917. * @since 2.6 
  1918. * @param array $post_types Array of post types supporting view mode 
  1919. * @return array Array of post types supporting view mode, without products, orders, and coupons 
  1920. */ 
  1921. public function disable_view_mode_options( $post_types ) { 
  1922. unset( $post_types['product'], $post_types['shop_order'], $post_types['shop_coupon'] ); 
  1923. return $post_types; 
  1924.  
  1925. public function maybe_render_blank_state( $which ) { 
  1926. global $post_type; 
  1927.  
  1928. if ( in_array( $post_type, array( 'shop_order', 'product', 'shop_coupon' ) ) && 'bottom' === $which ) { 
  1929. $counts = (array) wp_count_posts( $post_type ); 
  1930. unset( $counts['auto-draft'] ); 
  1931. $count = array_sum( $counts ); 
  1932.  
  1933. if ( 0 < $count ) { 
  1934. return; 
  1935.  
  1936. echo '<div class="woocommerce-BlankState">'; 
  1937.  
  1938. switch ( $post_type ) { 
  1939. case 'shop_order' : 
  1940. ?> 
  1941. <h2 class="woocommerce-BlankState-message"><?php _e( 'When you receive a new order, it will appear here.', 'woocommerce' ); ?></h2> 
  1942. <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> 
  1943. <?php 
  1944. break; 
  1945. case 'shop_coupon' : 
  1946. ?> 
  1947. <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> 
  1948. <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> 
  1949. <?php 
  1950. break; 
  1951. case 'product' : 
  1952. ?> 
  1953. <h2 class="woocommerce-BlankState-message"><?php _e( 'Ready to start selling something awesome?', 'woocommerce' ); ?></h2> 
  1954. <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> 
  1955. <?php 
  1956. break; 
  1957.  
  1958. echo '<style type="text/css">#posts-filter .wp-list-table, #posts-filter .tablenav.top, .wrap .subsubsub { display: none; } </style></div>';