PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php

https://bitbucket.org/theshipswakecreative/psw
PHP | 2091 lines | 1426 code | 353 blank | 312 comment | 320 complexity | 9830b8c0b9acf16e316569647256b7ba MD5 | raw file
Possible License(s): LGPL-3.0, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Post Types Admin
  4. *
  5. * @author WooThemes
  6. * @category Admin
  7. * @package WooCommerce/Admin
  8. * @version 2.1.0
  9. */
  10. if ( ! defined( 'ABSPATH' ) ) {
  11. exit; // Exit if accessed directly
  12. }
  13. if ( ! class_exists( 'WC_Admin_Post_Types' ) ) :
  14. /**
  15. * WC_Admin_Post_Types Class
  16. *
  17. * Handles the edit posts views and some functionality on the edit post screen for WC post types.
  18. */
  19. class WC_Admin_Post_Types {
  20. /**
  21. * Constructor
  22. */
  23. public function __construct() {
  24. add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
  25. add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) );
  26. // WP List table columns. Defined here so they are always available for events such as inline editing.
  27. add_filter( 'manage_edit-product_columns', array( $this, 'product_columns' ) );
  28. add_filter( 'manage_edit-shop_coupon_columns', array( $this, 'shop_coupon_columns' ) );
  29. add_filter( 'manage_edit-shop_order_columns', array( $this, 'shop_order_columns' ) );
  30. add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_columns' ), 2 );
  31. add_action( 'manage_shop_coupon_posts_custom_column', array( $this, 'render_shop_coupon_columns' ), 2 );
  32. add_action( 'manage_shop_order_posts_custom_column', array( $this, 'render_shop_order_columns' ), 2 );
  33. add_filter( 'manage_edit-product_sortable_columns', array( $this, 'product_sortable_columns' ) );
  34. add_filter( 'manage_edit-shop_coupon_sortable_columns', array( $this, 'shop_coupon_sortable_columns' ) );
  35. add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'shop_order_sortable_columns' ) );
  36. add_filter( 'bulk_actions-edit-shop_order', array( $this, 'shop_order_bulk_actions' ) );
  37. add_filter( 'views_edit-product', array( $this, 'product_sorting_link' ) );
  38. // Bulk / quick edit
  39. add_action( 'bulk_edit_custom_box', array( $this, 'bulk_edit' ), 10, 2 );
  40. add_action( 'quick_edit_custom_box', array( $this, 'quick_edit' ), 10, 2 );
  41. add_action( 'save_post', array( $this, 'bulk_and_quick_edit_save_post' ), 10, 2 );
  42. add_action( 'admin_footer', array( $this, 'bulk_admin_footer' ), 10 );
  43. add_action( 'load-edit.php', array( $this, 'bulk_action' ) );
  44. add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
  45. // Order Search
  46. add_filter( 'get_search_query', array( $this, 'shop_order_search_label' ) );
  47. add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) );
  48. add_action( 'parse_query', array( $this, 'shop_order_search_custom_fields' ) );
  49. // Filters
  50. add_action( 'restrict_manage_posts', array( $this, 'restrict_manage_posts' ) );
  51. add_filter( 'request', array( $this, 'request_query' ) );
  52. add_filter( 'parse_query', array( $this, 'product_filters_query' ) );
  53. add_filter( 'posts_search', array( $this, 'product_search' ) );
  54. // Status transitions
  55. add_action( 'delete_post', array( $this, 'delete_post' ) );
  56. add_action( 'wp_trash_post', array( $this, 'trash_post' ) );
  57. add_action( 'untrash_post', array( $this, 'untrash_post' ) );
  58. add_action( 'before_delete_post', array( $this, 'delete_order_items' ) );
  59. // Edit post screens
  60. add_filter( 'media_view_strings', array( $this, 'media_view_strings' ), 10, 2 );
  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( 'media_view_strings', array( $this, 'change_insert_into_post' ) );
  64. add_action( 'post_submitbox_misc_actions', array( $this, 'product_data_visibility' ) );
  65. $this->change_featured_image_text();
  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. if ( ! function_exists( 'duplicate_post_plugin_activation' ) ) {
  70. include( 'class-wc-admin-duplicate-product.php' );
  71. }
  72. include_once( 'class-wc-admin-meta-boxes.php' );
  73. // Download permissions
  74. add_action( 'woocommerce_process_product_file_download_paths', array( $this, 'process_product_file_download_paths' ), 10, 3 );
  75. }
  76. /**
  77. * Define custom columns for products
  78. * @param array $existing_columns
  79. * @return array
  80. */
  81. public function product_columns( $existing_columns ) {
  82. if ( empty( $existing_columns ) && ! is_array( $existing_columns ) ) {
  83. $existing_columns = array();
  84. }
  85. unset( $existing_columns['title'], $existing_columns['comments'], $existing_columns['date'] );
  86. $columns = array();
  87. $columns['cb'] = '<input type="checkbox" />';
  88. $columns['thumb'] = '<span class="wc-image tips" data-tip="' . __( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>';
  89. $columns['name'] = __( 'Name', 'woocommerce' );
  90. if ( wc_product_sku_enabled() ) {
  91. $columns['sku'] = __( 'SKU', 'woocommerce' );
  92. }
  93. if ( 'yes' == get_option( 'woocommerce_manage_stock' ) ) {
  94. $columns['is_in_stock'] = __( 'Stock', 'woocommerce' );
  95. }
  96. $columns['price'] = __( 'Price', 'woocommerce' );
  97. $columns['product_cat'] = __( 'Categories', 'woocommerce' );
  98. $columns['product_tag'] = __( 'Tags', 'woocommerce' );
  99. $columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . __( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
  100. $columns['product_type'] = '<span class="wc-type parent-tips" data-tip="' . __( 'Type', 'woocommerce' ) . '">' . __( 'Type', 'woocommerce' ) . '</span>';
  101. $columns['date'] = __( 'Date', 'woocommerce' );
  102. return array_merge( $columns, $existing_columns );
  103. }
  104. /**
  105. * Define custom columns for coupons
  106. * @param array $existing_columns
  107. * @return array
  108. */
  109. public function shop_coupon_columns( $existing_columns ) {
  110. $columns = array();
  111. $columns["cb"] = "<input type=\"checkbox\" />";
  112. $columns["coupon_code"] = __( 'Code', 'woocommerce' );
  113. $columns["type"] = __( 'Coupon type', 'woocommerce' );
  114. $columns["amount"] = __( 'Coupon amount', 'woocommerce' );
  115. $columns["description"] = __( 'Description', 'woocommerce' );
  116. $columns["products"] = __( 'Product IDs', 'woocommerce' );
  117. $columns["usage"] = __( 'Usage / Limit', 'woocommerce' );
  118. $columns["expiry_date"] = __( 'Expiry date', 'woocommerce' );
  119. return $columns;
  120. }
  121. /**
  122. * Define custom columns for orders
  123. * @param array $existing_columns
  124. * @return array
  125. */
  126. public function shop_order_columns( $existing_columns ) {
  127. $columns = array();
  128. $columns['cb'] = '<input type="checkbox" />';
  129. $columns['order_status'] = '<span class="status_head tips" data-tip="' . esc_attr__( 'Status', 'woocommerce' ) . '">' . esc_attr__( 'Status', 'woocommerce' ) . '</span>';
  130. $columns['order_title'] = __( 'Order', 'woocommerce' );
  131. $columns['order_items'] = __( 'Purchased', 'woocommerce' );
  132. $columns['shipping_address'] = __( 'Ship to', 'woocommerce' );
  133. $columns['customer_message'] = '<span class="notes_head tips" data-tip="' . esc_attr__( 'Customer Message', 'woocommerce' ) . '">' . esc_attr__( 'Customer Message', 'woocommerce' ) . '</span>';
  134. $columns['order_notes'] = '<span class="order-notes_head tips" data-tip="' . esc_attr__( 'Order Notes', 'woocommerce' ) . '">' . esc_attr__( 'Order Notes', 'woocommerce' ) . '</span>';
  135. $columns['order_date'] = __( 'Date', 'woocommerce' );
  136. $columns['order_total'] = __( 'Total', 'woocommerce' );
  137. $columns['order_actions'] = __( 'Actions', 'woocommerce' );
  138. return $columns;
  139. }
  140. /**
  141. * Ouput custom columns for products
  142. * @param string $column
  143. */
  144. public function render_product_columns( $column ) {
  145. global $post;
  146. if ( empty( $the_product ) || $the_product->id != $post->ID ) {
  147. $the_product = wc_get_product( $post );
  148. }
  149. switch ( $column ) {
  150. case 'thumb' :
  151. echo '<a href="' . get_edit_post_link( $post->ID ) . '">' . $the_product->get_image() . '</a>';
  152. break;
  153. case 'name' :
  154. $edit_link = get_edit_post_link( $post->ID );
  155. $title = _draft_or_post_title();
  156. $post_type_object = get_post_type_object( $post->post_type );
  157. $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID );
  158. echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) .'">' . $title.'</a>';
  159. _post_states( $post );
  160. echo '</strong>';
  161. if ( $post->post_parent > 0 ) {
  162. echo '&nbsp;&nbsp;&larr; <a href="'. get_edit_post_link( $post->post_parent ) .'">'. get_the_title( $post->post_parent ) .'</a>';
  163. }
  164. // Excerpt view
  165. if ( isset( $_GET['mode'] ) && 'excerpt' == $_GET['mode'] ) {
  166. echo apply_filters( 'the_excerpt', $post->post_excerpt );
  167. }
  168. // Get actions
  169. $actions = array();
  170. $actions['id'] = 'ID: ' . $post->ID;
  171. if ( $can_edit_post && 'trash' != $post->post_status ) {
  172. $actions['edit'] = '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this item', 'woocommerce' ) ) . '">' . __( 'Edit', 'woocommerce' ) . '</a>';
  173. $actions['inline hide-if-no-js'] = '<a href="#" class="editinline" title="' . esc_attr( __( 'Edit this item inline', 'woocommerce' ) ) . '">' . __( 'Quick&nbsp;Edit', 'woocommerce' ) . '</a>';
  174. }
  175. if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) {
  176. if ( 'trash' == $post->post_status ) {
  177. $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 . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . '">' . __( 'Restore', 'woocommerce' ) . '</a>';
  178. } elseif ( EMPTY_TRASH_DAYS ) {
  179. $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>';
  180. }
  181. if ( 'trash' == $post->post_status || ! EMPTY_TRASH_DAYS ) {
  182. $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>';
  183. }
  184. }
  185. if ( $post_type_object->public ) {
  186. if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) {
  187. if ( $can_edit_post )
  188. $actions['view'] = '<a href="' . esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;', 'woocommerce' ), $title ) ) . '" rel="permalink">' . __( 'Preview', 'woocommerce' ) . '</a>';
  189. } elseif ( 'trash' != $post->post_status ) {
  190. $actions['view'] = '<a href="' . get_permalink( $post->ID ) . '" title="' . esc_attr( sprintf( __( 'View &#8220;%s&#8221;', 'woocommerce' ), $title ) ) . '" rel="permalink">' . __( 'View', 'woocommerce' ) . '</a>';
  191. }
  192. }
  193. $actions = apply_filters( 'post_row_actions', $actions, $post );
  194. echo '<div class="row-actions">';
  195. $i = 0;
  196. $action_count = sizeof($actions);
  197. foreach ( $actions as $action => $link ) {
  198. ++$i;
  199. ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
  200. echo '<span class="' . $action . '">' . $link . $sep . '</span>';
  201. }
  202. echo '</div>';
  203. get_inline_data( $post );
  204. /* Custom inline data for woocommerce */
  205. echo '
  206. <div class="hidden" id="woocommerce_inline_' . $post->ID . '">
  207. <div class="menu_order">' . $post->menu_order . '</div>
  208. <div class="sku">' . $the_product->sku . '</div>
  209. <div class="regular_price">' . $the_product->regular_price . '</div>
  210. <div class="sale_price">' . $the_product->sale_price . '</div>
  211. <div class="weight">' . $the_product->weight . '</div>
  212. <div class="length">' . $the_product->length . '</div>
  213. <div class="width">' . $the_product->width . '</div>
  214. <div class="height">' . $the_product->height . '</div>
  215. <div class="visibility">' . $the_product->visibility . '</div>
  216. <div class="stock_status">' . $the_product->stock_status . '</div>
  217. <div class="stock">' . $the_product->stock . '</div>
  218. <div class="manage_stock">' . $the_product->manage_stock . '</div>
  219. <div class="featured">' . $the_product->featured . '</div>
  220. <div class="product_type">' . $the_product->product_type . '</div>
  221. <div class="product_is_virtual">' . $the_product->virtual . '</div>
  222. <div class="tax_status">' . $the_product->tax_status . '</div>
  223. <div class="tax_class">' . $the_product->tax_class . '</div>
  224. <div class="backorders">' . $the_product->backorders . '</div>
  225. </div>
  226. ';
  227. break;
  228. case 'sku' :
  229. echo $the_product->get_sku() ? $the_product->get_sku() : '<span class="na">&ndash;</span>';
  230. break;
  231. case 'product_type' :
  232. if ( 'grouped' == $the_product->product_type ) {
  233. echo '<span class="product-type tips grouped" data-tip="' . __( 'Grouped', 'woocommerce' ) . '"></span>';
  234. } elseif ( 'external' == $the_product->product_type ) {
  235. echo '<span class="product-type tips external" data-tip="' . __( 'External/Affiliate', 'woocommerce' ) . '"></span>';
  236. } elseif ( 'simple' == $the_product->product_type ) {
  237. if ( $the_product->is_virtual() ) {
  238. echo '<span class="product-type tips virtual" data-tip="' . __( 'Virtual', 'woocommerce' ) . '"></span>';
  239. } elseif ( $the_product->is_downloadable() ) {
  240. echo '<span class="product-type tips downloadable" data-tip="' . __( 'Downloadable', 'woocommerce' ) . '"></span>';
  241. } else {
  242. echo '<span class="product-type tips simple" data-tip="' . __( 'Simple', 'woocommerce' ) . '"></span>';
  243. }
  244. } elseif ( 'variable' == $the_product->product_type ) {
  245. echo '<span class="product-type tips variable" data-tip="' . __( 'Variable', 'woocommerce' ) . '"></span>';
  246. } else {
  247. // Assuming that we have other types in future
  248. echo '<span class="product-type tips ' . $the_product->product_type . '" data-tip="' . ucfirst( $the_product->product_type ) . '"></span>';
  249. }
  250. break;
  251. case 'price' :
  252. echo $the_product->get_price_html() ? $the_product->get_price_html() : '<span class="na">&ndash;</span>';
  253. break;
  254. case 'product_cat' :
  255. case 'product_tag' :
  256. if ( ! $terms = get_the_terms( $post->ID, $column ) ) {
  257. echo '<span class="na">&ndash;</span>';
  258. } else {
  259. foreach ( $terms as $term ) {
  260. $termlist[] = '<a href="' . admin_url( 'edit.php?' . $column . '=' . $term->slug . '&post_type=product' ) . ' ">' . $term->name . '</a>';
  261. }
  262. echo implode( ', ', $termlist );
  263. }
  264. break;
  265. case 'featured' :
  266. $url = wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_feature_product&product_id=' . $post->ID ), 'woocommerce-feature-product' );
  267. echo '<a href="' . esc_url( $url ) . '" title="'. __( 'Toggle featured', 'woocommerce' ) . '">';
  268. if ( $the_product->is_featured() ) {
  269. echo '<span class="wc-featured tips" data-tip="' . __( 'Yes', 'woocommerce' ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>';
  270. } else {
  271. echo '<span class="wc-featured not-featured tips" data-tip="' . __( 'No', 'woocommerce' ) . '">' . __( 'No', 'woocommerce' ) . '</span>';
  272. }
  273. echo '</a>';
  274. break;
  275. case 'is_in_stock' :
  276. if ( $the_product->is_in_stock() ) {
  277. echo '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>';
  278. } else {
  279. echo '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>';
  280. }
  281. if ( $the_product->managing_stock() ) {
  282. echo ' &times; ' . $the_product->get_total_stock();
  283. }
  284. break;
  285. default :
  286. break;
  287. }
  288. }
  289. /**
  290. * Output custom columns for coupons
  291. * @param string $column
  292. */
  293. public function render_shop_coupon_columns( $column ) {
  294. global $post, $woocommerce;
  295. switch ( $column ) {
  296. case "coupon_code" :
  297. $edit_link = get_edit_post_link( $post->ID );
  298. $title = _draft_or_post_title();
  299. $post_type_object = get_post_type_object( $post->post_type );
  300. $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID );
  301. echo '<div class="code tips" data-tip="' . __( 'Edit coupon', 'woocommerce' ) . '"><a href="' . esc_attr( $edit_link ) . '"><span>' . esc_html( $title ). '</span></a></div>';
  302. _post_states( $post );
  303. // Get actions
  304. $actions = array();
  305. if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) {
  306. if ( 'trash' == $post->post_status )
  307. $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 . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore', 'woocommerce' ) . "</a>";
  308. elseif ( EMPTY_TRASH_DAYS )
  309. $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>";
  310. if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS )
  311. $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>";
  312. }
  313. $actions = apply_filters( 'post_row_actions', $actions, $post );
  314. echo '<div class="row-actions">';
  315. $i = 0;
  316. $action_count = sizeof($actions);
  317. foreach ( $actions as $action => $link ) {
  318. ++$i;
  319. ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
  320. echo "<span class='$action'>$link$sep</span>";
  321. }
  322. echo '</div>';
  323. break;
  324. case "type" :
  325. echo esc_html( wc_get_coupon_type( get_post_meta( $post->ID, 'discount_type', true ) ) );
  326. break;
  327. case "amount" :
  328. echo esc_html( get_post_meta( $post->ID, 'coupon_amount', true ) );
  329. break;
  330. case "products" :
  331. $product_ids = get_post_meta( $post->ID, 'product_ids', true );
  332. $product_ids = $product_ids ? array_map( 'absint', explode( ',', $product_ids ) ) : array();
  333. if ( sizeof( $product_ids ) > 0 )
  334. echo esc_html( implode( ', ', $product_ids ) );
  335. else
  336. echo '&ndash;';
  337. break;
  338. case "usage_limit" :
  339. $usage_limit = get_post_meta( $post->ID, 'usage_limit', true );
  340. if ( $usage_limit )
  341. echo esc_html( $usage_limit );
  342. else
  343. echo '&ndash;';
  344. break;
  345. case "usage" :
  346. $usage_count = absint( get_post_meta( $post->ID, 'usage_count', true ) );
  347. $usage_limit = esc_html( get_post_meta($post->ID, 'usage_limit', true) );
  348. if ( $usage_limit )
  349. printf( __( '%s / %s', 'woocommerce' ), $usage_count, $usage_limit );
  350. else
  351. printf( __( '%s / &infin;', 'woocommerce' ), $usage_count );
  352. break;
  353. case "expiry_date" :
  354. $expiry_date = get_post_meta($post->ID, 'expiry_date', true);
  355. if ( $expiry_date )
  356. echo esc_html( date_i18n( 'F j, Y', strtotime( $expiry_date ) ) );
  357. else
  358. echo '&ndash;';
  359. break;
  360. case "description" :
  361. echo wp_kses_post( $post->post_excerpt );
  362. break;
  363. }
  364. }
  365. /**
  366. * Output custom columns for coupons
  367. * @param string $column
  368. */
  369. public function render_shop_order_columns( $column ) {
  370. global $post, $woocommerce, $the_order;
  371. if ( empty( $the_order ) || $the_order->id != $post->ID ) {
  372. $the_order = wc_get_order( $post->ID );
  373. }
  374. switch ( $column ) {
  375. case 'order_status' :
  376. 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() ) );
  377. break;
  378. case 'order_date' :
  379. if ( '0000-00-00 00:00:00' == $post->post_date ) {
  380. $t_time = $h_time = __( 'Unpublished', 'woocommerce' );
  381. } else {
  382. $t_time = get_the_time( __( 'Y/m/d g:i:s A', 'woocommerce' ), $post );
  383. $gmt_time = strtotime( $post->post_date_gmt . ' UTC' );
  384. $time_diff = current_time( 'timestamp', 1 ) - $gmt_time;
  385. $h_time = get_the_time( __( 'Y/m/d', 'woocommerce' ), $post );
  386. }
  387. echo '<abbr title="' . esc_attr( $t_time ) . '">' . esc_html( apply_filters( 'post_date_column_time', $h_time, $post ) ) . '</abbr>';
  388. break;
  389. case 'customer_message' :
  390. if ( $the_order->customer_message )
  391. echo '<span class="note-on tips" data-tip="' . esc_attr( $the_order->customer_message ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>';
  392. else
  393. echo '<span class="na">&ndash;</span>';
  394. break;
  395. case 'order_items' :
  396. 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>';
  397. if ( sizeof( $the_order->get_items() ) > 0 ) {
  398. echo '<table class="order_items" cellspacing="0">';
  399. foreach ( $the_order->get_items() as $item ) {
  400. $_product = apply_filters( 'woocommerce_order_item_product', $the_order->get_product_from_item( $item ), $item );
  401. $item_meta = new WC_Order_Item_Meta( $item['item_meta'] );
  402. $item_meta_html = $item_meta->display( true, true );
  403. ?>
  404. <tr class="<?php echo apply_filters( 'woocommerce_admin_order_item_class', '', $item ); ?>">
  405. <td class="qty"><?php echo absint( $item['qty'] ); ?></td>
  406. <td class="name">
  407. <?php if ( wc_product_sku_enabled() && $_product && $_product->get_sku() ) echo $_product->get_sku() . ' - '; ?><?php echo apply_filters( 'woocommerce_order_item_name', $item['name'], $item ); ?>
  408. <?php if ( $item_meta_html ) : ?>
  409. <a class="tips" href="#" data-tip="<?php echo esc_attr( $item_meta_html ); ?>">[?]</a>
  410. <?php endif; ?>
  411. </td>
  412. </tr>
  413. <?php
  414. }
  415. echo '</table>';
  416. } else echo '&ndash;';
  417. break;
  418. case 'shipping_address' :
  419. if ( $the_order->get_formatted_shipping_address() )
  420. echo '<a target="_blank" href="' . esc_url( 'http://maps.google.com/maps?&q=' . urlencode( $the_order->get_shipping_address() ) . '&z=16' ) . '">'. esc_html( preg_replace( '#<br\s*/?>#i', ', ', $the_order->get_formatted_shipping_address() ) ) .'</a>';
  421. else
  422. echo '&ndash;';
  423. if ( $the_order->get_shipping_method() )
  424. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->get_shipping_method() ) . '</small>';
  425. break;
  426. case 'order_notes' :
  427. if ( $post->comment_count ) {
  428. // check the status of the post
  429. ( $post->post_status !== 'trash' ) ? $status = '' : $status = 'post-trashed';
  430. $latest_notes = get_comments( array(
  431. 'post_id' => $post->ID,
  432. 'number' => 1,
  433. 'status' => $status
  434. ) );
  435. $latest_note = current( $latest_notes );
  436. if ( $post->comment_count == 1 ) {
  437. echo '<span class="note-on tips" data-tip="' . esc_attr( $latest_note->comment_content ) . '">' . __( 'Yes', 'woocommerce' ) . '</span>';
  438. } else {
  439. $note_tip = isset( $latest_note->comment_content ) ? esc_attr( $latest_note->comment_content . '<small style="display:block">' . sprintf( _n( 'plus %d other note', 'plus %d other notes', ( $post->comment_count - 1 ), 'woocommerce' ), ( $post->comment_count - 1 ) ) . '</small>' ) : sprintf( _n( '%d note', '%d notes', $post->comment_count, 'woocommerce' ), $post->comment_count );
  440. echo '<span class="note-on tips" data-tip="' . $note_tip . '">' . __( 'Yes', 'woocommerce' ) . '</span>';
  441. }
  442. } else {
  443. echo '<span class="na">&ndash;</span>';
  444. }
  445. break;
  446. case 'order_total' :
  447. echo esc_html( strip_tags( $the_order->get_formatted_order_total() ) );
  448. if ( $the_order->payment_method_title ) {
  449. echo '<small class="meta">' . __( 'Via', 'woocommerce' ) . ' ' . esc_html( $the_order->payment_method_title ) . '</small>';
  450. }
  451. break;
  452. case 'order_title' :
  453. $customer_tip = '';
  454. if ( $address = $the_order->get_formatted_billing_address() ) {
  455. $customer_tip .= __( 'Billing:', 'woocommerce' ) . ' ' . $address . '<br/><br/>';
  456. }
  457. if ( $the_order->billing_phone ) {
  458. $customer_tip .= __( 'Tel:', 'woocommerce' ) . ' ' . $the_order->billing_phone;
  459. }
  460. echo '<div class="tips" data-tip="' . esc_attr( $customer_tip ) . '">';
  461. if ( $the_order->user_id ) {
  462. $user_info = get_userdata( $the_order->user_id );
  463. }
  464. if ( ! empty( $user_info ) ) {
  465. $username = '<a href="user-edit.php?user_id=' . absint( $user_info->ID ) . '">';
  466. if ( $user_info->first_name || $user_info->last_name ) {
  467. $username .= esc_html( ucfirst( $user_info->first_name ) . ' ' . ucfirst( $user_info->last_name ) );
  468. } else {
  469. $username .= esc_html( ucfirst( $user_info->display_name ) );
  470. }
  471. $username .= '</a>';
  472. } else {
  473. if ( $the_order->billing_first_name || $the_order->billing_last_name ) {
  474. $username = trim( $the_order->billing_first_name . ' ' . $the_order->billing_last_name );
  475. } else {
  476. $username = __( 'Guest', 'woocommerce' );
  477. }
  478. }
  479. printf( __( '%s by %s', 'woocommerce' ), '<a href="' . admin_url( 'post.php?post=' . absint( $post->ID ) . '&action=edit' ) . '"><strong>' . esc_attr( $the_order->get_order_number() ) . '</strong></a>', $username );
  480. if ( $the_order->billing_email ) {
  481. echo '<small class="meta email"><a href="' . esc_url( 'mailto:' . $the_order->billing_email ) . '">' . esc_html( $the_order->billing_email ) . '</a></small>';
  482. }
  483. echo '</div>';
  484. break;
  485. case 'order_actions' :
  486. ?><p>
  487. <?php
  488. do_action( 'woocommerce_admin_order_actions_start', $the_order );
  489. $actions = array();
  490. if ( $the_order->has_status( array( 'pending', 'on-hold' ) ) ) {
  491. $actions['processing'] = array(
  492. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_processing&order_id=' . $post->ID ), 'woocommerce-mark-order-processing' ),
  493. 'name' => __( 'Processing', 'woocommerce' ),
  494. 'action' => "processing"
  495. );
  496. }
  497. if ( $the_order->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) {
  498. $actions['complete'] = array(
  499. 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_complete&order_id=' . $post->ID ), 'woocommerce-mark-order-complete' ),
  500. 'name' => __( 'Complete', 'woocommerce' ),
  501. 'action' => "complete"
  502. );
  503. }
  504. $actions['view'] = array(
  505. 'url' => admin_url( 'post.php?post=' . $post->ID . '&action=edit' ),
  506. 'name' => __( 'View', 'woocommerce' ),
  507. 'action' => "view"
  508. );
  509. $actions = apply_filters( 'woocommerce_admin_order_actions', $actions, $the_order );
  510. foreach ( $actions as $action ) {
  511. 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'] ) );
  512. }
  513. do_action( 'woocommerce_admin_order_actions_end', $the_order );
  514. ?>
  515. </p><?php
  516. break;
  517. }
  518. }
  519. /**
  520. * Make columns sortable - https://gist.github.com/906872
  521. *
  522. * @param array $columns
  523. * @return array
  524. */
  525. public function product_sortable_columns( $columns ) {
  526. $custom = array(
  527. 'price' => 'price',
  528. 'featured' => 'featured',
  529. 'sku' => 'sku',
  530. 'name' => 'title'
  531. );
  532. return wp_parse_args( $custom, $columns );
  533. }
  534. /**
  535. * Make columns sortable - https://gist.github.com/906872
  536. *
  537. * @param array $columns
  538. * @return array
  539. */
  540. public function shop_coupon_sortable_columns( $columns ) {
  541. return $columns;
  542. }
  543. /**
  544. * Make columns sortable - https://gist.github.com/906872
  545. *
  546. * @param array $columns
  547. * @return array
  548. */
  549. public function shop_order_sortable_columns( $columns ) {
  550. $custom = array(
  551. 'order_title' => 'ID',
  552. 'order_total' => 'order_total',
  553. 'order_date' => 'date'
  554. );
  555. unset( $columns['comments'] );
  556. return wp_parse_args( $custom, $columns );
  557. }
  558. /**
  559. * Remove edit from the bulk actions.
  560. *
  561. * @param array $actions
  562. * @return array
  563. */
  564. public function shop_order_bulk_actions( $actions ) {
  565. if ( isset( $actions['edit'] ) ) {
  566. unset( $actions['edit'] );
  567. }
  568. return $actions;
  569. }
  570. /**
  571. * Product sorting link
  572. *
  573. * Based on Simple Page Ordering by 10up (http://wordpress.org/extend/plugins/simple-page-ordering/)
  574. *
  575. * @param array $views
  576. * @return array
  577. */
  578. public function product_sorting_link( $views ) {
  579. global $post_type, $wp_query;
  580. if ( ! current_user_can('edit_others_pages') ) {
  581. return $views;
  582. }
  583. $class = ( isset( $wp_query->query['orderby'] ) && $wp_query->query['orderby'] == 'menu_order title' ) ? 'current' : '';
  584. $query_string = remove_query_arg(array( 'orderby', 'order' ));
  585. $query_string = add_query_arg( 'orderby', urlencode('menu_order title'), $query_string );
  586. $query_string = add_query_arg( 'order', urlencode('ASC'), $query_string );
  587. $views['byorder'] = '<a href="'. $query_string . '" class="' . esc_attr( $class ) . '">' . __( 'Sort Products', 'woocommerce' ) . '</a>';
  588. return $views;
  589. }
  590. /**
  591. * Custom bulk edit - form
  592. *
  593. * @access public
  594. * @param mixed $column_name
  595. * @param mixed $post_type
  596. */
  597. public function bulk_edit( $column_name, $post_type ) {
  598. if ( 'price' != $column_name || 'product' != $post_type ) {
  599. return;
  600. }
  601. include( WC()->plugin_path() . '/includes/admin/views/html-bulk-edit-product.php' );
  602. }
  603. /**
  604. * Custom quick edit - form
  605. *
  606. * @access public
  607. * @param mixed $column_name
  608. * @param mixed $post_type
  609. */
  610. public function quick_edit( $column_name, $post_type ) {
  611. if ( 'price' != $column_name || 'product' != $post_type ) {
  612. return;
  613. }
  614. include( WC()->plugin_path() . '/includes/admin/views/html-quick-edit-product.php' );
  615. }
  616. /**
  617. * Quick and bulk edit saving
  618. *
  619. * @access public
  620. * @param int $post_id
  621. * @param WP_Post $post
  622. * @return int
  623. */
  624. public function bulk_and_quick_edit_save_post( $post_id, $post ) {
  625. // If this is an autosave, our form has not been submitted, so we don't want to do anything.
  626. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
  627. return $post_id;
  628. }
  629. // Don't save revisions and autosaves
  630. if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
  631. return $post_id;
  632. }
  633. // Check post type is product
  634. if ( 'product' != $post->post_type ) {
  635. return $post_id;
  636. }
  637. // Check user permission
  638. if ( ! current_user_can( 'edit_post', $post_id ) ) {
  639. return $post_id;
  640. }
  641. // Check nonces
  642. if ( ! isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) ) {
  643. return $post_id;
  644. }
  645. if ( isset( $_REQUEST['woocommerce_quick_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_quick_edit_nonce'], 'woocommerce_quick_edit_nonce' ) ) {
  646. return $post_id;
  647. }
  648. if ( isset( $_REQUEST['woocommerce_bulk_edit_nonce'] ) && ! wp_verify_nonce( $_REQUEST['woocommerce_bulk_edit_nonce'], 'woocommerce_bulk_edit_nonce' ) ) {
  649. return $post_id;
  650. }
  651. // Get the product and save
  652. $product = wc_get_product( $post );
  653. if ( ! empty( $_REQUEST['woocommerce_quick_edit'] ) ) {
  654. $this->quick_edit_save( $post_id, $product );
  655. } else {
  656. $this->bulk_edit_save( $post_id, $product );
  657. }
  658. // Clear transient
  659. wc_delete_product_transients( $post_id );
  660. return $post_id;
  661. }
  662. /**
  663. * Quick edit
  664. * @param integer $post_id
  665. */
  666. private function quick_edit_save( $post_id, $product ) {
  667. global $wpdb;
  668. $old_regular_price = $product->regular_price;
  669. $old_sale_price = $product->sale_price;
  670. // Save fields
  671. if ( isset( $_REQUEST['_sku'] ) ) {
  672. $sku = get_post_meta( $post_id, '_sku', true );
  673. $new_sku = wc_clean( stripslashes( $_REQUEST['_sku'] ) );
  674. if ( $new_sku !== $sku ) {
  675. if ( ! empty( $new_sku ) ) {
  676. $unique_sku = wc_product_has_unique_sku( $post_id, $new_sku );
  677. if ( $unique_sku ) {
  678. update_post_meta( $post_id, '_sku', $new_sku );
  679. }
  680. } else {
  681. update_post_meta( $post_id, '_sku', '' );
  682. }
  683. }
  684. }
  685. if ( isset( $_REQUEST['_weight'] ) ) {
  686. update_post_meta( $post_id, '_weight', wc_clean( $_REQUEST['_weight'] ) );
  687. }
  688. if ( isset( $_REQUEST['_length'] ) ) {
  689. update_post_meta( $post_id, '_length', wc_clean( $_REQUEST['_length'] ) );
  690. }
  691. if ( isset( $_REQUEST['_width'] ) ) {
  692. update_post_meta( $post_id, '_width', wc_clean( $_REQUEST['_width'] ) );
  693. }
  694. if ( isset( $_REQUEST['_height'] ) ) {
  695. update_post_meta( $post_id, '_height', wc_clean( $_REQUEST['_height'] ) );
  696. }
  697. if ( isset( $_REQUEST['_visibility'] ) ) {
  698. if ( update_post_meta( $post_id, '_visibility', wc_clean( $_REQUEST['_visibility'] ) ) ) {
  699. do_action( 'woocommerce_product_set_visibility', $post_id, wc_clean( $_REQUEST['_visibility'] ) );
  700. }
  701. }
  702. if ( isset( $_REQUEST['_featured'] ) ) {
  703. if ( update_post_meta( $post_id, '_featured', 'yes' ) ) {
  704. delete_transient( 'wc_featured_products' );
  705. }
  706. } else {
  707. if ( update_post_meta( $post_id, '_featured', 'no' ) ) {
  708. delete_transient( 'wc_featured_products' );
  709. }
  710. }
  711. if ( isset( $_REQUEST['_tax_status'] ) ) {
  712. update_post_meta( $post_id, '_tax_status', wc_clean( $_REQUEST['_tax_status'] ) );
  713. }
  714. if ( isset( $_REQUEST['_tax_class'] ) ) {
  715. update_post_meta( $post_id, '_tax_class', wc_clean( $_REQUEST['_tax_class'] ) );
  716. }
  717. if ( $product->is_type('simple') || $product->is_type('external') ) {
  718. if ( isset( $_REQUEST['_regular_price'] ) ) {
  719. $new_regular_price = $_REQUEST['_regular_price'] === '' ? '' : wc_format_decimal( $_REQUEST['_regular_price'] );
  720. update_post_meta( $post_id, '_regular_price', $new_regular_price );
  721. } else {
  722. $new_regular_price = null;
  723. }
  724. if ( isset( $_REQUEST['_sale_price'] ) ) {
  725. $new_sale_price = $_REQUEST['_sale_price'] === '' ? '' : wc_format_decimal( $_REQUEST['_sale_price'] );
  726. update_post_meta( $post_id, '_sale_price', $new_sale_price );
  727. } else {
  728. $new_sale_price = null;
  729. }
  730. // Handle price - remove dates and set to lowest
  731. $price_changed = false;
  732. if ( ! is_null( $new_regular_price ) && $new_regular_price != $old_regular_price ) {
  733. $price_changed = true;
  734. } elseif ( ! is_null( $new_sale_price ) && $new_sale_price != $old_sale_price ) {
  735. $price_changed = true;
  736. }
  737. if ( $price_changed ) {
  738. update_post_meta( $post_id, '_sale_price_dates_from', '' );
  739. update_post_meta( $post_id, '_sale_price_dates_to', '' );
  740. if ( ! is_null( $new_sale_price ) && $new_sale_price !== '' ) {
  741. update_post_meta( $post_id, '_price', $new_sale_price );
  742. } else {
  743. update_post_meta( $post_id, '_price', $new_regular_price );
  744. }
  745. }
  746. }
  747. // Handle stock status
  748. if ( isset( $_REQUEST['_stock_status'] ) ) {
  749. wc_update_product_stock_status( $post_id, wc_clean( $_REQUEST['_stock_status'] ) );
  750. }
  751. // Handle stock
  752. if ( ! $product->is_type('grouped') ) {
  753. if ( isset( $_REQUEST['_manage_stock'] ) ) {
  754. update_post_meta( $post_id, '_manage_stock', 'yes' );
  755. wc_update_product_stock( $post_id, wc_stock_amount( $_REQUEST['_stock'] ) );
  756. } else {
  757. update_post_meta( $post_id, '_manage_stock', 'no' );
  758. wc_update_product_stock( $post_id, 0 );
  759. }
  760. if ( ! empty( $_REQUEST['_backorders'] ) ) {
  761. update_post_meta( $post_id, '_backorders', wc_clean( $_REQUEST['_backorders'] ) );
  762. }
  763. }
  764. do_action( 'woocommerce_product_quick_edit_save', $product );
  765. }
  766. /**
  767. * Bulk edit
  768. * @param integer $post_id
  769. */
  770. public function bulk_edit_save( $post_id, $product ) {
  771. $old_regular_price = $product->regular_price;
  772. $old_sale_price = $product->sale_price;
  773. // Save fields
  774. if ( ! empty( $_REQUEST['change_weight'] ) && isset( $_REQUEST['_weight'] ) ) {
  775. update_post_meta( $post_id, '_weight', wc_clean( stripslashes( $_REQUEST['_weight'] ) ) );
  776. }
  777. if ( ! empty( $_REQUEST['change_dimensions'] ) ) {
  778. if ( isset( $_REQUEST['_length'] ) ) {
  779. update_post_meta( $post_id, '_length', wc_clean( stripslashes( $_REQUEST['_length'] ) ) );
  780. }
  781. if ( isset( $_REQUEST['_width'] ) ) {
  782. update_post_meta( $post_id, '_width', wc_clean( stripslashes( $_REQUEST['_width'] ) ) );
  783. }
  784. if ( isset( $_REQUEST['_height'] ) ) {
  785. update_post_meta( $post_id, '_height', wc_clean( stripslashes( $_REQUEST['_height'] ) ) );
  786. }
  787. }
  788. if ( ! empty( $_REQUEST['_tax_status'] ) ) {
  789. update_post_meta( $post_id, '_tax_status', wc_clean( $_REQUEST['_tax_status'] ) );
  790. }
  791. if ( ! empty( $_REQUEST['_tax_class'] ) ) {
  792. $tax_class = wc_clean( $_REQUEST['_tax_class'] );
  793. if ( 'standard' == $tax_class ) {
  794. $tax_class = '';
  795. }
  796. update_post_meta( $post_id, '_tax_class', $tax_class );
  797. }
  798. if ( ! empty( $_REQUEST['_stock_status'] ) ) {
  799. wc_update_product_stock_status( $post_id, wc_clean( $_REQUEST['_stock_status'] ) );
  800. }
  801. if ( ! empty( $_REQUEST['_visibility'] ) ) {
  802. if ( update_post_meta( $post_id, '_visibility', wc_clean( $_REQUEST['_visibility'] ) ) ) {
  803. do_action( 'woocommerce_product_set_visibility', $post_id, wc_clean( $_REQUEST['_visibility'] ) );
  804. }
  805. }
  806. if ( ! empty( $_REQUEST['_featured'] ) ) {
  807. if ( update_post_meta( $post_id, '_featured', stripslashes( $_REQUEST['_featured'] ) ) ) {
  808. delete_transient( 'wc_featured_products' );
  809. }
  810. }
  811. // Sold Individually
  812. if ( ! empty( $_REQUEST['_sold_individually'] ) ) {
  813. if ( $_REQUEST['_sold_individually'] == 'yes' ) {
  814. update_post_meta( $post_id, '_sold_individually', 'yes' );
  815. }
  816. else {
  817. update_post_meta( $post_id, '_sold_individually', '' );
  818. }
  819. }
  820. // Handle price - remove dates and set to lowest
  821. if ( $product->is_type( 'simple' ) || $product->is_type( 'external' ) ) {
  822. $price_changed = false;
  823. if ( ! empty( $_REQUEST['change_regular_price'] ) ) {
  824. $change_regular_price = absint( $_REQUEST['change_regular_price'] );
  825. $regular_price = esc_attr( stripslashes( $_REQUEST['_regular_price'] ) );
  826. switch ( $change_regular_price ) {
  827. case 1 :
  828. $new_price = $regular_price;
  829. break;
  830. case 2 :
  831. if ( strstr( $regular_price, '%' ) ) {
  832. $percent = str_replace( '%', '', $regular_price ) / 100;
  833. $new_price = $old_regular_price + ( round( $old_regular_price * $percent, absint( get_option( 'woocommerce_price_num_decimals' ) ) ) );
  834. } else {
  835. $new_price = $old_regular_price + $regular_price;
  836. }
  837. break;
  838. case 3 :
  839. if ( strstr( $regular_price, '%' ) ) {
  840. $percent = str_replace( '%', '', $regular_price ) / 100;
  841. $new_price = max( 0, $old_regular_price - ( round ( $old_regular_price * $percent, absint( get_option( 'woocommerce_price_num_decimals' ) ) ) ) );
  842. } else {
  843. $new_price = max( 0, $old_regular_price - $regular_price );
  844. }
  845. break;
  846. default :
  847. break;
  848. }
  849. if ( isset( $new_price ) && $new_price != $old_regular_price ) {
  850. $price_changed = true;
  851. $new_price = round( $new_price, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
  852. update_post_meta( $post_id, '_regular_price', $new_price );
  853. $product->regular_price = $new_price;
  854. }
  855. }
  856. if ( ! empty( $_REQUEST['change_sale_price'] ) ) {
  857. $change_sale_price = absint( $_REQUEST['change_sale_price'] );
  858. $sale_price = esc_attr( stripslashes( $_REQUEST['_sale_price'] ) );
  859. switch ( $change_sale_price ) {
  860. case 1 :
  861. $new_price = $sale_price;
  862. break;
  863. case 2 :
  864. if ( strstr( $sale_price, '%' ) ) {
  865. $percent = str_replace( '%', '', $sale_price ) / 100;
  866. $new_price = $old_sale_price + ( $old_sale_price * $percent );
  867. } else {
  868. $new_price = $old_sale_price + $sale_price;
  869. }
  870. break;
  871. case 3 :
  872. if ( strstr( $sale_price, '%' ) ) {
  873. $percent = str_replace( '%', '', $sale_price ) / 100;
  874. $new_price = max( 0, $old_sale_price - ( $old_sale_price * $percent ) );
  875. } else {
  876. $new_price = max( 0, $old_sale_price - $sale_price );
  877. }
  878. break;
  879. case 4 :
  880. if ( strstr( $sale_price, '%' ) ) {
  881. $percent = str_replace( '%', '', $sale_price ) / 100;
  882. $new_price = max( 0, $product->regular_price - ( $product->regular_price * $percent ) );
  883. } else {
  884. $new_price = max( 0, $product->regular_price - $sale_price );
  885. }
  886. break;
  887. default :
  888. break;
  889. }
  890. if ( isset( $new_price ) && $new_price != $old_sale_price ) {
  891. $price_changed = true;
  892. $new_price = round( $new_price, absint( get_option( 'woocommerce_price_num_decimals' ) ) );
  893. update_post_meta( $post_id, '_sale_price', $new_price );
  894. $product->sale_price = $new_price;
  895. }
  896. }
  897. if ( $price_changed ) {
  898. update_post_meta( $post_id, '_sale_price_dates_from', '' );
  899. update_post_meta( $post_id, '_sale_price_dates_to', '' );
  900. if ( $product->regular_price < $product->sale_price ) {
  901. $product->sale_price = '';
  902. update_post_meta( $post_id, '_sale_price', '' );
  903. }
  904. if ( $product->sale_price ) {
  905. update_post_meta( $post_id, '_price', $product->sale_price );
  906. } else {
  907. update_post_meta( $post_id, '_price', $product->regular_price );
  908. }
  909. }
  910. }
  911. // Handle stock
  912. if ( ! $product->is_type( 'grouped' ) ) {
  913. if ( ! empty( $_REQUEST['change_stock'] ) ) {
  914. update_post_meta( $post_id, '_manage_stock', 'yes' );
  915. wc_update_product_stock( $post_id, wc_stock_amount( $_REQUEST['_stock'] ) );
  916. }
  917. if ( ! empty( $_REQUEST['_manage_stock'] ) ) {
  918. if ( $_REQUEST['_manage_stock'] == 'yes' ) {
  919. update_post_meta( $post_id, '_manage_stock', 'yes' );
  920. } else {
  921. update_post_meta( $post_id, '_manage_stock', 'no' );
  922. wc_update_product_stock( $post_id, 0 );
  923. }
  924. }
  925. if ( ! empty( $_REQUEST['_backorders'] ) ) {
  926. update_post_meta( $post_id, '_backorders', wc_clean( $_REQUEST['_backorders'] ) );
  927. }
  928. }
  929. do_action( 'woocommerce_product_bulk_edit_save', $product );
  930. }
  931. /**
  932. * Add extra bulk action options to mark orders as complete or processing
  933. *
  934. * Using Javascript until WordPress core fixes: http://core.trac.wordpress.org/ticket/16031
  935. *
  936. * @access public
  937. * @return void
  938. */
  939. public function bulk_admin_footer() {
  940. global $post_type;
  941. if ( 'shop_order' == $post_type ) {
  942. ?>
  943. <script type="text/javascript">
  944. jQuery(function() {
  945. jQuery('<option>').val('mark_processing').text('<?php _e( 'Mark processing', 'woocommerce' )?>').appendTo("select[name='action']");
  946. jQuery('<option>').val('mark_processing').text('<?php _e( 'Mark processing', 'woocommerce' )?>').appendTo("select[name='action2']");
  947. jQuery('<option>').val('mark_on-hold').text('<?php _e( 'Mark on-hold', 'woocommerce' )?>').appendTo("select[name='action']");
  948. jQuery('<option>').val('mark_on-hold').text('<?php _e( 'Mark on-hold', 'woocommerce' )?>').appendTo("select[name='action2']");
  949. jQuery('<option>').val('mark_completed').text('<?php _e( 'Mark complete', 'woocommerce' )?>').appendTo("select[name='action']");
  950. jQuery('<option>').val('mark_completed').text('<?php _e( 'Mark complete', 'woocommerce' )?>').appendTo("select[name='action2']");
  951. });
  952. </script>
  953. <?php
  954. }
  955. }
  956. /**
  957. * Process the new bulk actions for changing order status
  958. *
  959. * @access public
  960. * @return void
  961. */
  962. public function bulk_action() {
  963. $wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
  964. $action = $wp_list_table->current_action();
  965. switch ( $action ) {
  966. case 'mark_completed':
  967. $new_status = 'completed';
  968. $report_action = 'marked_complete';
  969. break;
  970. case 'mark_processing':
  971. $new_status = 'processing';
  972. $report_action = 'marked_processing';
  973. break;
  974. case 'mark_on-hold' :
  975. $new_status = 'on-hold';
  976. $report_action = 'marked_on-hold';
  977. break;
  978. break;
  979. default:
  980. return;
  981. }
  982. $changed = 0;
  983. $post_ids = array_map( 'absint', (array) $_REQUEST['post'] );
  984. foreach ( $post_ids as $post_id ) {
  985. $order = wc_get_order( $post_id );
  986. $order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ) );
  987. $changed++;
  988. }
  989. $sendback = add_query_arg( array( 'post_type' => 'shop_order', $report_action => true, 'changed' => $changed, 'ids' => join( ',', $post_ids ) ), '' );
  990. wp_redirect( $sendback );
  991. exit();
  992. }
  993. /**
  994. * Show confirmation message that order status changed for number of orders
  995. *
  996. * @access public
  997. * @return void
  998. */
  999. public function bulk_admin_notices() {
  1000. global $post_type, $pagenow;
  1001. if ( isset( $_REQUEST['marked_complete'] ) || isset( $_REQUEST['marked_processing'] ) || isset( $_REQUEST['marked_on-hold'] ) ) {
  1002. $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0;
  1003. if ( 'edit.php' == $pagenow && 'shop_order' == $post_type ) {
  1004. $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) );
  1005. echo '<div class="updated"><p>' . $message . '</p></div>';
  1006. }
  1007. }
  1008. }
  1009. /**
  1010. * Search custom fields as well as content.
  1011. *
  1012. * @access public
  1013. * @param WP_Query $wp
  1014. * @return void
  1015. */
  1016. public function shop_order_search_custom_fields( $wp ) {
  1017. global $pagenow, $wpdb;
  1018. if ( 'edit.php' != $pagenow || empty( $wp->query_vars['s'] ) || $wp->query_vars['post_type'] != 'shop_order' ) {
  1019. return;
  1020. }
  1021. $search_fields = array_map( 'wc_clean', apply_filters( 'woocommerce_shop_order_search_fields', array(
  1022. '_order_key',
  1023. '_billing_company',
  1024. '_billing_address_1',
  1025. '_billing_address_2',
  1026. '_billing_city',
  1027. '_billing_postcode',
  1028. '_billing_country',
  1029. '_billing_state',
  1030. '_billing_email',
  1031. '_billing_phone',
  1032. '_shipping_address_1',
  1033. '_shipping_address_2',
  1034. '_shipping_city',
  1035. '_shipping_postcode',
  1036. '_shipping_country',
  1037. '_shipping_state'
  1038. ) ) );
  1039. $search_order_id = str_replace( 'Order #', '', $_GET['s'] );
  1040. if ( ! is_numeric( $search_order_id ) ) {
  1041. $search_order_id = 0;
  1042. }
  1043. // Search orders
  1044. $post_ids = array_unique( array_merge(
  1045. $wpdb->get_col(
  1046. $wpdb->prepare( "
  1047. SELECT p1.post_id
  1048. FROM {$wpdb->postmeta} p1
  1049. INNER JOIN {$wpdb->postmeta} p2 ON p1.post_id = p2.post_id
  1050. WHERE
  1051. ( p1.meta_key = '_billing_first_name' AND p2.meta_key = '_billing_last_name' AND CONCAT(p1.meta_value, ' ', p2.meta_value) LIKE '%%%s%%' )
  1052. OR
  1053. ( p1.meta_key = '_shipping_first_name' AND p2.meta_key = '_shipping_last_name' AND CONCAT(p1.meta_value, ' ', p2.meta_value) LIKE '%%%s%%' )
  1054. OR
  1055. ( p1.meta_key IN ('" . implode( "','", $search_fields ) . "') AND p1.meta_value LIKE '%%%s%%' )
  1056. ",
  1057. esc_attr( $_GET['s'] ), esc_attr( $_GET['s'] ), esc_attr( $_GET['s'] )
  1058. )
  1059. ),
  1060. $wpdb->get_col(
  1061. $wpdb->prepare( "
  1062. SELECT order_id
  1063. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  1064. WHERE order_item_name LIKE '%%%s%%'
  1065. ",
  1066. esc_attr( $_GET['s'] )
  1067. )
  1068. ),
  1069. array( $search_order_id )
  1070. ) );
  1071. // Remove s - we don't want to search order name
  1072. unset( $wp->query_vars['s'] );
  1073. // so we know we're doing this
  1074. $wp->query_vars['shop_order_search'] = true;
  1075. // Search by found posts
  1076. $wp->query_vars['post__in'] = $post_ids;
  1077. }
  1078. /**
  1079. * Change the label when searching orders.
  1080. *
  1081. * @access public
  1082. * @param mixed $query
  1083. * @return string
  1084. */
  1085. public function shop_order_search_label( $query ) {
  1086. global $pagenow, $typenow;
  1087. if ( 'edit.php' != $pagenow ) {
  1088. return $query;
  1089. }
  1090. if ( $typenow != 'shop_order' ) {
  1091. return $query;
  1092. }
  1093. if ( ! get_query_var( 'shop_order_search' ) ) {
  1094. return $query;
  1095. }
  1096. return wp_unslash( $_GET['s'] );
  1097. }
  1098. /**
  1099. * Query vars for custom searches.
  1100. *
  1101. * @access public
  1102. * @param mixed $public_query_vars
  1103. * @return array
  1104. */
  1105. public function add_custom_query_var( $public_query_vars ) {
  1106. $public_query_vars[] = 'sku';
  1107. $public_query_vars[] = 'shop_order_search';
  1108. return $public_query_vars;
  1109. }
  1110. /**
  1111. * Filters for post types
  1112. */
  1113. public function restrict_manage_posts() {
  1114. global $typenow, $wp_query;
  1115. switch ( $typenow ) {
  1116. case 'product' :
  1117. $this->product_filters();
  1118. break;
  1119. case 'shop_coupon' :
  1120. $this->shop_coupon_filters();
  1121. break;
  1122. case 'shop_order' :
  1123. $this->shop_order_filters();
  1124. break;
  1125. default :
  1126. break;
  1127. }
  1128. }
  1129. /**
  1130. * Show a category filter box
  1131. */
  1132. public function product_filters() {
  1133. global $wp_query;
  1134. // Category Filtering
  1135. wc_product_dropdown_categories();
  1136. // Type filtering
  1137. $terms = get_terms( 'product_type' );
  1138. $output = '<select name="product_type" id="dropdown_product_type">';
  1139. $output .= '<option value="">' . __( 'Show all product types', 'woocommerce' ) . '</option>';
  1140. foreach ( $terms as $term ) {
  1141. $output .= '<option value="' . sanitize_title( $term->name ) . '" ';
  1142. if ( isset( $wp_query->query['product_type'] ) ) {
  1143. $output .= selected( $term->slug, $wp_query->query['product_type'], false );
  1144. }
  1145. $output .= '>';
  1146. switch ( $term->name ) {
  1147. case 'grouped' :
  1148. $output .= __( 'Grouped product', 'woocommerce' );
  1149. break;
  1150. case 'external' :
  1151. $output .= __( 'External/Affiliate product', 'woocommerce' );
  1152. break;
  1153. case 'variable' :
  1154. $output .= __( 'Variable product', 'woocommerce' );
  1155. break;
  1156. case 'simple' :
  1157. $output .= __( 'Simple product', 'woocommerce' );
  1158. break;
  1159. default :
  1160. // Assuming that we have other types in future
  1161. $output .= ucfirst( $term->name );
  1162. break;
  1163. }
  1164. $output .= " ($term->count)</option>";
  1165. if ( 'simple' == $term->name ) {
  1166. $output .= '<option value="downloadable" ';
  1167. if ( isset( $wp_query->query['product_type'] ) ) {
  1168. $output .= selected( 'downloadable', $wp_query->query['product_type'], false );
  1169. }
  1170. $output .= '> &rarr; ' . __( 'Downloadable', 'woocommerce' ) . '</option>';
  1171. $output .= '<option value="virtual" ';
  1172. if ( isset( $wp_query->query['product_type'] ) ) {
  1173. $output .= selected( 'virtual'

Large files files are truncated, but you can click here to view the full file