html-variation-admin.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <?php
  2. /**
  3. * Outputs a variation for editing.
  4. *
  5. * @var int $variation_id
  6. * @var WP_POST $variation
  7. * @var WC_Product_Variation $variation_object
  8. * @var array $variation_data array of variation data @deprecated.
  9. */
  10. if ( ! defined( 'ABSPATH' ) ) {
  11. exit;
  12. }
  13. ?>
  14. <div class="woocommerce_variation wc-metabox closed">
  15. <h3>
  16. <a href="#" class="remove_variation delete" rel="<?php echo esc_attr( $variation_id ); ?>"><?php esc_html_e( 'Remove', 'woocommerce' ); ?></a>
  17. <div class="handlediv" aria-label="<?php esc_attr_e( 'Click to toggle', 'woocommerce' ); ?>"></div>
  18. <div class="tips sort" data-tip="<?php esc_attr_e( 'Drag and drop, or click to set admin variation order', 'woocommerce' ); ?>"></div>
  19. <strong>#<?php echo esc_html( $variation_id ); ?> </strong>
  20. <?php
  21. $attribute_values = $variation_object->get_attributes( 'edit' );
  22. foreach ( $product_object->get_attributes( 'edit' ) as $attribute ) {
  23. if ( ! $attribute->get_variation() ) {
  24. continue;
  25. }
  26. $selected_value = isset( $attribute_values[ sanitize_title( $attribute->get_name() ) ] ) ? $attribute_values[ sanitize_title( $attribute->get_name() ) ] : '';
  27. ?>
  28. <select name="attribute_<?php echo sanitize_title( $attribute->get_name() ) . "[{$loop}]"; ?>">
  29. <option value="">
  30. <?php
  31. /* translators: %s: attribute label */
  32. printf( esc_html__( 'Any %s&hellip;', 'woocommerce' ), wc_attribute_label( $attribute->get_name() ) );
  33. ?>
  34. </option>
  35. <?php if ( $attribute->is_taxonomy() ) : ?>
  36. <?php foreach ( $attribute->get_terms() as $option ) : ?>
  37. <option <?php selected( $selected_value, $option->slug ); ?> value="<?php echo esc_attr( $option->slug ); ?>"><?php echo esc_html( apply_filters( 'woocommerce_variation_option_name', $option->name ) ); ?></option>
  38. <?php endforeach; ?>
  39. <?php else : ?>
  40. <?php foreach ( $attribute->get_options() as $option ) : ?>
  41. <option <?php selected( $selected_value, $option ); ?> value="<?php echo esc_attr( $option ); ?>"><?php echo esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ); ?></option>
  42. <?php endforeach; ?>
  43. <?php endif; ?>
  44. </select>
  45. <?php
  46. }
  47. ?>
  48. <input type="hidden" name="variable_post_id[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( $variation_id ); ?>" />
  49. <input type="hidden" class="variation_menu_order" name="variation_menu_order[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( $variation_object->get_menu_order( 'edit' ) ); ?>" />
  50. </h3>
  51. <div class="woocommerce_variable_attributes wc-metabox-content" style="display: none;">
  52. <div class="data">
  53. <p class="form-row form-row-first upload_image">
  54. <a href="#" class="upload_image_button tips <?php echo $variation_object->get_image_id( 'edit' ) ? 'remove' : ''; ?>" data-tip="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_attr__( 'Remove this image', 'woocommerce' ) : esc_attr__( 'Upload an image', 'woocommerce' ); ?>" rel="<?php echo esc_attr( $variation_id ); ?>">
  55. <img src="<?php echo $variation_object->get_image_id( 'edit' ) ? esc_url( wp_get_attachment_thumb_url( $variation_object->get_image_id( 'edit' ) ) ) : esc_url( wc_placeholder_img_src() ); ?>" /><input type="hidden" name="upload_image_id[<?php echo esc_attr( $loop ); ?>]" class="upload_image_id" value="<?php echo esc_attr( $variation_object->get_image_id( 'edit' ) ); ?>" />
  56. </a>
  57. </p>
  58. <?php
  59. if ( wc_product_sku_enabled() ) {
  60. woocommerce_wp_text_input(
  61. array(
  62. 'id' => "variable_sku{$loop}",
  63. 'name' => "variable_sku[{$loop}]",
  64. 'value' => $variation_object->get_sku( 'edit' ),
  65. 'placeholder' => $variation_object->get_sku(),
  66. 'label' => '<abbr title="' . esc_attr__( 'Stock Keeping Unit', 'woocommerce' ) . '">' . esc_html__( 'SKU', 'woocommerce' ) . '</abbr>',
  67. 'desc_tip' => true,
  68. 'description' => __( 'SKU refers to a Stock-keeping unit, a unique identifier for each distinct product and service that can be purchased.', 'woocommerce' ),
  69. 'wrapper_class' => 'form-row form-row-last',
  70. )
  71. );
  72. }
  73. ?>
  74. <p class="form-row form-row-full options">
  75. <label>
  76. <?php esc_html_e( 'Enabled', 'woocommerce' ); ?>:
  77. <input type="checkbox" class="checkbox" name="variable_enabled[<?php echo $loop; ?>]" <?php checked( in_array( $variation_object->get_status( 'edit' ), array( 'publish', false ), true ), true ); ?> />
  78. </label>
  79. <label class="tips" data-tip="<?php esc_html_e( 'Enable this option if access is given to a downloadable file upon purchase of a product', 'woocommerce' ); ?>">
  80. <?php esc_html_e( 'Downloadable', 'woocommerce' ); ?>:
  81. <input type="checkbox" class="checkbox variable_is_downloadable" name="variable_is_downloadable[<?php echo esc_attr( $loop ); ?>]" <?php checked( $variation_object->get_downloadable( 'edit' ), true ); ?> />
  82. </label>
  83. <label class="tips" data-tip="<?php esc_html_e( 'Enable this option if a product is not shipped or there is no shipping cost', 'woocommerce' ); ?>">
  84. <?php esc_html_e( 'Virtual', 'woocommerce' ); ?>:
  85. <input type="checkbox" class="checkbox variable_is_virtual" name="variable_is_virtual[<?php echo esc_attr( $loop ); ?>]" <?php checked( $variation_object->get_virtual( 'edit' ), true ); ?> />
  86. </label>
  87. <?php if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) : ?>
  88. <label class="tips" data-tip="<?php esc_html_e( 'Enable this option to enable stock management at variation level', 'woocommerce' ); ?>">
  89. <?php esc_html_e( 'Manage stock?', 'woocommerce' ); ?>
  90. <input type="checkbox" class="checkbox variable_manage_stock" name="variable_manage_stock[<?php echo esc_attr( $loop ); ?>]" <?php checked( $variation_object->get_manage_stock(), true ); // Use view context so 'parent' is considered. ?> />
  91. </label>
  92. <?php endif; ?>
  93. <?php do_action( 'woocommerce_variation_options', $loop, $variation_data, $variation ); ?>
  94. </p>
  95. <div class="variable_pricing">
  96. <?php
  97. $label = sprintf(
  98. /* translators: %s: currency symbol */
  99. __( 'Regular price (%s)', 'woocommerce' ),
  100. get_woocommerce_currency_symbol()
  101. );
  102. woocommerce_wp_text_input(
  103. array(
  104. 'id' => "variable_regular_price_{$loop}",
  105. 'name' => "variable_regular_price[{$loop}]",
  106. 'value' => wc_format_localized_price( $variation_object->get_regular_price( 'edit' ) ),
  107. 'label' => $label,
  108. 'data_type' => 'price',
  109. 'wrapper_class' => 'form-row form-row-first',
  110. 'placeholder' => __( 'Variation price (required)', 'woocommerce' ),
  111. )
  112. );
  113. $label = sprintf(
  114. /* translators: %s: currency symbol */
  115. __( 'Sale price (%s)', 'woocommerce' ),
  116. get_woocommerce_currency_symbol()
  117. );
  118. woocommerce_wp_text_input(
  119. array(
  120. 'id' => "variable_sale_price{$loop}",
  121. 'name' => "variable_sale_price[{$loop}]",
  122. 'value' => wc_format_localized_price( $variation_object->get_sale_price( 'edit' ) ),
  123. 'data_type' => 'price',
  124. 'label' => $label . ' <a href="#" class="sale_schedule">' . esc_html__( 'Schedule', 'woocommerce' ) . '</a><a href="#" class="cancel_sale_schedule hidden">' . esc_html__( 'Cancel schedule', 'woocommerce' ) . '</a>',
  125. 'wrapper_class' => 'form-row form-row-last',
  126. )
  127. );
  128. $sale_price_dates_from = $variation_object->get_date_on_sale_from( 'edit' ) && ( $date = $variation_object->get_date_on_sale_from( 'edit' )->getOffsetTimestamp() ) ? date_i18n( 'Y-m-d', $date ) : '';
  129. $sale_price_dates_to = $variation_object->get_date_on_sale_to( 'edit' ) && ( $date = $variation_object->get_date_on_sale_to( 'edit' )->getOffsetTimestamp() ) ? date_i18n( 'Y-m-d', $date ) : '';
  130. echo '<div class="form-field sale_price_dates_fields hidden">
  131. <p class="form-row form-row-first">
  132. <label>' . __( 'Sale start date', 'woocommerce' ) . '</label>
  133. <input type="text" class="sale_price_dates_from" name="variable_sale_price_dates_from[' . $loop . ']" value="' . esc_attr( $sale_price_dates_from ) . '" placeholder="' . _x( 'From&hellip;', 'placeholder', 'woocommerce' ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" />
  134. </p>
  135. <p class="form-row form-row-last">
  136. <label>' . __( 'Sale end date', 'woocommerce' ) . '</label>
  137. <input type="text" class="sale_price_dates_to" name="variable_sale_price_dates_to[' . esc_attr( $loop ) . ']" value="' . esc_attr( $sale_price_dates_to ) . '" placeholder="' . esc_html_x( 'To&hellip;', 'placeholder', 'woocommerce' ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" />
  138. </p>
  139. </div>';
  140. /**
  141. * woocommerce_variation_options_pricing action.
  142. *
  143. * @since 2.5.0
  144. *
  145. * @param int $loop
  146. * @param array $variation_data
  147. * @param WP_Post $variation
  148. */
  149. do_action( 'woocommerce_variation_options_pricing', $loop, $variation_data, $variation );
  150. ?>
  151. </div>
  152. <?php if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) : ?>
  153. <div class="show_if_variation_manage_stock" style="display: none;">
  154. <?php
  155. woocommerce_wp_text_input(
  156. array(
  157. 'id' => "variable_stock{$loop}",
  158. 'name' => "variable_stock[{$loop}]",
  159. 'value' => wc_stock_amount( $variation_object->get_stock_quantity( 'edit' ) ),
  160. 'label' => __( 'Stock quantity', 'woocommerce' ),
  161. 'desc_tip' => true,
  162. 'description' => __( "Enter a quantity to enable stock management at variation level, or leave blank to use the parent product's options.", 'woocommerce' ),
  163. 'type' => 'number',
  164. 'custom_attributes' => array(
  165. 'step' => 'any',
  166. ),
  167. 'data_type' => 'stock',
  168. 'wrapper_class' => 'form-row form-row-first',
  169. )
  170. );
  171. echo '<input type="hidden" name="variable_original_stock[' . esc_attr( $loop ) . ']" value="' . esc_attr( wc_stock_amount( $variation_object->get_stock_quantity( 'edit' ) ) ) . '" />';
  172. woocommerce_wp_select(
  173. array(
  174. 'id' => "variable_backorders{$loop}",
  175. 'name' => "variable_backorders[{$loop}]",
  176. 'value' => $variation_object->get_backorders( 'edit' ),
  177. 'label' => __( 'Allow backorders?', 'woocommerce' ),
  178. 'options' => wc_get_product_backorder_options(),
  179. 'desc_tip' => true,
  180. 'description' => __( 'If managing stock, this controls whether or not backorders are allowed. If enabled, stock quantity can go below 0.', 'woocommerce' ),
  181. 'wrapper_class' => 'form-row form-row-last',
  182. )
  183. );
  184. /**
  185. * woocommerce_variation_options_inventory action.
  186. *
  187. * @since 2.5.0
  188. *
  189. * @param int $loop
  190. * @param array $variation_data
  191. * @param WP_Post $variation
  192. */
  193. do_action( 'woocommerce_variation_options_inventory', $loop, $variation_data, $variation );
  194. ?>
  195. </div>
  196. <?php endif; ?>
  197. <div>
  198. <?php
  199. woocommerce_wp_select(
  200. array(
  201. 'id' => "variable_stock_status{$loop}",
  202. 'name' => "variable_stock_status[{$loop}]",
  203. 'value' => $variation_object->get_stock_status( 'edit' ),
  204. 'label' => __( 'Stock status', 'woocommerce' ),
  205. 'options' => wc_get_product_stock_status_options(),
  206. 'desc_tip' => true,
  207. 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ),
  208. 'wrapper_class' => 'form-row form-row-full hide_if_variation_manage_stock',
  209. )
  210. );
  211. if ( wc_product_weight_enabled() ) {
  212. $label = sprintf(
  213. /* translators: %s: weight unit */
  214. __( 'Weight (%s)', 'woocommerce' ),
  215. esc_html( get_option( 'woocommerce_weight_unit' ) )
  216. );
  217. woocommerce_wp_text_input(
  218. array(
  219. 'id' => "variable_weight{$loop}",
  220. 'name' => "variable_weight[{$loop}]",
  221. 'value' => wc_format_localized_decimal( $variation_object->get_weight( 'edit' ) ),
  222. 'placeholder' => wc_format_localized_decimal( $product_object->get_weight() ),
  223. 'label' => $label,
  224. 'desc_tip' => true,
  225. 'description' => __( 'Weight in decimal form', 'woocommerce' ),
  226. 'type' => 'text',
  227. 'data_type' => 'decimal',
  228. 'wrapper_class' => 'form-row form-row-first hide_if_variation_virtual',
  229. )
  230. );
  231. }
  232. if ( wc_product_dimensions_enabled() ) {
  233. $parent_length = wc_format_localized_decimal( $product_object->get_length() );
  234. $parent_width = wc_format_localized_decimal( $product_object->get_width() );
  235. $parent_height = wc_format_localized_decimal( $product_object->get_height() );
  236. ?>
  237. <p class="form-field form-row dimensions_field hide_if_variation_virtual form-row-last">
  238. <label for="product_length">
  239. <?php
  240. printf(
  241. /* translators: %s: dimension unit */
  242. esc_html__( 'Dimensions (L&times;W&times;H) (%s)', 'woocommerce' ),
  243. get_option( 'woocommerce_dimension_unit' )
  244. );
  245. ?>
  246. </label>
  247. <?php echo wc_help_tip( __( 'Length x width x height in decimal form', 'woocommerce' ) ); ?>
  248. <span class="wrap">
  249. <input id="product_length" placeholder="<?php echo $parent_length ? esc_attr( $parent_length ) : esc_attr__( 'Length', 'woocommerce' ); ?>" class="input-text wc_input_decimal" size="6" type="text" name="variable_length[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_decimal( $variation_object->get_length( 'edit' ) ) ); ?>" />
  250. <input placeholder="<?php echo $parent_width ? esc_attr( $parent_width ) : esc_attr__( 'Width', 'woocommerce' ); ?>" class="input-text wc_input_decimal" size="6" type="text" name="variable_width[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_decimal( $variation_object->get_width( 'edit' ) ) ); ?>" />
  251. <input placeholder="<?php echo $parent_height ? esc_attr( $parent_height ) : esc_attr__( 'Height', 'woocommerce' ); ?>" class="input-text wc_input_decimal last" size="6" type="text" name="variable_height[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( wc_format_localized_decimal( $variation_object->get_height( 'edit' ) ) ); ?>" />
  252. </span>
  253. </p>
  254. <?php
  255. }
  256. /**
  257. * woocommerce_variation_options_dimensions action.
  258. *
  259. * @since 2.5.0
  260. *
  261. * @param int $loop
  262. * @param array $variation_data
  263. * @param WP_Post $variation
  264. */
  265. do_action( 'woocommerce_variation_options_dimensions', $loop, $variation_data, $variation );
  266. ?>
  267. </div>
  268. <div>
  269. <p class="form-row hide_if_variation_virtual form-row-full">
  270. <label><?php esc_html_e( 'Shipping class', 'woocommerce' ); ?></label>
  271. <?php
  272. wp_dropdown_categories(
  273. array(
  274. 'taxonomy' => 'product_shipping_class',
  275. 'hide_empty' => 0,
  276. 'show_option_none' => __( 'Same as parent', 'woocommerce' ),
  277. 'name' => 'variable_shipping_class[' . $loop . ']',
  278. 'id' => '',
  279. 'selected' => $variation_object->get_shipping_class_id( 'edit' ),
  280. )
  281. );
  282. ?>
  283. </p>
  284. <?php
  285. if ( wc_tax_enabled() ) {
  286. woocommerce_wp_select(
  287. array(
  288. 'id' => "variable_tax_class{$loop}",
  289. 'name' => "variable_tax_class[{$loop}]",
  290. 'value' => $variation_object->get_tax_class( 'edit' ),
  291. 'label' => __( 'Tax class', 'woocommerce' ),
  292. 'options' => array( 'parent' => __( 'Same as parent', 'woocommerce' ) ) + wc_get_product_tax_class_options(),
  293. 'desc_tip' => 'true',
  294. 'description' => __( 'Choose a tax class for this product. Tax classes are used to apply different tax rates specific to certain types of product.', 'woocommerce' ),
  295. 'wrapper_class' => 'form-row form-row-full',
  296. )
  297. );
  298. /**
  299. * woocommerce_variation_options_tax action.
  300. *
  301. * @since 2.5.0
  302. *
  303. * @param int $loop
  304. * @param array $variation_data
  305. * @param WP_Post $variation
  306. */
  307. do_action( 'woocommerce_variation_options_tax', $loop, $variation_data, $variation );
  308. }
  309. ?>
  310. </div>
  311. <div>
  312. <?php
  313. woocommerce_wp_textarea_input(
  314. array(
  315. 'id' => "variable_description{$loop}",
  316. 'name' => "variable_description[{$loop}]",
  317. 'value' => $variation_object->get_description( 'edit' ),
  318. 'label' => __( 'Description', 'woocommerce' ),
  319. 'desc_tip' => true,
  320. 'description' => __( 'Enter an optional description for this variation.', 'woocommerce' ),
  321. 'wrapper_class' => 'form-row form-row-full',
  322. )
  323. );
  324. ?>
  325. </div>
  326. <div class="show_if_variation_downloadable" style="display: none;">
  327. <div class="form-row form-row-full downloadable_files">
  328. <label><?php esc_html_e( 'Downloadable files', 'woocommerce' ); ?></label>
  329. <table class="widefat">
  330. <thead>
  331. <div>
  332. <th><?php esc_html_e( 'Name', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the name of the download shown to the customer.', 'woocommerce' ) ); ?></th>
  333. <th colspan="2"><?php esc_html_e( 'File URL', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the URL or absolute path to the file which customers will get access to. URLs entered here should already be encoded.', 'woocommerce' ) ); ?></th>
  334. <th>&nbsp;</th>
  335. </div>
  336. </thead>
  337. <tbody>
  338. <?php
  339. if ( $downloads = $variation_object->get_downloads( 'edit' ) ) {
  340. foreach ( $downloads as $key => $file ) {
  341. include 'html-product-variation-download.php';
  342. }
  343. }
  344. ?>
  345. </tbody>
  346. <tfoot>
  347. <div>
  348. <th colspan="4">
  349. <a href="#" class="button insert" data-row="
  350. <?php
  351. $key = '';
  352. $file = array(
  353. 'file' => '',
  354. 'name' => '',
  355. );
  356. ob_start();
  357. require 'html-product-variation-download.php';
  358. echo esc_attr( ob_get_clean() );
  359. ?>
  360. "><?php esc_html_e( 'Add file', 'woocommerce' ); ?></a>
  361. </th>
  362. </div>
  363. </tfoot>
  364. </table>
  365. </div>
  366. </div>
  367. <div class="show_if_variation_downloadable" style="display: none;">
  368. <?php
  369. woocommerce_wp_text_input(
  370. array(
  371. 'id' => "variable_download_limit{$loop}",
  372. 'name' => "variable_download_limit[{$loop}]",
  373. 'value' => $variation_object->get_download_limit( 'edit' ) < 0 ? '' : $variation_object->get_download_limit( 'edit' ),
  374. 'label' => __( 'Download limit', 'woocommerce' ),
  375. 'placeholder' => __( 'Unlimited', 'woocommerce' ),
  376. 'description' => __( 'Leave blank for unlimited re-downloads.', 'woocommerce' ),
  377. 'type' => 'number',
  378. 'desc_tip' => true,
  379. 'custom_attributes' => array(
  380. 'step' => '1',
  381. 'min' => '0',
  382. ),
  383. 'wrapper_class' => 'form-row form-row-first',
  384. )
  385. );
  386. woocommerce_wp_text_input(
  387. array(
  388. 'id' => "variable_download_expiry{$loop}",
  389. 'name' => "variable_download_expiry[{$loop}]",
  390. 'value' => $variation_object->get_download_expiry( 'edit' ) < 0 ? '' : $variation_object->get_download_expiry( 'edit' ),
  391. 'label' => __( 'Download expiry', 'woocommerce' ),
  392. 'placeholder' => __( 'Never', 'woocommerce' ),
  393. 'description' => __( 'Enter the number of days before a download link expires, or leave blank.', 'woocommerce' ),
  394. 'type' => 'number',
  395. 'desc_tip' => true,
  396. 'custom_attributes' => array(
  397. 'step' => '1',
  398. 'min' => '0',
  399. ),
  400. 'wrapper_class' => 'form-row form-row-last',
  401. )
  402. );
  403. /**
  404. * woocommerce_variation_options_download action.
  405. *
  406. * @since 2.5.0
  407. *
  408. * @param int $loop
  409. * @param array $variation_data
  410. * @param WP_Post $variation
  411. */
  412. do_action( 'woocommerce_variation_options_download', $loop, $variation_data, $variation );
  413. ?>
  414. </div>
  415. <?php do_action( 'woocommerce_product_after_variable_attributes', $loop, $variation_data, $variation ); ?>
  416. </div>
  417. </div>
  418. </div>