|
@@ -0,0 +1,371 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+/**
|
|
|
|
|
+ * StudioU WC Mandatory Products Admin
|
|
|
|
|
+ *
|
|
|
|
|
+ * @package StudioU_WC_Mandatory_Products
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+// Exit if accessed directly
|
|
|
|
|
+if (!defined('ABSPATH')) {
|
|
|
|
|
+ exit;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class StudioU_WCMP_Admin {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Constructor
|
|
|
|
|
+ */
|
|
|
|
|
+ public function __construct() {
|
|
|
|
|
+ // Nothing to do at constructor
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Initialize hooks
|
|
|
|
|
+ */
|
|
|
|
|
+ public function init() {
|
|
|
|
|
+ // Add fields to product category edit page
|
|
|
|
|
+ add_action('product_cat_edit_form_fields', array($this, 'add_category_fields'), 20);
|
|
|
|
|
+
|
|
|
|
|
+ // Add fields to product category add page
|
|
|
|
|
+ add_action('product_cat_add_form_fields', array($this, 'add_category_fields_new'), 20);
|
|
|
|
|
+
|
|
|
|
|
+ // Save category fields
|
|
|
|
|
+ add_action('edited_product_cat', array($this, 'save_category_fields'), 20);
|
|
|
|
|
+ add_action('created_product_cat', array($this, 'save_category_fields'), 20);
|
|
|
|
|
+
|
|
|
|
|
+ // Add admin scripts and styles
|
|
|
|
|
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
|
|
|
|
+
|
|
|
|
|
+ // Add product meta box to show mandatory products info
|
|
|
|
|
+ add_action('add_meta_boxes', array($this, 'add_product_meta_boxes'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Add fields to product category edit form
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param object $term The term object
|
|
|
|
|
+ */
|
|
|
|
|
+ public function add_category_fields($term) {
|
|
|
|
|
+ // Get current value
|
|
|
|
|
+ $mandatory_products = get_term_meta($term->term_id, 'mandatory_products', true);
|
|
|
|
|
+
|
|
|
|
|
+ // If not array, initialize it
|
|
|
|
|
+ if (!is_array($mandatory_products)) {
|
|
|
|
|
+ $mandatory_products = array();
|
|
|
|
|
+ }
|
|
|
|
|
+ ?>
|
|
|
|
|
+ <tr class="form-field">
|
|
|
|
|
+ <th scope="row" valign="top">
|
|
|
|
|
+ <label><?php _e('Mandatory Products', 'studiou-wc-product-cat-manage'); ?></label>
|
|
|
|
|
+ </th>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <div id="studiou_mandatory_products_container">
|
|
|
|
|
+ <?php if (!empty($mandatory_products)) : ?>
|
|
|
|
|
+ <?php foreach ($mandatory_products as $index => $mandatory_product) :
|
|
|
|
|
+ $product_id = isset($mandatory_product['product_id']) ? absint($mandatory_product['product_id']) : 0;
|
|
|
|
|
+ $quantity = isset($mandatory_product['quantity']) ? absint($mandatory_product['quantity']) : 1;
|
|
|
|
|
+ $type = isset($mandatory_product['type']) ? sanitize_text_field($mandatory_product['type']) : 'OnePerCart';
|
|
|
|
|
+ $product_title = '';
|
|
|
|
|
+
|
|
|
|
|
+ if ($product_id > 0) {
|
|
|
|
|
+ $product = wc_get_product($product_id);
|
|
|
|
|
+ if ($product) {
|
|
|
|
|
+ $product_title = $product->get_formatted_name();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ?>
|
|
|
|
|
+ <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Product:', 'studiou-wc-product-cat-manage'); ?></label>
|
|
|
|
|
+ <select
|
|
|
|
|
+ class="wc-product-search"
|
|
|
|
|
+ style="width: 50%;"
|
|
|
|
|
+ name="mandatory_product_id[]"
|
|
|
|
|
+ data-placeholder="<?php esc_attr_e('Search for a product…', 'studiou-wc-product-cat-manage'); ?>"
|
|
|
|
|
+ data-action="woocommerce_json_search_products_and_variations"
|
|
|
|
|
+ data-allow_clear="true"
|
|
|
|
|
+ >
|
|
|
|
|
+ <?php if ($product_id && $product_title) : ?>
|
|
|
|
|
+ <option value="<?php echo esc_attr($product_id); ?>" selected="selected"><?php echo esc_html($product_title); ?></option>
|
|
|
|
|
+ <?php endif; ?>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Quantity:', 'studiou-wc-product-cat-manage'); ?></label>
|
|
|
|
|
+ <input type="number" name="mandatory_product_quantity[]" value="<?php echo esc_attr($quantity); ?>" min="1" step="1" style="width: 80px;" />
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Type:', 'studiou-wc-product-cat-manage'); ?></label>
|
|
|
|
|
+ <select name="mandatory_product_type[]">
|
|
|
|
|
+ <option value="OnePerCart" <?php selected($type, 'OnePerCart'); ?>><?php _e('One Per Cart', 'studiou-wc-product-cat-manage'); ?></option>
|
|
|
|
|
+ <option value="OnePerProduct" <?php selected($type, 'OnePerProduct'); ?>><?php _e('One Per Product', 'studiou-wc-product-cat-manage'); ?></option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-product-cat-manage'); ?></button>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <?php endforeach; ?>
|
|
|
|
|
+ <?php endif; ?>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <button type="button" class="button add-mandatory-product"><?php _e('Add Mandatory Product', 'studiou-wc-mandatory-products'); ?></button>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p class="description">
|
|
|
|
|
+ <?php _e('These products will be automatically added to the cart when a product from this category is added.', 'studiou-wc-product-cat-manage'); ?>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <?php _e('Note: Child categories will inherit these settings.', 'studiou-wc-product-cat-manage'); ?>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+
|
|
|
|
|
+ <script type="text/template" id="tmpl-mandatory-product-row">
|
|
|
|
|
+ <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Product:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <select
|
|
|
|
|
+ class="wc-product-search"
|
|
|
|
|
+ style="width: 50%;"
|
|
|
|
|
+ name="mandatory_product_id[]"
|
|
|
|
|
+ data-placeholder="<?php esc_attr_e('Search for a product…', 'studiou-wc-mandatory-products'); ?>"
|
|
|
|
|
+ data-action="woocommerce_json_search_products_and_variations"
|
|
|
|
|
+ data-allow_clear="true"
|
|
|
|
|
+ ></select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Quantity:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <input type="number" name="mandatory_product_quantity[]" value="1" min="1" step="1" style="width: 80px;" />
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Type:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <select name="mandatory_product_type[]">
|
|
|
|
|
+ <option value="OnePerCart"><?php _e('One Per Cart', 'studiou-wc-mandatory-products'); ?></option>
|
|
|
|
|
+ <option value="OnePerProduct"><?php _e('One Per Product', 'studiou-wc-mandatory-products'); ?></option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-mandatory-products'); ?></button>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </script>
|
|
|
|
|
+
|
|
|
|
|
+ <script>
|
|
|
|
|
+ jQuery(function($) {
|
|
|
|
|
+ // Initialize select2
|
|
|
|
|
+ $(document).ready(function() {
|
|
|
|
|
+ $('.wc-product-search').selectWoo({
|
|
|
|
|
+ width: '100%'
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Add new row
|
|
|
|
|
+ $('.add-mandatory-product').on('click', function() {
|
|
|
|
|
+ var template = wp.template('mandatory-product-row');
|
|
|
|
|
+ $('#studiou_mandatory_products_container').append(template());
|
|
|
|
|
+
|
|
|
|
|
+ // Re-initialize select2 for the new row
|
|
|
|
|
+ $('#studiou_mandatory_products_container .wc-product-search:last').selectWoo({
|
|
|
|
|
+ width: '100%'
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Remove row
|
|
|
|
|
+ $(document).on('click', '.remove-mandatory-product', function() {
|
|
|
|
|
+ $(this).closest('.mandatory-product-row').remove();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ </script>
|
|
|
|
|
+ <?php
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Add fields to product category add form
|
|
|
|
|
+ */
|
|
|
|
|
+ public function add_category_fields_new() {
|
|
|
|
|
+ ?>
|
|
|
|
|
+ <div class="form-field">
|
|
|
|
|
+ <label><?php _e('Mandatory Products', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <div id="studiou_mandatory_products_container">
|
|
|
|
|
+ <!-- Empty container for new category -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <button type="button" class="button add-mandatory-product"><?php _e('Add Mandatory Product', 'studiou-wc-mandatory-products'); ?></button>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p class="description">
|
|
|
|
|
+ <?php _e('These products will be automatically added to the cart when a product from this category is added.', 'studiou-wc-mandatory-products'); ?>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <?php _e('Note: Child categories will inherit these settings.', 'studiou-wc-mandatory-products'); ?>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <script type="text/template" id="tmpl-mandatory-product-row">
|
|
|
|
|
+ <div class="mandatory-product-row" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9;">
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Product:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <select
|
|
|
|
|
+ class="wc-product-search"
|
|
|
|
|
+ style="width: 50%;"
|
|
|
|
|
+ name="mandatory_product_id[]"
|
|
|
|
|
+ data-placeholder="<?php esc_attr_e('Search for a product…', 'studiou-wc-mandatory-products'); ?>"
|
|
|
|
|
+ data-action="woocommerce_json_search_products_and_variations"
|
|
|
|
|
+ data-allow_clear="true"
|
|
|
|
|
+ ></select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Quantity:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <input type="number" name="mandatory_product_quantity[]" value="1" min="1" step="1" style="width: 80px;" />
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <label><?php _e('Type:', 'studiou-wc-mandatory-products'); ?></label>
|
|
|
|
|
+ <select name="mandatory_product_type[]">
|
|
|
|
|
+ <option value="OnePerCart"><?php _e('One Per Cart', 'studiou-wc-mandatory-products'); ?></option>
|
|
|
|
|
+ <option value="OnePerProduct"><?php _e('One Per Product', 'studiou-wc-mandatory-products'); ?></option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ <button type="button" class="button remove-mandatory-product"><?php _e('Remove', 'studiou-wc-mandatory-products'); ?></button>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </script>
|
|
|
|
|
+
|
|
|
|
|
+ <script>
|
|
|
|
|
+ jQuery(function($) {
|
|
|
|
|
+ // Add new row
|
|
|
|
|
+ $('.add-mandatory-product').on('click', function() {
|
|
|
|
|
+ var template = wp.template('mandatory-product-row');
|
|
|
|
|
+ $('#studiou_mandatory_products_container').append(template());
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize select2 for the new row
|
|
|
|
|
+ $('#studiou_mandatory_products_container .wc-product-search:last').selectWoo({
|
|
|
|
|
+ width: '100%'
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Remove row
|
|
|
|
|
+ $(document).on('click', '.remove-mandatory-product', function() {
|
|
|
|
|
+ $(this).closest('.mandatory-product-row').remove();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ </script>
|
|
|
|
|
+ <?php
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Save category fields
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param int $term_id The term ID
|
|
|
|
|
+ */
|
|
|
|
|
+ public function save_category_fields($term_id) {
|
|
|
|
|
+ $mandatory_products = array();
|
|
|
|
|
+
|
|
|
|
|
+ if (isset($_POST['mandatory_product_id']) && is_array($_POST['mandatory_product_id'])) {
|
|
|
|
|
+ $product_ids = array_map('absint', $_POST['mandatory_product_id']);
|
|
|
|
|
+ $quantities = isset($_POST['mandatory_product_quantity']) ? array_map('absint', $_POST['mandatory_product_quantity']) : array();
|
|
|
|
|
+ $types = isset($_POST['mandatory_product_type']) ? array_map('sanitize_text_field', $_POST['mandatory_product_type']) : array();
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($product_ids as $index => $product_id) {
|
|
|
|
|
+ // Skip if product ID is empty
|
|
|
|
|
+ if (empty($product_id)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $quantity = isset($quantities[$index]) ? max(1, $quantities[$index]) : 1;
|
|
|
|
|
+ $type = isset($types[$index]) ? $types[$index] : 'OnePerCart';
|
|
|
|
|
+
|
|
|
|
|
+ // Validate type
|
|
|
|
|
+ if (!in_array($type, array('OnePerCart', 'OnePerProduct'))) {
|
|
|
|
|
+ $type = 'OnePerCart';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $mandatory_products[] = array(
|
|
|
|
|
+ 'product_id' => $product_id,
|
|
|
|
|
+ 'quantity' => $quantity,
|
|
|
|
|
+ 'type' => $type
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ update_term_meta($term_id, 'mandatory_products', $mandatory_products);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Enqueue admin scripts and styles
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $hook Current admin page
|
|
|
|
|
+ */
|
|
|
|
|
+ public function enqueue_admin_scripts($hook) {
|
|
|
|
|
+ // Only on category edit page
|
|
|
|
|
+ if ('edit-tags.php' !== $hook && 'term.php' !== $hook) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Check if we're on the product category screen
|
|
|
|
|
+ $screen = get_current_screen();
|
|
|
|
|
+ if (!$screen || 'product_cat' !== $screen->taxonomy) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // WooCommerce admin scripts
|
|
|
|
|
+ wp_enqueue_script('wc-enhanced-select');
|
|
|
|
|
+ wp_enqueue_style('woocommerce_admin_styles');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Add product meta boxes
|
|
|
|
|
+ */
|
|
|
|
|
+ public function add_product_meta_boxes() {
|
|
|
|
|
+ add_meta_box(
|
|
|
|
|
+ 'studiou-wcmp-mandatory-products',
|
|
|
|
|
+ __('Mandatory Products Info', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ array($this, 'render_product_meta_box'),
|
|
|
|
|
+ 'product',
|
|
|
|
|
+ 'side',
|
|
|
|
|
+ 'default'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Render product meta box
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param WP_Post $post The post object
|
|
|
|
|
+ */
|
|
|
|
|
+ public function render_product_meta_box($post) {
|
|
|
|
|
+ $product_id = $post->ID;
|
|
|
|
|
+
|
|
|
|
|
+ // Get plugin instance
|
|
|
|
|
+ $plugin = new StudioU_WC_Mandatory_Products();
|
|
|
|
|
+
|
|
|
|
|
+ // Get mandatory products
|
|
|
|
|
+ $mandatory_products = $plugin->get_mandatory_products($product_id);
|
|
|
|
|
+
|
|
|
|
|
+ if (empty($mandatory_products)) {
|
|
|
|
|
+ echo '<p>' . __('No mandatory products set for this product\'s categories.', 'studiou-wc-mandatory-products') . '</p>';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ echo '<p>' . __('This product has mandatory products set in its categories:', 'studiou-wc-mandatory-products') . '</p>';
|
|
|
|
|
+ echo '<ul>';
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($mandatory_products as $mandatory_product) {
|
|
|
|
|
+ $product_id = absint($mandatory_product['product_id']);
|
|
|
|
|
+ $quantity = absint($mandatory_product['quantity']);
|
|
|
|
|
+ $type = sanitize_text_field($mandatory_product['type']);
|
|
|
|
|
+
|
|
|
|
|
+ $mandatory_product_obj = wc_get_product($product_id);
|
|
|
|
|
+
|
|
|
|
|
+ if ($mandatory_product_obj) {
|
|
|
|
|
+ echo '<li>';
|
|
|
|
|
+ echo sprintf(
|
|
|
|
|
+ '%s x %d (%s)',
|
|
|
|
|
+ $mandatory_product_obj->get_name(),
|
|
|
|
|
+ $quantity,
|
|
|
|
|
+ $type === 'OnePerCart' ? __('One per cart', 'studiou-wc-mandatory-products') : __('One per product', 'studiou-wc-mandatory-products')
|
|
|
|
|
+ );
|
|
|
|
|
+ echo '</li>';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ echo '</ul>';
|
|
|
|
|
+ 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>';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|