admin管理员组

文章数量:1122832

I use the following code which immediately displays the pre-defined product attributes for a Dokan Auction Product with drop-down menus (instead of having the user manually click "Add Attribute"). I have also added two custom attribute sections, "Reference number" and "Watch year", where the user types in the values themselves.

// Enqueue Choices.js and its CSS
function enqueue_choices_js() {
    wp_enqueue_style('choices-css', '.js/public/assets/styles/choices.min.css', array(), '1.0.0');
    wp_enqueue_script('choices-js', '.js/public/assets/scripts/choices.min.js', array('jquery'), '1.0.0', true);
}
add_action('wp_enqueue_scripts', 'enqueue_choices_js');

// Main Function to Handle Dropdown with Choices.js, Ownership Verification, Reference Number, and Watch Year
function dokan_predefined_attributes_with_choices_and_custom_fields_js() {
    global $post;
    $product_id = isset($post) ? $post->ID : 0;

    // Fetch the existing values for Reference Number and Watch Year (if they exist)
    $existing_reference_number = $product_id ? get_post_meta($product_id, '_reference_number', true) : '';
    $existing_watch_year = $product_id ? get_post_meta($product_id, '_watch_year', true) : '';

    ?>
    <style>
        .dokan-attribute-obligatory {
            font-weight: bold;
            color: #d9534f;
        }
        .product-edit-new-container .dokan-attribute-variation-options .dokan-product-attribute-wrapper ul li.product-attribute-list .dokan-product-attribute-item {
            padding: 10px;
        }
        .choices[data-type*=select-one] .choices__button {
            margin-top: -20px;
            margin-right: 30px;
        }
        .dokan-attribute-missing {
            border: 1px solid #d9534f;
            padding: 10px;
            border-radius: 5px;
        }
        .ui-sortable-handle {
            touch-action: auto;
        }
        .dokan-btn-disabled {
            opacity: 0.5;
            pointer-events: none;
        }
        .ownership-confirmation {
            margin-bottom: 20px;
            padding: 10px;
            border: 2px solid #0a3d62;
            background-color: #f9f9f9;
            border-radius: 5px;
        }
        .ownership-confirmation h3 {
            margin-bottom: 10px;
            font-weight: bold;
            color: #0a3d62;
        }
        .choices__item--choice.is-highlighted {
            background-color: #0a3d62 !important;
            color: white !important;
        }
        /* Add margin between attributes */
        .dokan-dashboard .dokan-dashboard-content ul li {
            margin-top: 15px;
            margin-bottom: 15px;
        }
        /* Red asterisk for required fields */
        .required-asterisk {
            color: #d9534f !important;
            font-weight: 700 !important;
        }
    </style>

    <script type="text/javascript">
    jQuery(document).ready(function($) {
        // Track ownership verification status and product image status
        let ownershipPhotoUploaded = !!$('#ownership_photo_upload').val();
        let productImageUploaded = !!$('input.dokan-feat-image-id').val();

        // Predefined attributes list
        var predefined_attributes = [
            { slug: 'pa_brand', name: 'Brand' },
            { slug: 'pa_condition', name: 'Condition' },
            { slug: 'pa_box', name: 'Original box' },
            { slug: 'pa_papers', name: 'Original papers' },
            { slug: 'pa_case-size', name: 'Case size' },
            { slug: 'pa_material', name: 'Material' },
            { slug: 'pa_dial-color', name: 'Dial color' }
        ];

        // Function to load saved attribute terms
        function getSavedAttributeValue(slug) {
            return $('[name="attribute_values_' + slug + '"]').val() || '';
        }

        function getAttributeTerms(attributeSlug, callback, selectedValue = '') {
            $.ajax({
                url: '<?php echo admin_url('admin-ajax.php'); ?>',
                method: 'POST',
                dataType: 'json',
                data: {
                    action: 'get_wc_attribute_terms',
                    attribute_slug: attributeSlug
                },
                success: function(response) {
                    callback(response, selectedValue);
                },
                error: function() {
                    callback([], selectedValue);
                }
            });
        }

        function addPredefinedAttributesToList() {
            var $attributeList = $("ul.dokan-attribute-option-list");
            $attributeList.empty();

            $.each(predefined_attributes, function(index, attribute) {
                var savedValue = getSavedAttributeValue(attribute.slug);

                var attributeHtml = `
                    <li class="product-attribute-list taxonomy ${attribute.slug}" data-taxonomy="${attribute.slug}">
                        <div class="dokan-product-attribute-heading">
                            <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>${attribute.name}</strong><span class="required-asterisk"> *</span></span>
                        </div>

                        <div class="dokan-product-attribute-item dokan-clearfix">
                            <div class="content-half-part">
                                <label class="form-label" for="">Name</label>
                                <strong>${attribute.name}</strong>
                                <input type="hidden" name="attribute_names[]" value="${attribute.slug}">
                                <input type="hidden" name="attribute_position[]" class="attribute_position" value="0">
                                <input type="hidden" name="attribute_is_taxonomy[]" value="1">
                                <label class="checkbox-item form-label">
                                    <input type="checkbox" checked="checked" name="attribute_visibility[]" value="1"> Visible on the product page
                                </label>
                            </div>

                            <div class="content-half-part dokan-attribute-values">
                                <label for="" class="form-label">Value(s)</label>
                                <select style="width:100%;" class="dokan_attribute_values choices__input" name="attribute_values[${index}][]">
                                    <option value="">Loading...</option>
                                </select>
                            </div>
                        </div>
                    </li>
                `;

                $attributeList.append(attributeHtml);

                getAttributeTerms(attribute.slug, function(values, selectedValue) {
                    var optionsHtml = '<option value="">Select value</option>';
                    $.each(values, function(i, value) {
                        var selected = (value === selectedValue) ? 'selected' : '';
                        optionsHtml += '<option value="' + value + '" ' + selected + '>' + value + '</option>';
                    });

                    var $select = $(`select[name="attribute_values[${index}][]"]`);
                    $select.html(optionsHtml);

                    new Choices($select[0], {
                        searchEnabled: true,
                        removeItemButton: true,
                        itemSelectText: '',
                    });
                }, savedValue);
            });
        }

        function addCustomFields() {
            // Reference Number and Watch Year moved to the top and outlined if missing
            var referenceNumberField = `
                <li class="product-attribute-list taxonomy reference-number" data-taxonomy="reference-number">
                    <div class="dokan-product-attribute-heading">
                        <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Reference Number</strong><span class="required-asterisk"> *</span></span>
                    </div>
                    <div class="dokan-product-attribute-item dokan-clearfix">
                        <div class="content-half-part">
                            <label class="form-label" for="reference_number">Reference Number</label>
                            <input type="text" id="reference_number" name="reference_number" class="dokan-form-control" placeholder="Enter reference number" value="<?php echo esc_attr($existing_reference_number); ?>" required>
                        </div>
                    </div>
                </li>
            `;

            var watchYearField = `
                <li class="product-attribute-list taxonomy watch-year" data-taxonomy="watch-year">
                    <div class="dokan-product-attribute-heading">
                        <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Watch Year</strong><span class="required-asterisk"> *</span></span>
                    </div>
                    <div class="dokan-product-attribute-item dokan-clearfix">
                        <div class="content-half-part">
                            <label class="form-label" for="watch_year">Watch Year</label>
                            <input type="text" id="watch_year" name="watch_year" class="dokan-form-control" placeholder="Enter watch year" value="<?php echo esc_attr($existing_watch_year); ?>" required>
                        </div>
                    </div>
                </li>
            `;

            // Prepend custom fields (Reference Number and Watch Year) to the top of the attribute list
            $("ul.dokan-attribute-option-list").prepend(watchYearField);
            $("ul.dokan-attribute-option-list").prepend(referenceNumberField);

            // Conditionally add Ownership Verification based on product status
            if ($(".dokan-product-status-label:contains('Pending Review'), .dokan-product-status-label:contains('Online'), .dokan-product-status-label:contains('Draft')").length === 0) {
                var ownershipField = `
                    <div class="ownership-confirmation">
                        <h3>Ownership Verification <span class="required-asterisk">*</span></h3>
                        <p>To confirm ownership, please upload a photo of your watch displaying the time <strong id="generated-time"></strong>.</p>
                        <div class="dokan-form-group">
                            <label class="dokan-control-label" for="ownership_photo_upload">Upload Photo</label>
                            <input type="file" name="ownership_photo_upload" id="ownership_photo_upload" accept="image/*" required>
                        </div>
                    </div>
                `;
                $('.dokan-attribute-variation-options').prepend('<div class="dokan-custom-fields">' + ownershipField + '</div>');

                function generateRandomTime() {
                    var hours = Math.floor(Math.random() * 24);
                    var minutes = Math.floor(Math.random() * 60);
                    return ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2);
                }

                var randomTime = generateRandomTime();
                $('#generated-time').text(randomTime);
            }
        }

        addPredefinedAttributesToList();
        addCustomFields();

        function checkRequiredFields() {
            var attributesFilled = checkAttributes();
            var imageUploaded = checkFeaturedImage();
            var customFieldsValid = checkCustomFields();

            var $submitButton = $('input[name="update_auction_product"]');
            if (attributesFilled && imageUploaded && customFieldsValid) {
                $submitButton.prop('disabled', false).removeClass('dokan-btn-disabled');
            } else {
                $submitButton.prop('disabled', true).addClass('dokan-btn-disabled');
            }
        }

        function checkAttributes() {
            var missingValue = false;
            $('ul.dokan-attribute-option-list select.dokan_attribute_values').each(function() {
                if ($(this).val() === "" || $(this).val().length === 0) {
                    $(this).closest('.product-attribute-list').addClass('dokan-attribute-missing');
                    missingValue = true;
                } else {
                    $(this).closest('.product-attribute-list').removeClass('dokan-attribute-missing');
                }
            });

            // Check if Reference Number or Watch Year is missing
            if ($('#reference_number').val() === "") {
                $('#reference_number').closest('.product-attribute-list').addClass('dokan-attribute-missing');
                missingValue = true;
            } else {
                $('#reference_number').closest('.product-attribute-list').removeClass('dokan-attribute-missing');
            }

            if ($('#watch_year').val() === "") {
                $('#watch_year').closest('.product-attribute-list').addClass('dokan-attribute-missing');
                missingValue = true;
            } else {
                $('#watch_year').closest('.product-attribute-list').removeClass('dokan-attribute-missing');
            }

            return !missingValue;
        }

        function checkFeaturedImage() {
            return !!$('input.dokan-feat-image-id').val() && $('input.dokan-feat-image-id').val() !== "0";
        }

        function checkCustomFields() {
            var referenceNumber = $('#reference_number').val();
            var watchYear = $('#watch_year').val();
            // Only check ownership verification if it's visible
            var ownershipValid = $(".ownership-confirmation").length === 0 || !!$('#ownership_photo_upload').val();
            return referenceNumber && watchYear && ownershipValid;
        }

        $(document).on('change', 'input.dokan-feat-image-id', function() {
            productImageUploaded = !!$('input.dokan-feat-image-id').val();
            checkRequiredFields();
        });

        $(document).on('click', '.dokan-remove-feat-image', function() {
            setTimeout(function() {
                productImageUploaded = false;
                $('input.dokan-feat-image-id').val("0");
                checkRequiredFields();
            }, 100);
        });

        $(document).on('change', 'select.dokan_attribute_values, #ownership_photo_upload', function() {
            checkRequiredFields();
        });

        checkRequiredFields();
    });
    </script>
    <?php
}
add_action('wp_footer', 'dokan_predefined_attributes_with_choices_and_custom_fields_js');

