B1: Đăng ký thư viện trong admin WordPress
function tmdev_add_style_scripts() { $js_list_an_item_ver = date("ymd-Gis", filemtime( get_template_directory(). '/assets/js/list-an-item.js' )); $assets_path = get_template_directory_uri(); wp_enqueue_script('jquery-validation', $assets_path.'/assets/js/jquery.validate.min.js', array('jquery')); wp_enqueue_script('product-submit', $assets_path.'/assets/js/list-an-item.js', array('jquery', 'custom-script'), $js_list_an_item_ver ); wp_enqueue_style( 'product-submit', $assets_path.'/assets/css/product-submit.css', '', 123 ); } add_action( 'wp_enqueue_scripts', 'tmdev_add_style_scripts' );
B2: Tạo form và xử lý validate ở ngoài Frontend
<div id="product-submission-form"> <div class="step-buttons"> <button type="button" class="step-btn" data-step="1">STEP ONE</button> <button type="button" class="step-btn" data-step="2">STEP TWO</button> <button type="button" class="step-btn" data-step="3">STEP THREE</button> </div> <form id="productForm"> <div class="step step-1"> <div class="step-name"><p>STEP ONE:<br>Product Information</p></div> <div class="form-group"> <label for="productName">Product Name</label> <input type="text" id="productName" name="productName" required> </div> <div class="form-group"> <label for="productDescription">Product Description</label> <textarea id="productDescription" name="productDescription" required></textarea> </div> <button type="button" class="next-step">Next</button> </div> <div class="step step-2" style="display: none;"> <div class="step-name"><p>STEP TWO:<br>Pricing Information*</p></div> <div class="form-group"> <label for="productPrice">Price</label> <input type="number" id="productPrice" name="productPrice" required> </div> <div class="form-group"> <label for="productImage">Product Image</label> <input type="file" id="productImage" name="productImage" required> </div> <button type="button" class="previous-step">Previous</button> <button type="submit">Submit</button> </div> <div class="step step-3" style="display: none;"> <div class="step-name"><p>STEP THREE:<br>Preview & List*</p></div> Layout </div> </form> </div>
jQuery(document).ready(function($) { var currentStep = 1; var $form = $("#productForm"); $form.validate({ errorClass: "invalid", errorElement: "div", rules: { productName: "required", productDescription: "required", productPrice: { required: true, number: true }, productImage: "required" }, messages: { productName: "Please enter the product name", productDescription: "Please enter the product description", productPrice: "Please enter a valid price", productImage: "Please select an image" } }); function showStep(step) { $(".step").hide(); $(".step-" + step).show(); $(".step-btn").removeClass("active"); $('.step-btn[data-step="' + step + '"]').addClass("active"); } $(".next-step").click(function() { if ($form.valid()) { currentStep++; showStep(currentStep); } }); $(".previous-step").click(function() { currentStep--; showStep(currentStep); }); $(".step-btn").click(function() { var step = $(this).data("step"); if ($form.valid()) { currentStep = step; showStep(step); } }); $form.on("submit", function(event) { event.preventDefault(); if ($form.valid()) { var formData = new FormData($form[0]); // Create a FormData object $.ajax({ url: product_submission_params.ajaxurl, type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { if (response.success) { alert(response.data.message); } else { alert('An error occurred. Please try again.'); } } }); } }); showStep(currentStep); // Initialize form to show first step });
B3: Xử lý form và lưu vào quản trị WordPress
add_action('wp_ajax_product_submission', 'nt_handle_product_submission'); function nt_handle_product_submission() { // phpcs:disable WordPress.Security.NonceVerification.Missing if ( !isset( $_POST['nonce'] ) || !isset( $_POST['list_an_item'] ) ) { wp_send_json_error( 'missing_fields' ); wp_die(); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if( !wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'file_upload' ) ){ wp_send_json_error( 'bad_nonce' ); wp_die(); } // phpcs:disable WordPress.Security.NonceVerification.Missing if( $_POST['list_an_item'] != '1' ){ wp_send_json_error( 'bad_nonce' ); wp_die(); } $current_user = wp_get_current_user(); $user_id = $current_user->ID; $user_email = $current_user->user_email; $user_display_name = $current_user->display_name; // Validate and sanitize inputs $productName = sanitize_text_field($_POST['productName']); $productCategory = intval($_POST['productCategory']); $productColour = intval($_POST['productColour']); $productBrand = $_POST['productBrand'] != 'add-new-brand' ? intval($_POST['productBrand']) : $_POST['productBrand']; $productSize = intval($_POST['productSize']); if( $productBrand == 'add-new-brand' ){ $newBrand = sanitize_text_field($_POST['newBrand']); $term_check = term_exists( $newBrand, 'brand' ); // array is returned if taxonomy is given if( $term_check !== 0 && $term_check !== null ){ $productBrand = (int)$term_check['term_id']; }else{ $productBrandObj = wp_insert_term( $newBrand, 'brand' ); $productBrand = (int)$productBrandObj['term_id']; } } $componentsRentalPrice = [ 'dailyRate' => intval($_POST['dailyRate']), 'dryCleaning' => intval($_POST['dryCleaning']), 'delivery' => intval($_POST['delivery']), 'securityDeposit' => intval($_POST['securityDeposit']) ]; $productDetails = sanitize_textarea_field($_POST['productDetails']); $productSizeFit = sanitize_textarea_field($_POST['productSizeFit']); $productStyleNotes = sanitize_textarea_field($_POST['productStyleNotes']); $retailPrice = floatval($_POST['retailPrice']); $productPrice = floatval($_POST['productPrice']); $rentalOnly = true; if( isset( $_POST['sell-your-item'] ) ){ $rentalOnly = false; } // Create the WooCommerce product $product = new WC_Product_Simple(); $product->set_name($productName); $product->set_description($productDetails); // $product->set_sku( $sku ); $product->set_regular_price($retailPrice); if( $productPrice != 0 ){ $product->set_sale_price($productPrice); } // Set product category $product->set_category_ids(array($productCategory)); // Update product meta: product details, style note, size and fit $product->update_meta_data('product_details', $productDetails ); $product->update_meta_data('style_notes', $productSizeFit ); $product->update_meta_data('size_and_fit', $productStyleNotes ); $product->update_meta_data('list_an_item', 1 ); $product->update_meta_data('rental_price_components', $componentsRentalPrice ); // Update rental products pricing $rentalProductsPrice = sanitize_text_field( $_POST['rentalProductsPrice'] ); // Use a regular expression to get the values before the | sign $price_first = 0; $rentalPrice4Day = $rentalPrice8Day = $rentalPrice16Day = $rentalPrice30Day = 0; if( preg_match_all('/[^|]+(?=\|)/', $rentalProductsPrice, $matches) ){ $argsRentalProductsPrice = explode("|",$rentalProductsPrice); foreach( $argsRentalProductsPrice as $key => $item ){ $price_item = explode( ':', $item ); $day = (int)$price_item[0]; switch ($day) { case 4: $rentalPrice4Day = $price_item[1]; break; case 8: $rentalPrice8Day = $price_item[1]; break; case 16: $rentalPrice16Day = $price_item[1]; break; case 30: $rentalPrice30Day = $price_item[1]; break; default: break; } } $product->update_meta_data('_wcrp_rental_products_pricing_period_additional_selections', $rentalProductsPrice ); // allow purchase product if( !$rentalOnly ){ $product->update_meta_data('_wcrp_rental_products_rental', 'yes_purchase' ); }else{ // Rental only $product->update_meta_data('_wcrp_rental_products_rental', 'yes' ); } $product->update_meta_data('_wcrp_rental_products_pricing_type', 'period_selection' ); $product->update_meta_data('_wcrp_rental_products_pricing_period', 4 ); $product->update_meta_data('_wcrp_rental_products_rental_purchase_price', $rentalPrice4Day ); $product->update_meta_data('_wcrp_rental_products_rental_stock', 1 ); } // Save product $product_id = $product->save(); if ($product_id) { // Upload product images $gallery_images = []; if ( !empty($_FILES['productImage1']) || !empty($_FILES['productImage2']) || !empty($_FILES['productImage3']) || !empty($_FILES['productImage4']) ) { // These files need to be included as dependencies when on the front end. if ( !function_exists('media_handle_upload') ) { require_once(ABSPATH . "wp-admin" . '/includes/image.php'); require_once(ABSPATH . "wp-admin" . '/includes/file.php'); require_once(ABSPATH . "wp-admin" . '/includes/media.php'); } foreach ($_FILES as $name => $file ) { $attachment_id = media_handle_upload( $name, $product_id); if (is_wp_error($attachment_id)) { continue; } if ($name == 'productImage1' ) { set_post_thumbnail($product_id, $attachment_id); } else { $gallery_images[] = $attachment_id; } } if (!empty($gallery_images)) { update_post_meta($product_id, '_product_image_gallery', implode(',', $gallery_images)); } } // Set custom taxonomy: brand, colour, size wp_set_post_terms( $product_id, array($productBrand), 'brand' ); wp_set_post_terms( $product_id, array($productSize), 'size' ); wp_set_post_terms( $product_id, array($productColour), 'colour' ); //Set custom taxonomy: user_owner if($user_id != 0) { $term_id = get_user_meta($user_id,'user_owner_tag', true); if($term_id != false && !empty($term_id)) { wp_set_object_terms($product_id, (int)$term_id, 'user_owner',false); } } $productCategoryName = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'names' ) ); $productCategoryName = !empty( $productCategoryName ) ? implode( ",", $productCategoryName ) : ''; $productBrandName = wp_get_post_terms( $product_id, 'brand', array( 'fields' => 'names' ) ); $productBrandName = !empty( $productBrandName ) ? implode( ",", $productBrandName ) : ''; $productSizeName = wp_get_post_terms( $product_id, 'size', array( 'fields' => 'names' ) ); $productSizeName = !empty( $productSizeName ) ? implode( ",", $productSizeName ) : ''; $productColourName = wp_get_post_terms( $product_id, 'colour', array( 'fields' => 'names' ) ); $productColourName = !empty( $productColourName ) ? implode( ",", $productColourName ) : ''; $product_info = [ 'cic_logo' => get_site_url().'/wp-content/themes/circular-couture/assets/images/cic-logo.png', 'user_email' => $user_email, 'user_display_name' => $user_display_name, 'productId' => $product_id, 'productName' => $productName, 'productImage' => get_the_post_thumbnail_url( $product_id, 'full' ), 'productCategory' => $productCategoryName, 'productBrand' => $productBrandName, 'productSize' => $productSizeName, 'productColour' => $productColourName, 'productRRP' => $retailPrice, 'productPrice' => $productPrice, 'productPreview' => get_site_url(). '/list-an-item?product_id='.$product_id, 'rentalPrice4Day' => $rentalPrice4Day, 'rentalPrice8Day' => $rentalPrice8Day, 'rentalPrice16Day' => $rentalPrice16Day, 'rentalPrice30Day' => $rentalPrice30Day, 'message_for_admin' => 'A new product has just been published from the user: ' ]; // Send mail for: Admin ob_start(); wc_get_template("emails/admin-alert-product.php", $product_info ); $title = 'New product created by a user owner'; $body = ob_get_contents(); ob_end_clean(); $admin_email = get_option('admin_email'); wp_mail($admin_email, $title, $body, "Content-type: text/html; charset=utf-8"); // Send mail for: User ob_start(); wc_get_template("emails/user-owner-alert-product.php", $product_info ); $title = 'Product created successfully!'; $body = ob_get_contents(); ob_end_clean(); wp_mail($user_email, $title, $body, "Content-type: text/html; charset=utf-8"); // Respond with success message wp_send_json_success(array('message' => 'Product created successfully!', 'product_id' => $product_id)); }else{ wp_send_json_error(array('message' => 'Product submission failed.')); } }