class-studiou-wcmp-admin.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <?php
  2. /**
  3. * StudioU WC Mandatory Products Admin
  4. *
  5. * @package StudioU_WC_Mandatory_Products
  6. */
  7. // Exit if accessed directly
  8. if (!defined('ABSPATH')) {
  9. exit;
  10. }
  11. class StudioU_WCMP_Admin {
  12. /**
  13. * Constructor
  14. */
  15. public function __construct() {
  16. // Nothing to do at constructor
  17. }
  18. /**
  19. * Initialize hooks
  20. */
  21. public function init() {
  22. // Add fields to product category edit page
  23. add_action('product_cat_edit_form_fields', array($this, 'add_category_fields'), 20);
  24. // Add fields to product category add page
  25. add_action('product_cat_add_form_fields', array($this, 'add_category_fields_new'), 20);
  26. // Save category fields
  27. add_action('edited_product_cat', array($this, 'save_category_fields'), 20);
  28. add_action('created_product_cat', array($this, 'save_category_fields'), 20);
  29. // Add admin scripts and styles
  30. add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
  31. // Add product meta box to show mandatory products info
  32. add_action('add_meta_boxes', array($this, 'add_product_meta_boxes'));
  33. }
  34. /**
  35. * Add fields to product category edit form
  36. *
  37. * @param object $term The term object
  38. */
  39. public function add_category_fields($term) {
  40. // Get current value
  41. $mandatory_products = get_term_meta($term->term_id, 'mandatory_products', true);
  42. // If not array, initialize it
  43. if (!is_array($mandatory_products)) {
  44. $mandatory_products = array();
  45. }
  46. ?>
  47. <tr class="form-field">
  48. <th scope="row" valign="top">
  49. <label><?php _e('Mandatory Products', 'studiou-wc-product-cat-manage'); ?></label>
  50. </th>
  51. <td>
  52. <div id="studiou_mandatory_products_container">
  53. <?php if (!empty($mandatory_products)) : ?>
  54. <?php foreach ($mandatory_products as $index => $mandatory_product) :
  55. $product_id = isset($mandatory_product['product_id']) ? absint($mandatory_product['product_id']) : 0;
  56. $quantity = isset($mandatory_product['quantity']) ? absint($mandatory_product['quantity']) : 1;
  57. $type = isset($mandatory_product['type']) ? sanitize_text_field($mandatory_product['type']) : 'OnePerCart';
  58. $product_title = '';
  59. if ($product_id > 0) {
  60. $product = wc_get_product($product_id);
  61. if ($product) {
  62. $product_title = $product->get_formatted_name();
  63. }
  64. }
  65. ?>
  66. <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
  67. <p>
  68. <label><?php _e('Product:', 'studiou-wc-product-cat-manage'); ?></label>
  69. <select
  70. class="wc-product-search"
  71. style="width: 50%;"
  72. name="mandatory_product_id[]"
  73. data-placeholder="<?php esc_attr_e('Search for a product&hellip;', 'studiou-wc-product-cat-manage'); ?>"
  74. data-action="woocommerce_json_search_products_and_variations"
  75. data-allow_clear="true"
  76. >
  77. <?php if ($product_id && $product_title) : ?>
  78. <option value="<?php echo esc_attr($product_id); ?>" selected="selected"><?php echo esc_html($product_title); ?></option>
  79. <?php endif; ?>
  80. </select>
  81. </p>
  82. <p>
  83. <label><?php _e('Quantity:', 'studiou-wc-product-cat-manage'); ?></label>
  84. <input type="number" name="mandatory_product_quantity[]" value="<?php echo esc_attr($quantity); ?>" min="1" step="1" style="width: 80px;" />
  85. </p>
  86. <p>
  87. <label><?php _e('Type:', 'studiou-wc-product-cat-manage'); ?></label>
  88. <select name="mandatory_product_type[]">
  89. <option value="OnePerCart" <?php selected($type, 'OnePerCart'); ?>><?php _e('One Per Cart', 'studiou-wc-product-cat-manage'); ?></option>
  90. <option value="OnePerProduct" <?php selected($type, 'OnePerProduct'); ?>><?php _e('One Per Product', 'studiou-wc-product-cat-manage'); ?></option>
  91. </select>
  92. </p>
  93. <p>
  94. <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-product-cat-manage'); ?></button>
  95. </p>
  96. </div>
  97. <?php endforeach; ?>
  98. <?php endif; ?>
  99. </div>
  100. <p>
  101. <button type="button" class="button add-mandatory-product"><?php _e('Add Mandatory Product', 'studiou-wc-mandatory-products'); ?></button>
  102. </p>
  103. <p class="description">
  104. <?php _e('These products will be automatically added to the cart when a product from this category is added.', 'studiou-wc-product-cat-manage'); ?>
  105. <br>
  106. <?php _e('Note: Child categories will inherit these settings.', 'studiou-wc-product-cat-manage'); ?>
  107. </p>
  108. </td>
  109. </tr>
  110. <script type="text/template" id="tmpl-mandatory-product-row">
  111. <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
  112. <p>
  113. <label><?php _e('Product:', 'studiou-wc-mandatory-products'); ?></label>
  114. <select
  115. class="wc-product-search"
  116. style="width: 50%;"
  117. name="mandatory_product_id[]"
  118. data-placeholder="<?php esc_attr_e('Search for a product&hellip;', 'studiou-wc-mandatory-products'); ?>"
  119. data-action="woocommerce_json_search_products_and_variations"
  120. data-allow_clear="true"
  121. ></select>
  122. </p>
  123. <p>
  124. <label><?php _e('Quantity:', 'studiou-wc-mandatory-products'); ?></label>
  125. <input type="number" name="mandatory_product_quantity[]" value="1" min="1" step="1" style="width: 80px;" />
  126. </p>
  127. <p>
  128. <label><?php _e('Type:', 'studiou-wc-mandatory-products'); ?></label>
  129. <select name="mandatory_product_type[]">
  130. <option value="OnePerCart"><?php _e('One Per Cart', 'studiou-wc-mandatory-products'); ?></option>
  131. <option value="OnePerProduct"><?php _e('One Per Product', 'studiou-wc-mandatory-products'); ?></option>
  132. </select>
  133. </p>
  134. <p>
  135. <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-mandatory-products'); ?></button>
  136. </p>
  137. </div>
  138. </script>
  139. <script>
  140. jQuery(function($) {
  141. // Initialize select2
  142. $(document).ready(function() {
  143. $('.wc-product-search').selectWoo({
  144. width: '100%'
  145. });
  146. });
  147. // Add new row
  148. $('.add-mandatory-product').on('click', function() {
  149. var template = wp.template('mandatory-product-row');
  150. $('#studiou_mandatory_products_container').append(template());
  151. // Re-initialize select2 for the new row
  152. $('#studiou_mandatory_products_container .wc-product-search:last').selectWoo({
  153. width: '100%'
  154. });
  155. });
  156. // Remove row
  157. $(document).on('click', '.remove-mandatory-product', function() {
  158. $(this).closest('.mandatory-product-row').remove();
  159. });
  160. });
  161. </script>
  162. <?php
  163. }
  164. /**
  165. * Add fields to product category add form
  166. */
  167. public function add_category_fields_new() {
  168. ?>
  169. <div class="form-field">
  170. <label><?php _e('Mandatory Products', 'studiou-wc-mandatory-products'); ?></label>
  171. <div id="studiou_mandatory_products_container">
  172. <!-- Empty container for new category -->
  173. </div>
  174. <p>
  175. <button type="button" class="button add-mandatory-product"><?php _e('Add Mandatory Product', 'studiou-wc-mandatory-products'); ?></button>
  176. </p>
  177. <p class="description">
  178. <?php _e('These products will be automatically added to the cart when a product from this category is added.', 'studiou-wc-mandatory-products'); ?>
  179. <br>
  180. <?php _e('Note: Child categories will inherit these settings.', 'studiou-wc-mandatory-products'); ?>
  181. </p>
  182. </div>
  183. <script type="text/template" id="tmpl-mandatory-product-row">
  184. <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
  185. <p>
  186. <label><?php _e('Product:', 'studiou-wc-mandatory-products'); ?></label>
  187. <select
  188. class="wc-product-search"
  189. style="width: 50%;"
  190. name="mandatory_product_id[]"
  191. data-placeholder="<?php esc_attr_e('Search for a product&hellip;', 'studiou-wc-mandatory-products'); ?>"
  192. data-action="woocommerce_json_search_products_and_variations"
  193. data-allow_clear="true"
  194. ></select>
  195. </p>
  196. <p>
  197. <label><?php _e('Quantity:', 'studiou-wc-mandatory-products'); ?></label>
  198. <input type="number" name="mandatory_product_quantity[]" value="1" min="1" step="1" style="width: 80px;" />
  199. </p>
  200. <p>
  201. <label><?php _e('Type:', 'studiou-wc-mandatory-products'); ?></label>
  202. <select name="mandatory_product_type[]">
  203. <option value="OnePerCart"><?php _e('One Per Cart', 'studiou-wc-mandatory-products'); ?></option>
  204. <option value="OnePerProduct"><?php _e('One Per Product', 'studiou-wc-mandatory-products'); ?></option>
  205. </select>
  206. </p>
  207. <p>
  208. <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-mandatory-products'); ?></button>
  209. </p>
  210. </div>
  211. </script>
  212. <script>
  213. jQuery(function($) {
  214. // Add new row
  215. $('.add-mandatory-product').on('click', function() {
  216. var template = wp.template('mandatory-product-row');
  217. $('#studiou_mandatory_products_container').append(template());
  218. // Initialize select2 for the new row
  219. $('#studiou_mandatory_products_container .wc-product-search:last').selectWoo({
  220. width: '100%'
  221. });
  222. });
  223. // Remove row
  224. $(document).on('click', '.remove-mandatory-product', function() {
  225. $(this).closest('.mandatory-product-row').remove();
  226. });
  227. });
  228. </script>
  229. <?php
  230. }
  231. /**
  232. * Save category fields
  233. *
  234. * @param int $term_id The term ID
  235. */
  236. public function save_category_fields($term_id) {
  237. $mandatory_products = array();
  238. if (isset($_POST['mandatory_product_id']) && is_array($_POST['mandatory_product_id'])) {
  239. $product_ids = array_map('absint', $_POST['mandatory_product_id']);
  240. $quantities = isset($_POST['mandatory_product_quantity']) ? array_map('absint', $_POST['mandatory_product_quantity']) : array();
  241. $types = isset($_POST['mandatory_product_type']) ? array_map('sanitize_text_field', $_POST['mandatory_product_type']) : array();
  242. foreach ($product_ids as $index => $product_id) {
  243. // Skip if product ID is empty
  244. if (empty($product_id)) {
  245. continue;
  246. }
  247. $quantity = isset($quantities[$index]) ? max(1, $quantities[$index]) : 1;
  248. $type = isset($types[$index]) ? $types[$index] : 'OnePerCart';
  249. // Validate type
  250. if (!in_array($type, array('OnePerCart', 'OnePerProduct'))) {
  251. $type = 'OnePerCart';
  252. }
  253. $mandatory_products[] = array(
  254. 'product_id' => $product_id,
  255. 'quantity' => $quantity,
  256. 'type' => $type
  257. );
  258. }
  259. }
  260. update_term_meta($term_id, 'mandatory_products', $mandatory_products);
  261. }
  262. /**
  263. * Enqueue admin scripts and styles
  264. *
  265. * @param string $hook Current admin page
  266. */
  267. public function enqueue_admin_scripts($hook) {
  268. // Only on category edit page
  269. if ('edit-tags.php' !== $hook && 'term.php' !== $hook) {
  270. return;
  271. }
  272. // Check if we're on the product category screen
  273. $screen = get_current_screen();
  274. if (!$screen || 'product_cat' !== $screen->taxonomy) {
  275. return;
  276. }
  277. // WooCommerce admin scripts
  278. wp_enqueue_script('wc-enhanced-select');
  279. wp_enqueue_style('woocommerce_admin_styles');
  280. }
  281. /**
  282. * Add product meta boxes
  283. */
  284. public function add_product_meta_boxes() {
  285. add_meta_box(
  286. 'studiou-wcmp-mandatory-products',
  287. __('Mandatory Products Info', 'studiou-wc-product-cat-manage'),
  288. array($this, 'render_product_meta_box'),
  289. 'product',
  290. 'side',
  291. 'default'
  292. );
  293. }
  294. /**
  295. * Render product meta box
  296. *
  297. * @param WP_Post $post The post object
  298. */
  299. public function render_product_meta_box($post) {
  300. $product_id = $post->ID;
  301. // Get plugin instance
  302. $plugin = new StudioU_WC_Mandatory_Products();
  303. // Get mandatory products
  304. $mandatory_products = $plugin->get_mandatory_products($product_id);
  305. if (empty($mandatory_products)) {
  306. echo '<p>' . __('No mandatory products set for this product\'s categories.', 'studiou-wc-mandatory-products') . '</p>';
  307. return;
  308. }
  309. echo '<p>' . __('This product has mandatory products set in its categories:', 'studiou-wc-mandatory-products') . '</p>';
  310. echo '<ul>';
  311. foreach ($mandatory_products as $mandatory_product) {
  312. $product_id = absint($mandatory_product['product_id']);
  313. $quantity = absint($mandatory_product['quantity']);
  314. $type = sanitize_text_field($mandatory_product['type']);
  315. $mandatory_product_obj = wc_get_product($product_id);
  316. if ($mandatory_product_obj) {
  317. echo '<li>';
  318. echo sprintf(
  319. '%s x %d (%s)',
  320. $mandatory_product_obj->get_name(),
  321. $quantity,
  322. $type === 'OnePerCart' ? __('One per cart', 'studiou-wc-mandatory-products') : __('One per product', 'studiou-wc-mandatory-products')
  323. );
  324. echo '</li>';
  325. }
  326. }
  327. echo '</ul>';
  328. echo '<p class="description">' . __('These products will be automatically added to the customer\'s cart when this product is added.', 'studiou-wc-mandatory-products') . '</p>';
  329. }
  330. }