// AJAX handler to get WooCommerce terms for an attribute
function get_wc_attribute_terms() {
    $attribute_slug = isset($_POST['attribute_slug']) ? sanitize_text_field($_POST['attribute_slug']) : '';
    $taxonomy = $attribute_slug;
    $terms = get_terms(array(
        'taxonomy' => $taxonomy,
        'hide_empty' => false,
    ));

    $term_names = array();
    if (!is_wp_error($terms)) {
        foreach ($terms as $term) {
            $term_names[] = $term->name;
        }
    }

    wp_send_json($term_names);
}
add_action('wp_ajax_get_wc_attribute_terms', 'get_wc_attribute_terms');
add_action('wp_ajax_nopriv_get_wc_attribute_terms', 'get_wc_attribute_terms');

// Hook to save custom fields (Reference Number and Watch Year) as WooCommerce product attributes
function save_reference_and_year_attributes($product_id) {
    if (isset($_POST['reference_number'])) {
        $reference_number = sanitize_text_field($_POST['reference_number']);
        wp_set_object_terms($product_id, $reference_number, 'pa_reference-number');
    }

    if (isset($_POST['watch_year'])) {
        $watch_year = sanitize_text_field($_POST['watch_year']);
        wp_set_object_terms($product_id, $watch_year, 'pa_watch-year');
    }
}
add_action('dokan_process_product_meta', 'save_reference_and_year_attributes', 10, 2);


I have two problems:

  1. When the user clicks "Save Product", all attribute values are successfully saved for the product except for the Reference number and Watch year which is typed in with text.

  2. The attribute values do not load when I edit an uploaded product (with status "Pending Review", "Draft" or "Online"). Instead, the user would have to re-select all values if they wish to make an edit and re-save the product.

Could someone please help me?

Kind regards,

Martin

本文标签: Custom Auction Product Attribute Section Dokan amp Woocommerce