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.'));
}
}