- '', - 'name' => '', - ]; - ob_start(); - require DOKAN_INC_DIR . '/woo-views/html-product-download.php'; - $row_html = ob_get_clean(); - ?> - - - - | -||
---|---|---|
[?] | -[?] | -- |
diff --git a/.gitignore b/.gitignore index fbdaef1b15..ec3818bb0b 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ assets/js/style.js assets/js/vue-admin.js.LICENSE.txt assets/js/vue-bootstrap.js.LICENSE.txt assets/js/vue-frontend.js.LICENSE.txt +assets/js/dokan-tailwind.js diff --git a/assets/src/js/product-editor.js b/assets/src/js/product-editor.js index 9c2a5886f9..d60aa411bf 100755 --- a/assets/src/js/product-editor.js +++ b/assets/src/js/product-editor.js @@ -6,6 +6,15 @@ var $image_gallery_ids = $('#product_image_gallery'); var $product_images = $('#product_images_container ul.product_images'); + var validatorError = function( error, element ) { + var form_group = $( element ).closest( '.dokan-form-group' ); + form_group.addClass( 'has-error' ).append( error ); + }; + + var validatorSuccess = function( label, element ) { + $( element ).closest( '.dokan-form-group' ).removeClass( 'has-error' ); + }; + var Dokan_Editor = { modal: false, @@ -13,6 +22,8 @@ * Constructor function */ init: function() { + var self = this; + this.productFormValidation(self); product_type = 'simple'; @@ -49,27 +60,9 @@ return false; }); - $( 'body' ).on( 'submit', 'form.dokan-product-edit-form', this.inputValidate ); - - // For new desing in product page - $( '.dokan-product-listing' ).on( 'click', 'a.dokan-add-new-product', this.addProductPopup ); - this.loadSelect2(); this.bindProductTagDropdown(); - this.attribute.sortable(); this.checkProductPostboxToggle(); - $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.dokan-product-toggle-attribute, .dokan-product-attribute-heading', this.attribute.toggleAttribute ); - $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.add_new_attribute', this.attribute.addNewAttribute ); - $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'keyup', 'input.dokan-product-attribute-name', this.attribute.dynamicAttrNameChange ); - $( '.dokan-product-attribute-wrapper ul.dokan-attribute-option-list' ).on( 'click', 'button.dokan-select-all-attributes', this.attribute.selectAllAttr ); - $( '.dokan-product-attribute-wrapper ul.dokan-attribute-option-list' ).on( 'click', 'button.dokan-select-no-attributes', this.attribute.selectNoneAttr ); - $( '.dokan-product-attribute-wrapper ul.dokan-attribute-option-list' ).on( 'click', 'button.dokan-add-new-attribute', this.attribute.addNewExtraAttr ); - $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.dokan-product-remove-attribute', this.attribute.removeAttribute ); - $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.dokan-save-attribute', this.attribute.saveAttribute ); - $( 'body' ).on( 'click', '.product-container-footer input[type="submit"]', this.createNewProduct ); - - this.attribute.disbalePredefinedAttribute(); - this.setCorrectProductId(); $( 'body' ).trigger( 'dokan-product-editor-loaded', this ); @@ -87,6 +80,38 @@ Dokan_Editor.dokan_show_earning_suggestion( Dokan_Editor.earning_suggestion_callbak ); } ); } + + this.renderCustomSectionBasedOnCategory(); + + wp.hooks.addAction( + 'dokan_selected_multistep_category', + 'render_custom_section_based_on_chosen_category', + Dokan_Editor.renderCustomSectionBasedOnCategory + ); + }, + + productFormValidation: function( self ) { + window.dokanProductformValidator = $( "form.dokan-product-edit-form" ).validate( { + errorElement: 'span', + errorClass: 'error validation-error', + errorPlacement: validatorError, + success: validatorSuccess, + } ); + }, + + renderCustomSectionBasedOnCategory: function( categoryId ) { + let productCatSelectorVal = $( '.dokan_chosen_product_cat' ).val(); + let productCustomSections = $( '.dokan-product-custom-section' ); + + productCustomSections.each( function() { + let sectionCatId = $( this ).data( 'category-id' ); + + if ( sectionCatId == productCatSelectorVal || 0 == sectionCatId ) { + $( this ).removeClass( 'dokan-hide' ); + } else { + $( this ).addClass( 'dokan-hide' ); + } + } ); }, setCorrectProductId : function () { @@ -314,390 +339,6 @@ }); }, - addProductPopup: function (e) { - e.preventDefault(); - Dokan_Editor.openProductPopup(); - }, - - openProductPopup: function() { - const productTemplate = wp.template( 'dokan-add-new-product' ), - modalElem = $( '#dokan-add-product-popup' ); - Dokan_Editor.modal = modalElem.iziModal( { - headerColor : dokan.modal_header_color, - overlayColor: 'rgba(0, 0, 0, 0.8)', - width : 690, - top : 32, - onOpening : () => { - Dokan_Editor.reRenderPopupElements(); - }, - onClosed: () => { - product_gallery_frame = undefined; - product_featured_frame = undefined; - $( '#dokan-add-new-product-popup input[name="_sale_price_dates_from"], #dokan-add-new-product-popup input[name="_sale_price_dates_to"]' ).datepicker( 'destroy' ); - }, - } ); - Dokan_Editor.modal.iziModal( 'setContent', productTemplate().trim() ); - Dokan_Editor.modal.iziModal( 'open' ); - }, - - reRenderPopupElements: function() { - Dokan_Editor.loadSelect2(); - Dokan_Editor.bindProductTagDropdown(); - - $( '#dokan-add-new-product-popup .sale_price_dates_fields input' ).daterangepicker({ - singleDatePicker: true, - showDropdowns: false, - autoApply: true, - parentEl: '#dokan-add-new-product-popup', - opens: 'left', - autoUpdateInput : false, - } ).on( 'apply.daterangepicker', function( ev, picker ) { - $( this ).val( picker.startDate.format( 'YYYY-MM-DD' ) ); - } ); - - $( '.tips' ).tooltip(); - - Dokan_Editor.gallery.sortable(); - $( 'body' ).trigger( 'dokan-product-editor-popup-opened', Dokan_Editor ); - }, - - createNewProduct: function (e) { - e.preventDefault(); - - var self = $(this), - form = self.closest('form#dokan-add-new-product-form'), - btn_id = self.attr('data-btn_id'); - - form.find( 'span.dokan-show-add-product-success' ).html(''); - form.find( 'span.dokan-show-add-product-error' ).html(''); - form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'inline-block' ); - - self.attr( 'disabled', 'disabled' ); - - if ( form.find( 'input[name="post_title"]' ).val() == '' ) { - $( 'span.dokan-show-add-product-error' ).html( dokan.product_title_required ); - self.removeAttr( 'disabled' ); - form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' ); - return; - } - - if ( form.find( 'select[name="product_cat"]' ).val() == '-1' ) { - $( 'span.dokan-show-add-product-error' ).html( dokan.product_category_required ); - self.removeAttr( 'disabled' ); - form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' ); - return; - } - - var data = { - action: 'dokan_create_new_product', - postdata: form.serialize(), - _wpnonce : dokan.nonce - }; - - Dokan_Editor.modal.iziModal('startLoading'); - $.post( dokan.ajaxurl, data, function( resp ) { - if ( resp.success ) { - self.removeAttr( 'disabled' ); - if ( btn_id === 'create_new' ) { - $( '#dokan-add-product-popup' ).iziModal('close'); - window.location.href = resp.data; - } else { - product_featured_frame = undefined; - $('.dokan-dashboard-product-listing-wrapper').load( window.location.href + ' table.product-listing-table' ); - Dokan_Editor.modal.iziModal('resetContent'); - Dokan_Editor.openProductPopup(); - Dokan_Editor.reRenderPopupElements(); - $( 'span.dokan-show-add-product-success' ).html( dokan.product_created_response ); - - setTimeout(function() { - $( 'span.dokan-show-add-product-success' ).html( '' ); - }, 3000); - } - } else { - self.removeAttr( 'disabled' ); - $( 'span.dokan-show-add-product-error' ).html( resp.data ); - } - form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' ); - }) - .always( function () { - Dokan_Editor.modal.iziModal('stopLoading'); - }); - }, - - attribute: { - - toggleAttribute: function(e) { - e.preventDefault(); - - var self = $(this), - list = self.closest('li'), - item = list.find('.dokan-product-attribute-item'); - - if ( $(item).hasClass('dokan-hide') ) { - self.closest('.dokan-product-attribute-heading').css({ borderBottom: '1px solid #e3e3e3' }); - $(item).slideDown( 200, function() { - self.find('i.fa').removeClass('fa-flip-horizointal').addClass('fa-flip-vertical'); - $(this).removeClass('dokan-hide'); - if ( ! $(e.target).hasClass( 'dokan-product-attribute-heading' ) ) { - $(e.target).closest('a').css('top', '12px'); - } else if ( $(e.target).hasClass( 'dokan-product-attribute-heading' ) ) { - self.find( 'a.dokan-product-toggle-attribute' ).css('top', '12px'); - } - }); - } else { - $(item).slideUp( 200, function() { - $(this).addClass('dokan-hide'); - self.find('i.fa').removeClass('fa-flip-vertical').addClass('fa-flip-horizointal'); - if ( ! $(e.target).hasClass('dokan-product-attribute-heading') ) { - $(e.target).closest('a').css('top', '7px'); - } else if ( $(e.target).hasClass( 'dokan-product-attribute-heading' ) ) { - self.find('a.dokan-product-toggle-attribute').css('top', '7px'); - } - self.closest('.dokan-product-attribute-heading').css({ borderBottom: 'none' }); - - }) - } - return false; - }, - - sortable: function() { - $('.dokan-product-attribute-wrapper ul').sortable({ - items: 'li.product-attribute-list', - cursor: 'move', - scrollSensitivity:40, - forcePlaceholderSize: true, - forceHelperSize: false, - helper: 'clone', - opacity: 0.65, - placeholder: 'dokan-sortable-placeholder', - start:function(event,ui){ - ui.item.css('background-color','#f6f6f6'); - }, - stop:function(event,ui){ - ui.item.removeAttr('style'); - }, - update: function(event, ui) { - var attachment_ids = ''; - Dokan_Editor.attribute.reArrangeAttribute(); - } - }); - }, - - dynamicAttrNameChange: function(e) { - e.preventDefault(); - var self = $(this), - value = self.val(); - - if ( value == '' ) { - self.closest( 'li' ).find( 'strong' ).html( dokan.i18n_attribute_label ); - } else { - self.closest( 'li' ).find( 'strong' ).html( value ); - } - }, - - selectAllAttr: function(e) { - e.preventDefault(); - $( this ).closest( 'li.product-attribute-list' ).find( 'select.dokan_attribute_values option' ).attr( 'selected', true ); - $( this ).closest( 'li.product-attribute-list' ).find( 'select.dokan_attribute_values' ).trigger( 'change' ); - return false; - }, - - selectNoneAttr: function(e) { - e.preventDefault(); - $( this ).closest( 'li.product-attribute-list' ).find( 'select.dokan_attribute_values option' ).attr( 'selected', false ); - $( this ).closest( 'li.product-attribute-list' ).find( 'select.dokan_attribute_values' ).trigger( 'change' ); - return false; - }, - - reArrangeAttribute: function() { - var attributeWrapper = $('.dokan-product-attribute-wrapper').find('ul.dokan-attribute-option-list'); - attributeWrapper.find( 'li.product-attribute-list' ).css( 'cursor', 'default' ).each(function( i ) { - $(this).find('.attribute_position').val(i); - }); - }, - - addNewExtraAttr: async function(e) { - e.preventDefault(); - - var $wrapper = $(this).closest( 'li.product-attribute-list' ); - var attribute = $wrapper.data( 'taxonomy' ); - let result = await dokan_sweetalert( dokan.new_attribute_prompt, { - action : 'prompt', - input :'text' - } ); - var new_attribute_name = result.value; - - if ( new_attribute_name ) { - - var data = { - action: 'dokan_add_new_attribute', - taxonomy: attribute, - term: new_attribute_name, - _wpnonce : dokan.nonce - }; - - $.post( dokan.ajaxurl, data, function( response ) { - if ( response.error ) { - dokan_sweetalert( response.error, { - action : 'alert', - icon : 'warning' - } ); - } else if ( response.slug ) { - $wrapper.find( 'select.dokan_attribute_values' ).append( '' ); - $wrapper.find( 'select.dokan_attribute_values' ).trigger( 'change' ); - } - - }); - } - }, - - addNewAttribute: function(e) { - e.preventDefault(); - - var self = $(this), - attrWrap = self.closest('.dokan-attribute-type').find('select#predefined_attribute'), - attribute = attrWrap.val(), - size = $( 'ul.dokan-attribute-option-list .product-attribute-list' ).length; - - - var data = { - action : 'dokan_get_pre_attribute', - taxonomy : attribute, - i : size, - _wpnonce : dokan.nonce - }; - - self.closest('.dokan-attribute-type').find('span.dokan-attribute-spinner').removeClass('dokan-hide'); - - $.post( dokan.ajaxurl, data, function( resp ) { - if ( resp.success ) { - var attributeWrapper = $('.dokan-product-attribute-wrapper').find('ul.dokan-attribute-option-list'); - $html = $.parseHTML(resp.data); - $($html).find('.dokan-product-attribute-item').removeClass('dokan-hide'); - $($html).find('i.fa.fa-sort-desc').removeClass('fa-flip-horizointal').addClass('fa-flip-vertical'); - $($html).find('a.dokan-product-toggle-attribute').css('top','12px'); - $($html).find('.dokan-product-attribute-heading').css({ borderBottom: '1px solid #e3e3e3' }); - - attributeWrapper.append( $html ); - Dokan_Editor.loadSelect2(); - Dokan_Editor.bindProductTagDropdown(); - Dokan_Editor.attribute.reArrangeAttribute(); - - if ( "variable" !== $( 'select#product_type' ).val() ) { - let labels = $( 'div.dokan-product-attribute-wrapper label.show_if_variable' ); - - for( let label of labels ) { - let checkBox = $( label ).find('input[type="checkbox"]'); - - if ( checkBox.length > 0 && checkBox[0].getAttribute('name')?.startsWith('attribute_variation[') ) { - $( label ).hide(); - } - } - } - } - - self.closest('.dokan-attribute-type').find('span.dokan-attribute-spinner').addClass('dokan-hide'); - - if ( attribute ) { - attrWrap.find( 'option[value="' + attribute + '"]' ).attr( 'disabled','disabled' ); - attrWrap.val( '' ); - } - }) - .done(function() { - $( 'select#product_type' ).trigger('change'); - }); - }, - - removeAttribute: async function(evt) { - evt.stopPropagation(); - evt.preventDefault(); - - const isRemoved = await dokan_sweetalert( dokan.remove_attribute, { - action :'confirm', - icon :'warning' - } ); - - if ( 'undefined' !== isRemoved && isRemoved.isConfirmed ) { - var $parent = $( this ).closest('li.product-attribute-list'); - - $parent.fadeOut( 300, function() { - if ( $parent.is( '.taxonomy' ) ) { - $parent.find( 'select, input[type=text]' ).val( '' ); - $( 'select.dokan_attribute_taxonomy' ).find( 'option[value="' + $parent.data( 'taxonomy' ) + '"]' ).removeAttr( 'disabled' ); - } else { - $parent.find( 'select, input[type=text]' ).val( '' ); - $parent.hide(); - } - - Dokan_Editor.attribute.reArrangeAttribute(); - }); - } - - return false; - }, - - saveAttribute: function(e) { - e.preventDefault(); - - var self = $(this), - data = { - post_id: $('#dokan-edit-product-id').val(), - data: $( 'ul.dokan-attribute-option-list' ).find( 'input, select, textarea' ).serialize(), - action: 'dokan_save_attributes' - }; - - $( '.dokan-product-attribute-wrapper' ).block({ - message: null, - fadeIn: 50, - fadeOut: 1000, - overlayCSS: { - background: '#fff', - opacity: 0.6 - } - }); - - $.post( dokan.ajaxurl, data, function( resp ) { - // Load variations panel. - $( '#dokan-variable-product-options' ).load( window.location.toString() + ' #dokan-variable-product-options-inner', function() { - $( '#dokan-variable-product-options' ).trigger( 'reload' ); - $( 'select#product_type' ).trigger('change'); - $( '.dokan-product-attribute-wrapper' ).unblock(); - }); - }); - - }, - - disbalePredefinedAttribute: function() { - $( 'ul.dokan-attribute-option-list li.product-attribute-list' ).each( function( index, el ) { - if ( $( el ).css( 'display' ) !== 'none' && $( el ).is( '.taxonomy' ) ) { - $( 'select#predefined_attribute' ).find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' ).attr( 'disabled', 'disabled' ); - } - }); - } - }, - - inputValidate: function( e ) { - e.preventDefault(); - - if ( $( '#post_title' ).val().trim() == '' ) { - $( '#post_title' ).focus(); - $( 'div.dokan-product-title-alert' ).removeClass('dokan-hide'); - return; - }else{ - $( 'div.dokan-product-title-alert' ).hide(); - } - - if ( $( 'select.product_cat' ).val() == -1 ) { - $( 'select.product_cat' ).focus(); - $( 'div.dokan-product-cat-alert' ).removeClass('dokan-hide'); - return; - }else{ - $( 'div.dokan-product-cat-alert' ).hide(); - } - $( 'input[type=submit]' ).attr( 'disabled', 'disabled' ); - this.submit(); - }, - downloadable: function() { if ( $(this).prop('checked') ) { $(this).closest('aside').find('.dokan-side-body').removeClass('dokan-hide'); @@ -1097,6 +738,8 @@ $wrap.find( '.cancel_sale_schedule' ).show(); $wrap.find( '.sale_price_dates_fields' ).show(); + $( '#is_discount_schedule_enabled' ).val( 1 ); + return false; }); @@ -1108,6 +751,8 @@ $wrap.find( '.sale_price_dates_fields' ).hide(); $wrap.find( '.sale_price_dates_fields' ).find( 'input' ).val(''); + $( '#is_discount_schedule_enabled' ).val( 0 ); + return false; }); diff --git a/assets/src/less/dashboard.less b/assets/src/less/dashboard.less index 102c4d67ff..6746356e90 100644 --- a/assets/src/less/dashboard.less +++ b/assets/src/less/dashboard.less @@ -503,6 +503,14 @@ div.media-sidebar a.edit-attachment{ margin-bottom: 10px; } } + + .product-edit-container { + span { + &.validation-error { + display: block; + } + } + } } div.chart-container > div.chart-placeholder > div.legend { diff --git a/assets/src/less/products.less b/assets/src/less/products.less index 21c945959b..aa7ce9555c 100644 --- a/assets/src/less/products.less +++ b/assets/src/less/products.less @@ -281,6 +281,10 @@ } .product-edit-new-container { + .dokan-hide { + display: none; + } + .dokan-edit-row { &.dokan-other-options { margin-bottom: 20px; @@ -465,14 +469,36 @@ .dokan-shipping-container { .dokan-shipping-dimention-options { - margin-bottom: 20px; - input { + margin-bottom: 5px; + + .dokan-form-group { width: 23.3%; float: left; margin-right: 2%; + dokan-form-group-height { + margin-right: 0; + } + + input { + &.dokan-form-control { + width: 100%; + margin-right: 0; + margin-bottom: 0; + } + } + } + + input { + &.dokan-form-control { + float: left; + width: 23.3%; + margin-right: 2%; + margin-bottom: 15px; + } + _height { - margin-right: 0px; + margin-right: 0; } } } @@ -972,7 +998,7 @@ .instruction-inside { text-align: center; - padding-top: 40px; + padding-top: 80px; color: #aaa; } diff --git a/dokan-class.php b/dokan-class.php index 167241bd02..bde632fee9 100755 --- a/dokan-class.php +++ b/dokan-class.php @@ -14,6 +14,7 @@ * @property WeDevs\Dokan\BackgroundProcess\Manager $bg_process Instance of WeDevs\Dokan\BackgroundProcess\Manager class * @property WeDevs\Dokan\Withdraw\Manager $withdraw Instance of WeDevs\Dokan\Withdraw\Manager class * @property WeDevs\Dokan\Frontend\Frontend $frontend_manager Instance of \WeDevs\Dokan\Frontend\Frontend class + * @property WeDevs\Dokan\Dashboard\Manager $dashboard Instance of \WeDevs\Dokan\Dashboard\Manager class * @property WeDevs\Dokan\Registration $registration Instance of WeDevs\Dokan\Registration class */ final class WeDevs_Dokan { @@ -214,6 +215,7 @@ public function define_constants() { defined( 'DOKAN_PLUGIN_VERSION' ) || define( 'DOKAN_PLUGIN_VERSION', $this->version ); defined( 'DOKAN_DIR' ) || define( 'DOKAN_DIR', __DIR__ ); defined( 'DOKAN_INC_DIR' ) || define( 'DOKAN_INC_DIR', __DIR__ . '/includes' ); + defined( 'DOKAN_TEMPLATE_DIR' ) || define( 'DOKAN_TEMPLATE_DIR', __DIR__ . '/templates' ); defined( 'DOKAN_LIB_DIR' ) || define( 'DOKAN_LIB_DIR', __DIR__ . '/lib' ); defined( 'DOKAN_PLUGIN_ASSEST' ) || define( 'DOKAN_PLUGIN_ASSEST', plugins_url( 'assets', DOKAN_FILE ) ); diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php index 5ddaef5b95..2d37a2cb8d 100644 --- a/includes/Admin/Settings.php +++ b/includes/Admin/Settings.php @@ -634,25 +634,6 @@ public function get_settings_fields() { 'default' => 'on', 'tooltip' => __( 'If checked, vendors will have permission to sell immediately after registration. If unchecked, newly registered vendors cannot add products until selling capability is activated manually from admin dashboard.', 'dokan-lite' ), ], - 'one_step_product_create' => [ - 'name' => 'one_step_product_create', - 'label' => __( 'One Page Product Creation', 'dokan-lite' ), - 'desc' => __( 'Add new product in single page view', 'dokan-lite' ), - 'type' => 'switcher', - 'default' => 'on', - 'tooltip' => __( 'If disabled, instead of a single add product page it will open a pop up window or vendor will redirect to product page when adding new product.', 'dokan-lite' ), - ], - 'disable_product_popup' => [ - 'name' => 'disable_product_popup', - 'label' => __( 'Disable Product Popup', 'dokan-lite' ), - 'desc' => __( 'Disable add new product in popup view', 'dokan-lite' ), - 'type' => 'switcher', - 'default' => 'off', - 'show_if' => [ - 'dokan_selling.one_step_product_create' => [ 'equal' => 'off' ], - ], - 'tooltip' => __( 'If disabled, instead of a pop up window vendor will redirect to product page when adding new product.', 'dokan-lite' ), - ], 'order_status_change' => [ 'name' => 'order_status_change', 'label' => __( 'Order Status Change', 'dokan-lite' ), diff --git a/includes/Ajax.php b/includes/Ajax.php index b7f93a7c52..502828cf22 100755 --- a/includes/Ajax.php +++ b/includes/Ajax.php @@ -41,8 +41,6 @@ public function __construct() { add_action( 'wp_ajax_dokan_seller_listing_search', [ $this, 'seller_listing_search' ] ); add_action( 'wp_ajax_nopriv_dokan_seller_listing_search', [ $this, 'seller_listing_search' ] ); - add_action( 'wp_ajax_dokan_create_new_product', [ $this, 'create_product' ] ); - add_action( 'wp_ajax_custom-header-crop', [ $this, 'crop_store_banner' ] ); add_action( 'wp_ajax_dokan_json_search_products_tags', [ $this, 'dokan_json_search_products_tags' ] ); @@ -57,43 +55,6 @@ public function __construct() { add_action( 'wp_ajax_dokan-upgrade-dissmiss', [ $this, 'dismiss_pro_notice' ] ); } - /** - * Create product from popup submission - * - * @since 2.5.0 - * - * @return void - */ - public function create_product() { - check_ajax_referer( 'dokan_reviews' ); - - if ( ! current_user_can( 'dokan_add_product' ) ) { - wp_send_json_error( __( 'You have no permission to do this action', 'dokan-lite' ) ); - } - - $submited_data = isset( $_POST['postdata'] ) ? wp_unslash( $_POST['postdata'] ) : ''; //phpcs:ignore - - parse_str( $submited_data, $postdata ); - - $response = dokan_save_product( $postdata ); - - if ( is_wp_error( $response ) ) { - wp_send_json_error( $response->get_error_message() ); - } - - if ( is_int( $response ) ) { - if ( current_user_can( 'dokan_edit_product' ) ) { - $redirect = dokan_edit_product_url( $response ); - } else { - $redirect = dokan_get_navigation_url( 'products' ); - } - - wp_send_json_success( $redirect ); - } else { - wp_send_json_error( __( 'Something wrong, please try again later', 'dokan-lite' ) ); - } - } - /** * Check the availability of shop name. * diff --git a/includes/Assets.php b/includes/Assets.php index 2279fd01ca..4d3e210000 100644 --- a/includes/Assets.php +++ b/includes/Assets.php @@ -6,6 +6,7 @@ use WeDevs\Dokan\ReverseWithdrawal\SettingsHelper; use WeDevs\Dokan\ProductCategory\Helper as CategoryHelper; use WeDevs\Dokan\Utilities\OrderUtil; +use WeDevs\Dokan\ProductForm\Factory as ProductFormFactory; class Assets { @@ -527,7 +528,7 @@ public function get_scripts() { ], 'dokan-util-helper' => [ 'src' => $asset_url . '/js/helper.js', - 'deps' => [ 'jquery', 'dokan-sweetalert2', 'moment' ], + 'deps' => [ 'jquery', 'dokan-sweetalert2', 'moment', 'jquery-tiptip' ], 'version' => filemtime( $asset_path . 'js/helper.js' ), 'in_footer' => false, ], @@ -572,6 +573,15 @@ public function get_scripts() { return $scripts; } + public function get_product_fields() { + $temp_fields = []; + foreach ( ProductFormFactory::get_fields() as $field_id => $field ) { + $temp_fields[ $field_id ] = $field->toArray(); + } + + return json_encode( $temp_fields ); + } + /** * Enqueue front-end scripts */ @@ -617,6 +627,7 @@ public function enqueue_front_scripts() { 'currency_format' => esc_attr( str_replace( [ '%1$s', '%2$s' ], [ '%s', '%v' ], get_woocommerce_price_format() ) ), // For accounting JS 'round_at_subtotal' => get_option( 'woocommerce_tax_round_at_subtotal', 'no' ), 'product_types' => apply_filters( 'dokan_product_types', [ 'simple' ] ), + 'product_form_fields' => $this->get_product_fields(), 'loading_img' => DOKAN_PLUGIN_ASSEST . '/images/loading.gif', 'store_product_search_nonce' => wp_create_nonce( 'dokan_store_product_search_nonce' ), 'i18n_download_permission' => __( 'Are you sure you want to revoke access to this download?', 'dokan-lite' ), @@ -792,11 +803,9 @@ public function load_dokan_global_scripts() { ], ], 'sweetalert_local' => [ - 'cancelButtonText' => __( 'Cancel', 'dokan-lite' ), - 'closeButtonText' => __( 'Close', 'dokan-lite' ), - 'confirmButtonText' => __( 'OK', 'dokan-lite' ), - 'denyButtonText' => __( 'No', 'dokan-lite' ), - 'closeButtonAriaLabel' => __( 'Close this dialog', 'dokan-lite' ), + 'confirmButtonText' => __( 'OK', 'dokan-lite' ), + 'denyButtonText' => __( 'No', 'dokan-lite' ), + 'cancelButtonText' => __( 'Cancel', 'dokan-lite' ), ], ] ); @@ -988,43 +997,21 @@ public function conditional_localized_args( $default_args ) { $has_flex_height = ! empty( $general_settings['store_banner_flex_height'] ) ? $general_settings['store_banner_flex_height'] : true; $custom_args = [ - 'i18n_choose_featured_img' => __( 'Upload featured image', 'dokan-lite' ), - 'i18n_choose_file' => __( 'Choose a file', 'dokan-lite' ), - 'i18n_choose_gallery' => __( 'Add Images to Product Gallery', 'dokan-lite' ), - 'i18n_choose_featured_img_btn_text' => __( 'Set featured image', 'dokan-lite' ), - 'i18n_choose_file_btn_text' => __( 'Insert file URL', 'dokan-lite' ), - 'i18n_choose_gallery_btn_text' => __( 'Add to gallery', 'dokan-lite' ), - 'duplicates_attribute_messg' => __( 'Sorry, this attribute option already exists, Try a different one.', 'dokan-lite' ), - 'variation_unset_warning' => __( 'Warning! This product will not have any variations if this option is not checked.', 'dokan-lite' ), - 'new_attribute_prompt' => __( 'Enter a name for the new attribute term:', 'dokan-lite' ), - 'remove_attribute' => __( 'Remove this attribute?', 'dokan-lite' ), - 'dokan_placeholder_img_src' => wc_placeholder_img_src(), - 'add_variation_nonce' => wp_create_nonce( 'add-variation' ), - 'link_variation_nonce' => wp_create_nonce( 'link-variations' ), - 'delete_variations_nonce' => wp_create_nonce( 'delete-variations' ), - 'load_variations_nonce' => wp_create_nonce( 'load-variations' ), - 'save_variations_nonce' => wp_create_nonce( 'save-variations' ), - 'bulk_edit_variations_nonce' => wp_create_nonce( 'bulk-edit-variations' ), - /* translators: %d: max linked variation. */ - 'i18n_link_all_variations' => esc_js( sprintf( __( 'Are you sure you want to link all variations? This will create a new variation for each and every possible combination of variation attributes (max %d per run).', 'dokan-lite' ), defined( 'WC_MAX_LINKED_VARIATIONS' ) ? WC_MAX_LINKED_VARIATIONS : 50 ) ), - 'i18n_enter_a_value' => esc_js( __( 'Enter a value', 'dokan-lite' ) ), - 'i18n_enter_menu_order' => esc_js( __( 'Variation menu order (determines position in the list of variations)', 'dokan-lite' ) ), - 'i18n_enter_a_value_fixed_or_percent' => esc_js( __( 'Enter a value (fixed or %)', 'dokan-lite' ) ), - 'i18n_delete_all_variations' => esc_js( __( 'Are you sure you want to delete all variations? This cannot be undone.', 'dokan-lite' ) ), - 'i18n_last_warning' => esc_js( __( 'Last warning, are you sure?', 'dokan-lite' ) ), - 'i18n_choose_image' => esc_js( __( 'Choose an image', 'dokan-lite' ) ), - 'i18n_set_image' => esc_js( __( 'Set variation image', 'dokan-lite' ) ), - 'i18n_variation_added' => esc_js( __( 'variation added', 'dokan-lite' ) ), - 'i18n_variations_added' => esc_js( __( 'variations added', 'dokan-lite' ) ), - 'i18n_no_variations_added' => esc_js( __( 'No variations added', 'dokan-lite' ) ), - 'i18n_remove_variation' => esc_js( __( 'Are you sure you want to remove this variation?', 'dokan-lite' ) ), - 'i18n_scheduled_sale_start' => esc_js( __( 'Sale start date (YYYY-MM-DD format or leave blank)', 'dokan-lite' ) ), - 'i18n_scheduled_sale_end' => esc_js( __( 'Sale end date (YYYY-MM-DD format or leave blank)', 'dokan-lite' ) ), - 'i18n_edited_variations' => esc_js( __( 'Save changes before changing page?', 'dokan-lite' ) ), - 'i18n_variation_count_single' => esc_js( __( '%qty% variation', 'dokan-lite' ) ), - 'i18n_variation_count_plural' => esc_js( __( '%qty% variations', 'dokan-lite' ) ), - 'i18n_no_result_found' => esc_js( __( 'No Result Found', 'dokan-lite' ) ), - 'i18n_sales_price_error' => esc_js( __( 'Please insert value less than the regular price!', 'dokan-lite' ) ), + 'i18n_choose_featured_img' => __( 'Upload featured image', 'dokan-lite' ), + 'i18n_choose_file' => __( 'Choose a file', 'dokan-lite' ), + 'i18n_choose_gallery' => __( 'Add Images to Product Gallery', 'dokan-lite' ), + 'i18n_choose_featured_img_btn_text' => __( 'Set featured image', 'dokan-lite' ), + 'i18n_choose_file_btn_text' => __( 'Insert file URL', 'dokan-lite' ), + 'i18n_choose_gallery_btn_text' => __( 'Add to gallery', 'dokan-lite' ), + 'duplicates_attribute_messg' => __( 'Sorry, this attribute option already exists, Try a different one.', 'dokan-lite' ), + 'variation_unset_warning' => __( 'Warning! This product will not have any variations if this option is not checked.', 'dokan-lite' ), + 'new_attribute_prompt' => __( 'Enter a name for the new attribute term:', 'dokan-lite' ), + 'remove_attribute' => __( 'Remove this attribute?', 'dokan-lite' ), + 'dokan_placeholder_img_src' => wc_placeholder_img_src(), + 'i18n_choose_image' => esc_js( __( 'Choose an image', 'dokan-lite' ) ), + 'i18n_set_image' => esc_js( __( 'Set variation image', 'dokan-lite' ) ), + 'i18n_no_result_found' => esc_js( __( 'No Result Found', 'dokan-lite' ) ), + 'i18n_sales_price_error' => esc_js( __( 'Please insert value less than the regular price!', 'dokan-lite' ) ), /* translators: %s: decimal */ 'i18n_decimal_error' => sprintf( __( 'Please enter with one decimal point (%s) without thousand separators.', 'dokan-lite' ), $decimal ), /* translators: %s: price decimal separator */ @@ -1071,6 +1058,30 @@ public function conditional_localized_args( $default_args ) { 'update_settings' => __( 'Update Settings', 'dokan-lite' ), 'search_downloadable_products_nonce' => wp_create_nonce( 'search-downloadable-products' ), 'search_downloadable_products_placeholder' => __( 'Please enter 3 or more characters', 'dokan-lite' ), + // Todo: Variation related i18n strings will be moved to the Dokan Pro Assets class after releasing product form manager module. + // Variation related i18n, file: product-variation.js + 'add_variation_nonce' => wp_create_nonce( 'add-variation' ), + 'link_variation_nonce' => wp_create_nonce( 'link-variations' ), + 'delete_variations_nonce' => wp_create_nonce( 'delete-variations' ), + 'load_variations_nonce' => wp_create_nonce( 'load-variations' ), + 'save_variations_nonce' => wp_create_nonce( 'save-variations' ), + 'bulk_edit_variations_nonce' => wp_create_nonce( 'bulk-edit-variations' ), + /* translators: %d: max linked variation. */ + 'i18n_link_all_variations' => esc_js( sprintf( __( 'Are you sure you want to link all variations? This will create a new variation for each and every possible combination of variation attributes (max %d per run).', 'dokan-lite' ), defined( 'WC_MAX_LINKED_VARIATIONS' ) ? WC_MAX_LINKED_VARIATIONS : 50 ) ), + 'i18n_enter_a_value' => esc_js( __( 'Enter a value', 'dokan-lite' ) ), + 'i18n_enter_menu_order' => esc_js( __( 'Variation menu order (determines position in the list of variations)', 'dokan-lite' ) ), + 'i18n_enter_a_value_fixed_or_percent' => esc_js( __( 'Enter a value (fixed or %)', 'dokan-lite' ) ), + 'i18n_delete_all_variations' => esc_js( __( 'Are you sure you want to delete all variations? This cannot be undone.', 'dokan-lite' ) ), + 'i18n_last_warning' => esc_js( __( 'Last warning, are you sure?', 'dokan-lite' ) ), + 'i18n_variation_added' => esc_js( __( 'Variation added', 'dokan-lite' ) ), + 'i18n_variations_added' => esc_js( __( 'Variations added', 'dokan-lite' ) ), + 'i18n_no_variations_added' => esc_js( __( 'No variations added', 'dokan-lite' ) ), + 'i18n_remove_variation' => esc_js( __( 'Are you sure you want to remove this variation?', 'dokan-lite' ) ), + 'i18n_scheduled_sale_start' => esc_js( __( 'Sale start date (YYYY-MM-DD format or leave blank)', 'dokan-lite' ) ), + 'i18n_scheduled_sale_end' => esc_js( __( 'Sale end date (YYYY-MM-DD format or leave blank)', 'dokan-lite' ) ), + 'i18n_edited_variations' => esc_js( __( 'Save changes before changing page?', 'dokan-lite' ) ), + 'i18n_variation_count_single' => esc_js( __( '%qty% variation', 'dokan-lite' ) ), + 'i18n_variation_count_plural' => esc_js( __( '%qty% variations', 'dokan-lite' ) ), ]; $default_args = array_merge( $default_args, $custom_args ); @@ -1216,6 +1227,7 @@ public function get_admin_localized_scripts() { 'decimal_point' => $decimal, 'mon_decimal_point' => wc_get_price_decimal_separator(), 'i18n_date_format' => wc_date_format(), + 'product_types' => apply_filters( 'dokan_product_types', [ 'simple' ] ), ] ); } diff --git a/includes/Blocks/ProductBlock.php b/includes/Blocks/ProductBlock.php index 000624ce54..826703f139 100644 --- a/includes/Blocks/ProductBlock.php +++ b/includes/Blocks/ProductBlock.php @@ -17,7 +17,7 @@ class ProductBlock { * @return array */ public function get_configurations() { - $can_create_tags = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' ); + $can_create_tags = dokan()->is_pro_exists() ? dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling', 'off' ) : 'off'; return apply_filters( 'dokan_get_product_block_configurations', diff --git a/includes/CatalogMode/Dashboard/ProductBulkEdit.php b/includes/CatalogMode/Dashboard/ProductBulkEdit.php index 661e40c000..093694d63c 100644 --- a/includes/CatalogMode/Dashboard/ProductBulkEdit.php +++ b/includes/CatalogMode/Dashboard/ProductBulkEdit.php @@ -72,6 +72,12 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) { // loop through the products and update the status if ( ! empty( $product_ids ) ) { foreach ( $product_ids as $product_id ) { + // get product object + $product = wc_get_product( $product_id ); + if ( ! $product ) { + continue; + } + // get existing product data $catalog_mode_data = Helper::get_catalog_mode_data_by_product( $product_id ); $count++; @@ -81,7 +87,7 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) { if ( Helper::hide_add_to_cart_button_option_is_enabled_by_admin() ) { $catalog_mode_data['hide_add_to_cart_button'] = 'on'; } - // if admin didn't enabled hide product price, set this value to off + // if admin didn't enable hide product price, set this value to off if ( ! Helper::hide_product_price_option_is_enabled_by_admin() ) { $catalog_mode_data['hide_product_price'] = 'off'; } @@ -92,8 +98,10 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) { $catalog_mode_data['hide_product_price'] = 'off'; break; } + // finally save catalog mode data - update_post_meta( $product_id, '_dokan_catalog_mode', $catalog_mode_data ); + $product->add_meta_data( '_dokan_catalog_mode', $catalog_mode_data, true ); + $product->save(); } } wp_safe_redirect( diff --git a/includes/CatalogMode/Dashboard/Products.php b/includes/CatalogMode/Dashboard/Products.php index bf84cf9d0e..ce566f3d3a 100644 --- a/includes/CatalogMode/Dashboard/Products.php +++ b/includes/CatalogMode/Dashboard/Products.php @@ -29,8 +29,7 @@ public function __construct() { // render catalog mode section under single product edit page add_action( 'dokan_product_edit_after_options', [ $this, 'render_product_section' ], 99, 1 ); // save catalog mode section data - add_action( 'dokan_product_updated', [ $this, 'save_catalog_mode_data' ], 13 ); - add_action( 'dokan_new_product_added', [ $this, 'save_catalog_mode_data' ], 13 ); + add_filter( 'dokan_product_edit_meta_data', [ $this, 'add_catalog_mode_data' ], 13, 1 ); } /** @@ -48,20 +47,23 @@ public function render_product_section( $product_id ) { return; } - // get product data $product = wc_get_product( $product_id ); - // return if product type is optional - if ( ! $product || 'auction' === $product->get_type() ) { + if ( ! $product ) { + return; + } + + // return if product type is auction + if ( 'auction' === $product->get_type() ) { return; } $defaults = Helper::get_defaults(); // check for saved values - $catalog_mode_data = get_post_meta( $product_id, '_dokan_catalog_mode', true ); + $catalog_mode_data = $product->get_meta( '_dokan_catalog_mode', true ); //load template dokan_get_template_part( - 'products/catalog-mode-content', '', [ + 'products/edit/sections/catalog-mode-content', '', [ 'product_id' => $product_id, 'saved_data' => $catalog_mode_data ? $catalog_mode_data : $defaults, ] @@ -73,17 +75,17 @@ public function render_product_section( $product_id ) { * * @since 3.6.4 * - * @param $product_id int + * @param array $meta_data * - * @return void + * @return array */ - public function save_catalog_mode_data( $product_id ) { + public function add_catalog_mode_data( array $meta_data ) { if ( ! isset( $_POST['_dokan_catalog_mode_frontend_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_dokan_catalog_mode_frontend_nonce'] ), 'dokan_catalog_mode_frontend' ) ) { - return; + return $meta_data; } if ( ! dokan_is_user_seller( dokan_get_current_user_id() ) ) { - return; + return $meta_data; } $catalog_mode_data = [ @@ -96,6 +98,8 @@ public function save_catalog_mode_data( $product_id ) { $catalog_mode_data['hide_product_price'] = 'off'; } - update_post_meta( $product_id, '_dokan_catalog_mode', $catalog_mode_data ); + $meta_data['_dokan_catalog_mode'] = $catalog_mode_data; + + return $meta_data; } } diff --git a/includes/CatalogMode/Helper.php b/includes/CatalogMode/Helper.php index 2009b90bf4..254139804e 100644 --- a/includes/CatalogMode/Helper.php +++ b/includes/CatalogMode/Helper.php @@ -174,15 +174,12 @@ public static function get_catalog_mode_data_by_product( $product ) { // get product id if ( 'variation' === $product->get_type() ) { - $product_id = $product->get_parent_id(); - } else { - $product_id = $product->get_id(); + $product = wc_get_product( $product->get_parent_id() ); } // check for saved values - $catalog_mode_data = get_post_meta( $product_id, '_dokan_catalog_mode', true ); - $catalog_mode_data = is_array( $catalog_mode_data ) && ! empty( $catalog_mode_data ) ? $catalog_mode_data : $defaults; + $catalog_mode_data = $product->get_meta( '_dokan_catalog_mode', true ); - return $catalog_mode_data; + return is_array( $catalog_mode_data ) && ! empty( $catalog_mode_data ) ? $catalog_mode_data : $defaults; } } diff --git a/includes/Dashboard/Manager.php b/includes/Dashboard/Manager.php index d15f96ce14..b99340dca1 100644 --- a/includes/Dashboard/Manager.php +++ b/includes/Dashboard/Manager.php @@ -5,6 +5,13 @@ use WeDevs\Dokan\Dashboard\Templates\Manager as TemplateManager; use WeDevs\Dokan\Traits\ChainableContainer; +/** + * Dashboard Manager + * + * @since 3.0.0 + * + * @property TemplateManager $templates Instance of TemplateManager class + */ class Manager { use ChainableContainer; diff --git a/includes/Dashboard/Templates/Manager.php b/includes/Dashboard/Templates/Manager.php index ca75447fa7..ae1e5c8cf3 100644 --- a/includes/Dashboard/Templates/Manager.php +++ b/includes/Dashboard/Templates/Manager.php @@ -2,15 +2,21 @@ namespace WeDevs\Dokan\Dashboard\Templates; -use WeDevs\Dokan\Dashboard\Templates\Dashboard; -use WeDevs\Dokan\Dashboard\Templates\Main; -use WeDevs\Dokan\Dashboard\Templates\Orders; -use WeDevs\Dokan\Dashboard\Templates\Products; -use WeDevs\Dokan\Dashboard\Templates\Settings; -use WeDevs\Dokan\Dashboard\Templates\Withdraw; -use WeDevs\Dokan\Dashboard\Templates\MultiStepCategories; use WeDevs\Dokan\Traits\ChainableContainer; +/** + * Dashboard Template Manager + * + * @since 3.0.0 + * + * @property Dashboard $dashboard Instance of Dashboard class + * @property Orders $orders Instance of Orders class + * @property Products $products Instance of Dashboard class + * @property Settings $settings Instance of Settings class + * @property Withdraw $withdraw Instance of Withdraw class + * @property MultiStepCategories $product_category Instance of MultiStepCategories class + * @property ReverseWithdrawal $reverse_withdrawal Instance of ReverseWithdrawal class + */ class Manager { use ChainableContainer; diff --git a/includes/Dashboard/Templates/Products.php b/includes/Dashboard/Templates/Products.php index 25547dadca..959fc30aa7 100644 --- a/includes/Dashboard/Templates/Products.php +++ b/includes/Dashboard/Templates/Products.php @@ -2,7 +2,12 @@ namespace WeDevs\Dokan\Dashboard\Templates; -use WeDevs\Dokan\ProductCategory\Helper; +use WC_Product; +use WC_Product_Factory; +use WC_Product_Simple; +use WeDevs\Dokan\ProductForm\Elements as ProductFormElements; +use WeDevs\Dokan\ProductForm\Factory as ProductFormFactory; +use WP_Post; /** * Product Functionality for Product Handler @@ -13,8 +18,31 @@ */ class Products { - public static $errors; + /** + * Errors + * + * @since 3.0.0 + * + * @var array + */ + public static $errors = []; + + /** + * Product Category + * + * @since 3.0.0 + * + * @var array + */ public static $product_cat; + + /** + * Post Content + * + * @since 3.0.0 + * + * @var array + */ public static $post_content; /** @@ -26,19 +54,20 @@ class Products { * @uses filters */ public function __construct() { + // render product listing template add_action( 'dokan_render_product_listing_template', [ $this, 'render_product_listing_template' ], 11 ); - add_action( 'template_redirect', [ $this, 'handle_product_add' ], 11 ); - add_action( 'template_redirect', [ $this, 'handle_product_update' ], 11 ); - add_action( 'template_redirect', [ $this, 'handle_delete_product' ] ); - add_action( 'dokan_render_new_product_template', [ $this, 'render_new_product_template' ], 10 ); + + // render product edit page template sections add_action( 'dokan_render_product_edit_template', [ $this, 'load_product_edit_template' ], 11 ); - add_action( 'template_redirect', [ $this, 'render_product_edit_page_for_email' ], 1 ); - add_action( 'dokan_after_listing_product', [ $this, 'load_add_new_product_popup' ], 10 ); - add_action( 'dokan_after_listing_product', [ $this, 'load_add_new_product_modal' ], 10 ); add_action( 'dokan_product_edit_after_title', [ __CLASS__, 'load_download_virtual_template' ], 10, 2 ); add_action( 'dokan_product_edit_after_main', [ __CLASS__, 'load_inventory_template' ], 5, 2 ); add_action( 'dokan_product_edit_after_main', [ __CLASS__, 'load_downloadable_template' ], 10, 2 ); add_action( 'dokan_product_edit_after_inventory_variants', [ __CLASS__, 'load_others_template' ], 85, 2 ); + + // handle product edit/delete operations + add_action( 'template_redirect', [ $this, 'render_product_edit_page_for_email' ], 1 ); + add_action( 'template_redirect', [ $this, 'handle_delete_product' ] ); + add_action( 'template_redirect', [ $this, 'handle_product_update' ], 11 ); } /** @@ -46,7 +75,7 @@ public function __construct() { * * @since 3.0.0 * - * @param void $errors + * @param array $errors * * @return void */ @@ -76,56 +105,145 @@ public function get_errors() { return self::$errors; } + /** + * Load Product Edit Template + * + * @since 2.4 + * + * @return void + */ + public function load_product_edit_template() { + // check for permission + if ( ! current_user_can( 'dokan_edit_product' ) ) { + dokan_get_template_part( + 'global/dokan-error', '', [ + 'deleted' => false, + 'message' => __( 'You have no permission to view this page', 'dokan-lite' ), + ] + ); + + return; + } + + // check if seller is enabled for selling + if ( ! dokan_is_seller_enabled( dokan_get_current_user_id() ) ) { + dokan_seller_not_enabled_notice(); + + return; + } + + // check for valid nonce and product id + if ( ! isset( $_GET['_dokan_edit_product_nonce'], $_GET['product_id'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_dokan_edit_product_nonce'] ), 'dokan_edit_product_nonce' ) ) { + dokan_get_template_part( + 'global/dokan-error', '', [ + 'deleted' => false, + 'message' => __( 'Are you cheating?', 'dokan-lite' ), + ] + ); + + return; + } + + // check if this is a product add or edit page + $product_id = intval( wp_unslash( $_GET['product_id'] ) ); + $new_product = false; + if ( ! $product_id ) { + // this is a product add page request + // now create a new product with auto draft status + $product = new WC_Product_Simple(); + $product->set_status( 'auto-draft' ); + $product->save(); + $new_product = true; + } else { + // this is a product edit page request + $product = wc_get_product( $product_id ); + } + + if ( ! dokan_is_product_author( $product->get_id() ) ) { + dokan_get_template_part( + 'global/dokan-error', '', [ + 'deleted' => false, + 'message' => __( 'Access Denied. Given product is not yours.', 'dokan-lite' ), + ] + ); + + return; + } + + dokan_get_template_part( + 'products/edit/edit-product-single', '', + [ + 'product' => $product, + 'new_product' => $new_product, + 'from_shortcode' => true, + ] + ); + } + /** * Load product * * @since 1.0.0 * + * @param WP_Post $post + * @param int $post_id + * * @return void */ public static function load_download_virtual_template( $post, $post_id ) { - $_downloadable = get_post_meta( $post_id, '_downloadable', true ); - $_virtual = get_post_meta( $post_id, '_virtual', true ); - $is_downloadable = 'yes' === $_downloadable; - $is_virtual = 'yes' === $_virtual; - $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' ); + $product = wc_get_product( $post_id ); + if ( ! $product ) { + return; + } + $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' ); if ( 'sell_physical' === $digital_mode ) { return; } + // get section data + $section = ProductFormFactory::get_section( 'downloadable' ); + if ( is_wp_error( $section ) || ! $section->is_visible() ) { + return; + } + dokan_get_template_part( - 'products/download-virtual', '', [ - 'post_id' => $post_id, - 'post' => $post, - 'is_downloadable' => $is_downloadable, - 'is_virtual' => $is_virtual, - 'digital_mode' => $digital_mode, - 'class' => 'show_if_subscription hide_if_variable-subscription show_if_simple', + 'products/edit/sections/download-virtual', '', [ + 'product' => $product, + 'section' => $section, + 'digital_mode' => $digital_mode, + 'class' => 'show_if_subscription hide_if_variable-subscription show_if_simple', ] ); } /** - * Load invendor template + * Load inventory template * * @since 2.9.2 * + * @param WP_Post $post + * @param int $post_id + * * @return void */ public static function load_inventory_template( $post, $post_id ) { - $_sold_individually = get_post_meta( $post_id, '_sold_individually', true ); - $_stock = get_post_meta( $post_id, '_stock', true ); - $_low_stock_amount = get_post_meta( $post_id, '_low_stock_amount', true ); + $product = wc_get_product( $post_id ); + if ( ! $product ) { + return; + } + + // get section data + $section = ProductFormFactory::get_section( 'inventory' ); + if ( is_wp_error( $section ) || ! $section->is_visible() ) { + return; + } dokan_get_template_part( - 'products/inventory', '', [ - 'post_id' => $post_id, - 'post' => $post, - '_sold_individually' => $_sold_individually, - '_stock' => $_stock, - '_low_stock_amount' => $_low_stock_amount, - 'class' => '', + 'products/edit/sections/inventory', '', [ + 'section' => $section, + 'product' => $product, + 'class' => '', ] ); } @@ -135,19 +253,32 @@ public static function load_inventory_template( $post, $post_id ) { * * @since 2.9.2 * + * @param WP_Post $post + * @param int $post_id + * * @return void */ public static function load_downloadable_template( $post, $post_id ) { - $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' ); + $product = wc_get_product( $post_id ); + if ( ! $product ) { + return; + } + $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' ); if ( 'sell_physical' === $digital_mode ) { return; } + // get section data + $section = ProductFormFactory::get_section( 'downloadable' ); + if ( is_wp_error( $section ) || ! $section->is_visible() ) { + return; + } + dokan_get_template_part( - 'products/downloadable', '', [ - 'post_id' => $post_id, - 'post' => $post, + 'products/edit/sections/downloadable', '', [ + 'section' => $section, + 'product' => $product, 'class' => 'show_if_downloadable', ] ); @@ -158,64 +289,31 @@ public static function load_downloadable_template( $post, $post_id ) { * * @since 2.9.2 * + * @param WP_Post $post + * @param int $post_id + * * @return void */ public static function load_others_template( $post, $post_id ) { - $product = wc_get_product( $post_id ); - $_visibility = $product->get_catalog_visibility(); - $visibility_options = dokan_get_product_visibility_options(); + $product = wc_get_product( $post_id ); + if ( ! $product ) { + return; + } - // set new post status - $post_status = dokan_get_default_product_status( dokan_get_current_user_id() ); - $post_status = $product->get_status() === 'auto-draft' ? $post_status : $product->get_status(); + $section = ProductFormFactory::get_section( 'others' ); + if ( is_wp_error( $section ) || ! $section->is_visible() ) { + return; + } dokan_get_template_part( - 'products/others', '', [ - 'post_id' => $post_id, - 'post' => $post, - 'post_status' => apply_filters( 'dokan_post_edit_default_status', $post_status, $product ), - '_visibility' => $_visibility, - 'visibility_options' => $visibility_options, - 'class' => '', + 'products/edit/sections/others', '', [ + 'section' => $section, + 'product' => $product, + 'class' => '', ] ); } - /** - * Render New Product Template for only free version - * - * @since 2.4 - * - * @param array $query_vars - * - * @return void - */ - public function render_new_product_template( $query_vars ) { - if ( isset( $query_vars['new-product'] ) && ! dokan()->is_pro_exists() ) { - dokan_get_template_part( 'products/new-product' ); - } - } - - /** - * Load Product Edit Template - * - * @since 2.4 - * - * @return void - */ - public function load_product_edit_template() { - if ( current_user_can( 'dokan_edit_product' ) ) { - dokan_get_template_part( 'products/edit-product-single' ); - } else { - dokan_get_template_part( - 'global/dokan-error', '', [ - 'deleted' => false, - 'message' => __( 'You have no permission to view this page', 'dokan-lite' ), - ] - ); - } - } - /** * Render Product Edit Page for Email. * @@ -243,11 +341,9 @@ public function render_product_edit_page_for_email() { * * @since 2.4 * - * @param string $action - * * @return void */ - public function render_product_listing_template( $action ) { + public function render_product_listing_template() { $bulk_statuses = apply_filters( 'dokan_bulk_product_statuses', [ '-1' => __( 'Bulk Actions', 'dokan-lite' ), @@ -259,11 +355,13 @@ public function render_product_listing_template( $action ) { } /** - * Handle product add + * Handle product update + * + * @var $product WC_Product * * @return void */ - public function handle_product_add() { + public function handle_product_update() { if ( ! is_user_logged_in() ) { return; } @@ -272,320 +370,207 @@ public function handle_product_add() { return; } - if ( ! isset( $_POST['dokan_add_new_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_add_new_product_nonce'] ), 'dokan_add_new_product' ) ) { + if ( ! isset( $_POST['dokan_edit_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_edit_product_nonce'] ), 'dokan_edit_product' ) ) { return; } - $postdata = wp_unslash( $_POST ); // phpcs:ignore - - $errors = []; - self::$product_cat = - 1; - self::$post_content = __( 'Details of your product ...', 'dokan-lite' ); - - if ( isset( $postdata['add_product'] ) ) { - $post_title = sanitize_text_field( $postdata['post_title'] ); - $post_content = wp_kses_post( $postdata['post_content'] ); - $post_excerpt = wp_kses_post( $postdata['post_excerpt'] ); - $featured_image = absint( sanitize_text_field( $postdata['feat_image_id'] ) ); - - if ( empty( $post_title ) ) { - $errors[] = __( 'Please enter product title', 'dokan-lite' ); - } - - if ( ! isset( $postdata['chosen_product_cat'] ) ) { - if ( Helper::product_category_selection_is_single() ) { - if ( absint( $postdata['product_cat'] ) < 0 ) { - $errors[] = __( 'Please select a category', 'dokan-lite' ); - } - } else { - if ( ! isset( $postdata['product_cat'] ) || empty( $postdata['product_cat'] ) ) { - $errors[] = __( 'Please select at least one category', 'dokan-lite' ); - } - } - } elseif ( empty( $postdata['chosen_product_cat'] ) ) { - $errors[] = __( 'Please select a category', 'dokan-lite' ); - } + $product_id = isset( $_POST['dokan_product_id'] ) ? absint( $_POST['dokan_product_id'] ) : 0; + if ( ! $product_id ) { + self::$errors[] = __( 'No product id is set!', 'dokan-lite' ); - self::$errors = apply_filters( 'dokan_can_add_product', $errors ); - - if ( ! self::$errors ) { - $timenow = dokan_current_datetime()->setTimezone( new \DateTimeZone( 'UTC' ) ); - $product_status = dokan_get_default_product_status( dokan_get_current_user_id() ); - $post_data = apply_filters( - 'dokan_insert_product_post_data', [ - 'post_type' => 'product', - 'post_status' => $product_status, - 'post_title' => $post_title, - 'post_content' => $post_content, - 'post_excerpt' => $post_excerpt, - 'post_date_gmt' => $timenow->format( 'Y-m-d H:i:s' ), - 'post_modified_gmt' => $timenow->format( 'Y-m-d H:i:s' ), - ] - ); - - $product_id = wp_insert_post( $post_data ); - - if ( $product_id ) { - // set images - if ( $featured_image ) { - set_post_thumbnail( $product_id, $featured_image ); - } + return; + } - if ( isset( $postdata['product_tag'] ) && ! empty( $postdata['product_tag'] ) ) { - $tags_ids = array_map( 'absint', (array) $postdata['product_tag'] ); - wp_set_object_terms( $product_id, $tags_ids, 'product_tag' ); - } + if ( ! dokan_is_product_author( $product_id ) ) { + self::$errors[] = __( 'I swear this is not your product!', 'dokan-lite' ); - /* set product category */ - if ( ! isset( $postdata['chosen_product_cat'] ) ) { - if ( Helper::product_category_selection_is_single() ) { - wp_set_object_terms( $product_id, (int) $postdata['product_cat'], 'product_cat' ); - } else { - if ( isset( $postdata['product_cat'] ) && ! empty( $postdata['product_cat'] ) ) { - $cat_ids = array_map( 'absint', (array) $postdata['product_cat'] ); - wp_set_object_terms( $product_id, $cat_ids, 'product_cat' ); - } - } - } else { - $chosen_cat = Helper::product_category_selection_is_single() ? [ reset( $postdata['chosen_product_cat'] ) ] : $postdata['chosen_product_cat']; - Helper::set_object_terms_from_chosen_categories( $product_id, $chosen_cat ); - } + return; + } - /** Set Product type, default is simple */ - $product_type = empty( $postdata['product_type'] ) ? 'simple' : $postdata['product_type']; - wp_set_object_terms( $product_id, $product_type, 'product_type' ); + // Process the product type first so that we have the correct class to run setters. + $product_type_field = ProductFormFactory::get_field( ProductFormElements::TYPE ); + $product_type = empty( $_POST[ $product_type_field->get_name() ] ) ? WC_Product_Factory::get_product_type( $product_id ) : sanitize_title( wp_unslash( $_POST[ $product_type_field->get_name() ] ) ); + $classname = WC_Product_Factory::get_product_classname( $product_id, $product_type ? $product_type : 'simple' ); + $product = new $classname( $product_id ); - // Gallery Images - if ( ! empty( $postdata['product_image_gallery'] ) ) { - $attachment_ids = array_filter( explode( ',', wc_clean( $postdata['product_image_gallery'] ) ) ); - update_post_meta( $product_id, '_product_image_gallery', implode( ',', $attachment_ids ) ); - } + /** + * @var WC_Product $product + */ + if ( ! $product ) { + self::$errors[] = __( 'No product found with given product id!', 'dokan-lite' ); - if ( isset( $postdata['_regular_price'] ) ) { - update_post_meta( $product_id, '_regular_price', ( $postdata['_regular_price'] === '' ) ? '' : wc_format_decimal( $postdata['_regular_price'] ) ); - } + return; + } - if ( isset( $postdata['_sale_price'] ) ) { - update_post_meta( $product_id, '_sale_price', ( $postdata['_sale_price'] === '' ? '' : wc_format_decimal( $postdata['_sale_price'] ) ) ); - $date_from = isset( $postdata['_sale_price_dates_from'] ) ? wc_clean( $postdata['_sale_price_dates_from'] ) : ''; - $date_to = isset( $postdata['_sale_price_dates_to'] ) ? wc_clean( $postdata['_sale_price_dates_to'] ) : ''; - $now = dokan_current_datetime(); - - // Dates - if ( $date_from ) { - update_post_meta( $product_id, '_sale_price_dates_from', $now->modify( $date_from )->setTime( 0, 0, 0 )->getTimestamp() ); - } else { - update_post_meta( $product_id, '_sale_price_dates_from', '' ); - } - - if ( $date_to ) { - update_post_meta( $product_id, '_sale_price_dates_to', $now->modify( $date_to )->setTime( 23, 59, 59 )->getTimestamp() ); - } else { - update_post_meta( $product_id, '_sale_price_dates_to', '' ); - } - - if ( $date_to && ! $date_from ) { - update_post_meta( $product_id, '_sale_price_dates_from', $now->setTime( 0, 0, 0 )->getTimestamp() ); - } - - if ( '' !== $postdata['_sale_price'] && '' === $date_to && '' === $date_from ) { - update_post_meta( $product_id, '_price', wc_format_decimal( $postdata['_sale_price'] ) ); - } else { - update_post_meta( $product_id, '_price', ( $postdata['_regular_price'] === '' ) ? '' : wc_format_decimal( $postdata['_regular_price'] ) ); - } - // Update price if on sale - if ( '' !== $postdata['_sale_price'] && $date_from && $now->modify( $date_from )->getTimestamp() < $now->getTimestamp() ) { - update_post_meta( $product_id, '_price', wc_format_decimal( $postdata['_sale_price'] ) ); - } - } + $props = []; + $meta_data = []; + $is_new_product = 'auto-draft' === $product->get_status(); - update_post_meta( $product_id, '_visibility', 'visible' ); - update_post_meta( $product_id, '_stock_status', 'instock' ); + // stock quantity + $props[ ProductFormElements::STOCK_QUANTITY ] = null; - do_action( 'dokan_new_product_added', $product_id, $postdata ); + foreach ( ProductFormFactory::get_fields() as $field_id => $field ) { + if ( $field->is_other_type() ) { + // we will only process props and meta in this block + continue; + } + // check if the field is visible + if ( ! $field->is_visible() ) { + continue; + } - if ( current_user_can( 'dokan_edit_product' ) ) { - $redirect = dokan_edit_product_url( $product_id ); - } else { - $redirect = dokan_get_navigation_url( 'products' ); - } + $field_name = sanitize_key( $field->get_name() ); - if ( 'create_and_add_new' === $postdata['add_product'] ) { - $redirect = add_query_arg( - [ - 'created_product' => $product_id, - '_dokan_add_product_nonce' => wp_create_nonce( 'dokan_add_product_nonce' ), - ], - dokan_get_navigation_url( 'new-product' ) - ); - } + // Skip all of the "Downloads" section fields if "Downloadable" field disabled. + $downloadable_fields = array_keys( ProductFormFactory::get_fields_by_type( '', 'downloadable' ) ); + if ( in_array( $field->get_id(), $downloadable_fields, true ) && isset( $_POST['_downloadable'] ) && 'no' === $_POST['_downloadable'] ) { + continue; + } - wp_safe_redirect( apply_filters( 'dokan_add_new_product_redirect', $redirect, $product_id ) ); - exit; - } + // Skip "This product requires shipping" checkbox field if "Virtual" field enabled. + if ( ProductFormElements::DISABLE_SHIPPING_META === $field->get_id() && isset( $_POST['_virtual'] ) && 'yes' === $_POST['_virtual'] ) { + continue; } - } - } - /** - * Handle product update - * - * @return void - */ - public function handle_product_update() { - if ( ! is_user_logged_in() ) { - return; - } + // Skip shipping related fields if "This product requires shipping" field disabled. + $shipping_fields = [ 'weight', 'length', 'width', 'height', 'shipping_class_id' ]; + if ( in_array( $field->get_id(), $shipping_fields, true ) && isset( $_POST['_disable_shipping'] ) && 'no' === $_POST['_disable_shipping'] ) { + continue; + } - if ( ! dokan_is_user_seller( get_current_user_id() ) ) { - return; - } + // Skip the "Stock Status" field if "Enable Product Stock Management" field enabled. + if ( ProductFormElements::STOCK_STATUS === $field->get_id() && isset( $_POST['_manage_stock'] ) && 'yes' === $_POST['_manage_stock'] ) { + continue; + } - if ( ! isset( $_POST['dokan_update_product'] ) ) { - return; - } + // Skip the "Stock Qty", "Low Stock Threshold" and "Allow Backorders" fields if "Enable Product Stock Management" field disabled. + $manage_stock_fields = [ ProductFormElements::STOCK_QUANTITY, ProductFormElements::LOW_STOCK_AMOUNT, ProductFormElements::BACKORDERS ]; + if ( in_array( $field->get_id(), $manage_stock_fields, true ) && isset( $_POST['_manage_stock'] ) && 'no' === $_POST['_manage_stock'] ) { + continue; + } - if ( ! isset( $_POST['dokan_edit_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_edit_product_nonce'] ), 'dokan_edit_product' ) ) { - return; - } + // Skip the "Minimum Quantity" and "Discount %" fields if "Enable Bulk Discount" field disabled. + if ( in_array( $field->get_id(), [ '_lot_discount_quantity', '_lot_discount_amount' ], true ) && isset( $_POST['_is_lot_discount'] ) && 'no' === $_POST['_is_lot_discount'] ) { + continue; + } - $errors = []; - $post_title = isset( $_POST['post_title'] ) ? sanitize_text_field( wp_unslash( $_POST['post_title'] ) ) : ''; - $post_slug = ! empty( $_POST['editable-post-name'] ) ? sanitize_title( wp_unslash( $_POST['editable-post-name'] ) ) : ''; - $post_status = isset( $_POST['post_status'] ) ? sanitize_text_field( wp_unslash( $_POST['post_status'] ) ) : ''; - $post_content = isset( $_POST['post_content'] ) ? wp_kses_post( wp_unslash( $_POST['post_content'] ) ) : ''; - $post_excerpt = isset( $_POST['post_excerpt'] ) ? wp_kses_post( wp_unslash( $_POST['post_excerpt'] ) ) : ''; + if ( apply_filters( 'dokan_product_form_skip_field_validation', false, $field ) ) { + continue; + } - if ( empty( $post_title ) ) { - $errors[] = __( 'Please enter product title', 'dokan-lite' ); - } + // check if field is required + if ( $field->is_required() && empty( $_POST[ $field_name ] ) ) { + self::$errors[ $field->get_id() ] = ! empty( $field->get_error_message() ) + ? $field->get_error_message() + // translators: 1) field title + : sprintf( __( '%1$s is a required field.', 'dokan-lite' ), $field->get_title() ); + continue; + } - $post_id = isset( $_POST['dokan_product_id'] ) ? absint( $_POST['dokan_product_id'] ) : 0; - if ( ! $post_id ) { - $errors[] = __( 'No product found!', 'dokan-lite' ); - } + // check if the field is present + if ( ! isset( $_POST[ $field_name ] ) ) { + continue; + } - $current_post_status = get_post_status( $post_id ); - $is_new_product = 'auto-draft' === $current_post_status; + // validate field data + switch ( $field->get_id() ) { + case ProductFormElements::CATEGORIES: + $field_value = $field->sanitize( (array) wp_unslash( $_POST['chosen_product_cat'] ), $product ); // phpcs:ignore + break; + + case ProductFormElements::DOWNLOADS: + $file_names = isset( $_POST['_wc_file_names'] ) ? wp_unslash( $_POST['_wc_file_names'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $file_urls = isset( $_POST['_wc_file_urls'] ) ? wp_unslash( $_POST['_wc_file_urls'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $file_hashes = isset( $_POST['_wc_file_hashes'] ) ? wp_unslash( $_POST['_wc_file_hashes'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $field_value = $field->sanitize( $file_names, $file_urls, $file_hashes ); //phpcs:ignore + break; + + case ProductFormElements::STOCK_QUANTITY: + $original_stock = isset( $_POST['_original_stock'] ) ? wc_stock_amount( wp_unslash( $_POST['_original_stock'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $field_value = $field->sanitize( wp_unslash( $_POST[ $field_name ] ), $original_stock, $product ); //phpcs:ignore + break; + + case ProductFormElements::GALLERY_IMAGE_IDS: + $attachment_ids = isset( $_POST['gallery_image_ids'] ) ? apply_filters( 'dokan_restrict_product_gallery_images_on_edit', wp_unslash( $_POST['gallery_image_ids'] ) ) : ''; + $attachment_ids = array_filter( explode( ',', wc_clean( $attachment_ids ) ) ); + $field_value = $field->sanitize( implode( ',', $attachment_ids ), $product ); //phpcs:ignore + break; + + default: + // give a chance to other plugins to sanitize their data + $field_value = apply_filters( 'dokan_product_update_field_value', null, $field, wp_unslash( $_POST[ $field_name ] ), $product ); // phpcs:ignore + if ( null === $field_value ) { + $field_value = $field->sanitize( wp_unslash( $_POST[ $field_name ] ) ); //phpcs:ignore + } + break; + } - if ( empty( $post_status ) ) { - $post_status = $current_post_status; - } + if ( is_wp_error( $field_value ) ) { + self::$errors[ $field->get_id() ] = $field_value->get_error_message(); + } else { + //store field value + $field->set_value( $field_value ); + } - if ( ! dokan_is_product_author( $post_id ) ) { - $errors[] = __( 'I swear this is not your product!', 'dokan-lite' ); + // store props and meta data + if ( $field->is_prop() ) { + $props[ $field->get_id() ] = $field_value; + } elseif ( $field->is_meta() || $field->is_custom() ) { + $meta_data[ $field->get_id() ] = $field_value; + } } - if ( empty( $_POST['chosen_product_cat'] ) ) { - $errors[] = __( 'Please select a category', 'dokan-lite' ); + // check for valid product tag + if ( ! empty( $_POST['editable-post-name'] ) ) { + $post_slug = ProductFormFactory::get_field( ProductFormElements::SLUG ); + $post_status = $props[ ProductFormElements::STATUS ] ?? dokan_get_default_product_status( dokan_get_current_user_id() ); + $props[ $post_slug->get_name() ] = $post_slug->sanitize( wp_unslash( $_POST['editable-post-name'] ), $product_id, $post_status ); //phpcs:ignore } - if ( isset( $_POST['product_tag'] ) ) { - /** - * Filter of maximun a vendor can add tags. - * - * @since 3.3.7 - * - * @param integer default -1 - */ - $maximum_tags_select_length = apply_filters( 'dokan_product_tags_select_max_length', - 1 ); + // apply hooks before saving product + $props = apply_filters( 'dokan_product_edit_props', $props, $product, $is_new_product ); + $meta_data = apply_filters( 'dokan_product_edit_meta_data', $meta_data, $product, $is_new_product ); - // Setting limitation for how many product tags that vendor can input. - $tag_count = count( (array) $_POST['product_tag'] ); - if ( $maximum_tags_select_length !== - 1 && $tag_count > $maximum_tags_select_length ) { - // translators: %s: maximum tag length - $errors[] = sprintf( __( 'You can only select %s tags', 'dokan-lite' ), number_format_i18n( $maximum_tags_select_length ) ); - } + $errors = $product->set_props( $props ); + if ( is_wp_error( $errors ) ) { + self::$errors = array_merge( self::$errors, $errors->get_error_messages() ); } - self::$errors = apply_filters( 'dokan_can_edit_product', $errors ); - - if ( self::$errors ) { + // return early for validation errors + self::$errors = apply_filters( 'dokan_can_edit_product', self::$errors ); + if ( ! empty( self::$errors ) ) { return; } - $product_info = apply_filters( - 'dokan_update_product_post_data', [ - 'ID' => $post_id, - 'post_title' => $post_title, - 'post_content' => $post_content, - 'post_excerpt' => $post_excerpt, - 'post_status' => $post_status, - 'comment_status' => isset( $_POST['_enable_reviews'] ) ? 'open' : 'closed', - ] - ); - - if ( $post_slug ) { - $product_info['post_name'] = wp_unique_post_slug( $post_slug, $post_id, $post_status, 'product', 0 ); - } - - wp_update_post( $product_info ); - - /** Set Product tags */ - if ( isset( $_POST['product_tag'] ) ) { - $tags_ids = array_map( 'absint', (array) wp_unslash( $_POST['product_tag'] ) ); + if ( $is_new_product ) { + do_action( 'dokan_before_new_product_added', $product, $props, $meta_data ); } else { - $tags_ids = []; + do_action( 'dokan_before_product_updated', $product, $props, $meta_data ); } - wp_set_object_terms( $post_id, $tags_ids, 'product_tag' ); - - /* set product category */ - $chosen_product_categories = array_map( 'absint', (array) wp_unslash( $_POST['chosen_product_cat'] ) ); - $chosen_cat = Helper::product_category_selection_is_single() ? [ reset( $chosen_product_categories ) ] : $chosen_product_categories; - Helper::set_object_terms_from_chosen_categories( $post_id, $chosen_cat ); + $postdata = wp_unslash( $_POST ); // phpcs:ignore - //set prodcuct type default is simple - $product_type = empty( $_POST['product_type'] ) ? 'simple' : sanitize_text_field( wp_unslash( $_POST['product_type'] ) ); - wp_set_object_terms( $post_id, $product_type, 'product_type' ); + // add product meta data + foreach ( $meta_data as $meta_key => $meta_value ) { + $product->add_meta_data( $meta_key, $meta_value, true ); - // set images - $featured_image = isset( $_POST['feat_image_id'] ) ? absint( wp_unslash( $_POST['feat_image_id'] ) ) : 0; - if ( $featured_image ) { - set_post_thumbnail( $post_id, $featured_image ); - } else { - delete_post_thumbnail( $post_id ); + unset( $postdata[ $meta_key ] ); } - $postdata = wp_unslash( $_POST ); // phpcs:ignore - /** Process all variation products meta */ - dokan_process_product_meta( $post_id, $postdata ); + // save product + $product->save(); if ( $is_new_product ) { - do_action( 'dokan_new_product_added', $post_id, $postdata ); + do_action( 'dokan_new_product_added', $product_id, $postdata ); } else { - do_action( 'dokan_product_updated', $post_id, $postdata ); - } - - $redirect = apply_filters( 'dokan_add_new_product_redirect', dokan_edit_product_url( $post_id ), $post_id ); - - // if any error inside dokan_process_product_meta function - global $woocommerce_errors; - - if ( $woocommerce_errors ) { - wp_safe_redirect( add_query_arg( [ 'errors' => array_map( 'urlencode', $woocommerce_errors ) ], $redirect ) ); - exit; + do_action( 'dokan_product_updated', $product_id, $postdata ); } + $redirect = apply_filters( 'dokan_add_new_product_redirect', dokan_edit_product_url( $product_id ), $product_id ); wp_safe_redirect( add_query_arg( [ 'message' => 'success' ], $redirect ) ); exit; } - public function load_add_new_product_popup() { - dokan_get_template_part( 'products/tmpl-add-product-popup' ); - } - - /** - * Add new product open modal html - * - * @since 3.7.0 - * - * @return void - */ - public function load_add_new_product_modal() { - dokan_get_template_part( 'products/add-new-product-modal' ); - } - /** * Handle delete product link * @@ -632,5 +617,4 @@ public function handle_delete_product() { wp_safe_redirect( add_query_arg( [ 'message' => 'product_deleted' ], $redirect ) ); exit; } - } diff --git a/includes/DependencyManagement/Providers/CommonServiceProvider.php b/includes/DependencyManagement/Providers/CommonServiceProvider.php index 03d0fb2858..410dd9f2c9 100644 --- a/includes/DependencyManagement/Providers/CommonServiceProvider.php +++ b/includes/DependencyManagement/Providers/CommonServiceProvider.php @@ -65,5 +65,9 @@ public function register(): void { $this->getContainer() ->addShared( \WeDevs\Dokan\Privacy::class, \WeDevs\Dokan\Privacy::class ) ->addTag( self::TAG ); + + $this->getContainer() + ->addShared( \WeDevs\Dokan\ProductForm\Init::class, \WeDevs\Dokan\ProductForm\Init::class ) + ->addTag( self::TAG ); } } diff --git a/includes/Emails/NewProduct.php b/includes/Emails/NewProduct.php index eee751f3d9..d665b7d3db 100644 --- a/includes/Emails/NewProduct.php +++ b/includes/Emails/NewProduct.php @@ -92,7 +92,7 @@ public function trigger( $product_id ) { return; } - $product->update_meta_data( '_dokan_new_product_email_sent', 'yes' ); + $product->add_meta_data( '_dokan_new_product_email_sent', 'yes', true ); $product->save(); if ( ! $this->is_enabled() || ! $this->get_recipient() ) { diff --git a/includes/Emails/NewProductPending.php b/includes/Emails/NewProductPending.php index 981df44403..1382e13ebb 100644 --- a/includes/Emails/NewProductPending.php +++ b/includes/Emails/NewProductPending.php @@ -93,7 +93,7 @@ public function trigger( $product_id ) { return; } - $product->update_meta_data( '_dokan_new_product_email_sent', 'yes' ); + $product->add_meta_data( '_dokan_new_product_email_sent', 'yes', true ); $product->save(); if ( ! $this->is_enabled() || ! $this->get_recipient() ) { diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php index 02f45a9847..5d364928fa 100644 --- a/includes/Product/Hooks.php +++ b/includes/Product/Hooks.php @@ -6,6 +6,8 @@ use WeDevs\Dokan\ProductCategory\Helper; use WC_Product; +defined( 'ABSPATH' ) || exit; + /** * Admin Hooks * @@ -31,7 +33,7 @@ public function __construct() { add_action( 'woocommerce_new_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 ); add_action( 'woocommerce_update_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 ); add_filter( 'dokan_post_status', [ $this, 'set_product_status' ], 1, 2 ); - add_action( 'dokan_new_product_added', [ $this, 'set_new_product_email_status' ], 1, 1 ); + add_filter( 'dokan_product_edit_meta_data', [ $this, 'set_new_product_email_status' ], 1, 3 ); // Remove product type filter if pro not exists. add_filter( 'dokan_product_listing_filter_args', [ $this, 'remove_product_type_filter' ] ); @@ -370,16 +372,20 @@ public function set_product_status( $all_statuses, int $product_id ) { * * @since 3.8.2 * - * @param int|WC_Product $product_id + * @param array $meta_data + * @param WC_Product $product + * @param bool $is_new_product * - * @return void + * @return array */ - public function set_new_product_email_status( $product_id ) { - if ( is_a( $product_id, 'WC_Product' ) ) { - $product_id->update_meta_data( '_dokan_new_product_email_sent', 'no' ); - } else { - update_post_meta( $product_id, '_dokan_new_product_email_sent', 'no' ); + public function set_new_product_email_status( array $meta_data, WC_Product $product, bool $is_new_product ) { + if ( ! $is_new_product ) { + return $meta_data; } + + $meta_data['_dokan_new_product_email_sent'] = 'no'; + + return $meta_data; } /** diff --git a/includes/Product/Manager.php b/includes/Product/Manager.php index 0a6a74cb69..bfec90d36f 100644 --- a/includes/Product/Manager.php +++ b/includes/Product/Manager.php @@ -3,12 +3,16 @@ namespace WeDevs\Dokan\Product; use WC_Product; +use WC_Product_Attribute; use WC_Product_Download; use WeDevs\Dokan\Cache; use WeDevs\Dokan\Commission\Model\Setting; use WeDevs\Dokan\Commission\Settings\Product; use WP_Error; use WP_Query; +use WeDevs\Dokan\ProductForm\Elements as FormElements; + +defined( 'ABSPATH' ) || exit; /** * Product manager Class @@ -30,7 +34,7 @@ public function all( $args = [] ) { $defaults = [ 'post_type' => 'product', 'post_status' => $post_statuses, - 'posts_per_page' => - 1, + 'posts_per_page' => -1, 'orderby' => 'post_date ID', 'order' => 'DESC', 'paged' => 1, @@ -73,100 +77,99 @@ public function get( $product_id ) { * @return WC_Product|null|false */ public function create( $args = [] ) { - $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0; + $product_id = isset( $args[ FormElements::ID ] ) ? absint( $args[ FormElements::ID ] ) : 0; + $is_update = ! empty( $product_id ); // Type is the most important part here because we need to be using the correct class and methods. - if ( isset( $args['type'] ) ) { - $classname = \WC_Product_Factory::get_classname_from_product_type( $args['type'] ); + if ( isset( $args[ FormElements::TYPE ] ) ) { + $classname = \WC_Product_Factory::get_classname_from_product_type( $args[ FormElements::TYPE ] ); if ( ! class_exists( $classname ) ) { $classname = 'WC_Product_Simple'; } - $product = new $classname( $id ); - } elseif ( isset( $args['id'] ) ) { - $product = wc_get_product( $id ); + $product = new $classname( $product_id ); + } elseif ( isset( $args[ FormElements::ID ] ) ) { + $product = wc_get_product( $product_id ); } else { $product = new \WC_Product_Simple(); } // Post title. - if ( isset( $args['name'] ) ) { - $product->set_name( wp_filter_post_kses( $args['name'] ) ); + if ( isset( $args[ FormElements::NAME ] ) ) { + $product->set_name( wp_filter_post_kses( $args[ FormElements::NAME ] ) ); } // Post content. - if ( isset( $args['description'] ) ) { - $product->set_description( wp_filter_post_kses( $args['description'] ) ); + if ( isset( $args[ FormElements::DESCRIPTION ] ) ) { + $product->set_description( wp_filter_post_kses( $args[ FormElements::DESCRIPTION ] ) ); } // Post excerpt. - if ( isset( $args['short_description'] ) ) { - $product->set_short_description( wp_filter_post_kses( $args['short_description'] ) ); + if ( isset( $args[ FormElements::SHORT_DESCRIPTION ] ) ) { + $product->set_short_description( wp_filter_post_kses( $args[ FormElements::SHORT_DESCRIPTION ] ) ); } // Post status. - if ( isset( $args['status'] ) ) { - $product->set_status( get_post_status_object( $args['status'] ) ? $args['status'] : 'draft' ); + if ( isset( $args[ FormElements::STATUS ] ) ) { + $product->set_status( get_post_status_object( $args[ FormElements::STATUS ] ) ? $args[ FormElements::STATUS ] : 'draft' ); } // Post slug. - if ( isset( $args['slug'] ) ) { - $product->set_slug( $args['slug'] ); + if ( isset( $args[ FormElements::SLUG ] ) ) { + $product->set_slug( $args[ FormElements::SLUG ] ); } // Menu order. - if ( isset( $args['menu_order'] ) ) { - $product->set_menu_order( $args['menu_order'] ); + if ( isset( $args[ FormElements::MENU_ORDER ] ) ) { + $product->set_menu_order( $args[ FormElements::MENU_ORDER ] ); } // Comment status. - if ( isset( $args['reviews_allowed'] ) ) { - $product->set_reviews_allowed( $args['reviews_allowed'] ); + if ( isset( $args[ FormElements::REVIEWS_ALLOWED ] ) ) { + $product->set_reviews_allowed( $args[ FormElements::REVIEWS_ALLOWED ] ); } // Virtual. - if ( isset( $args['virtual'] ) ) { - $product->set_virtual( $args['virtual'] ); + if ( isset( $args[ FormElements::VIRTUAL ] ) ) { + $product->set_virtual( $args[ FormElements::VIRTUAL ] ); } // Tax status. - if ( isset( $args['tax_status'] ) ) { - $product->set_tax_status( $args['tax_status'] ); + if ( isset( $args[ FormElements::TAX_STATUS ] ) ) { + $product->set_tax_status( $args[ FormElements::TAX_STATUS ] ); } // Tax Class. - if ( isset( $args['tax_class'] ) ) { - $product->set_tax_class( $args['tax_class'] ); + if ( isset( $args[ FormElements::TAX_CLASS ] ) ) { + $product->set_tax_class( $args[ FormElements::TAX_CLASS ] ); } // Catalog Visibility. - if ( isset( $args['catalog_visibility'] ) ) { - $product->set_catalog_visibility( $args['catalog_visibility'] ); + if ( isset( $args[ FormElements::CATALOG_VISIBILITY ] ) ) { + $product->set_catalog_visibility( $args[ FormElements::CATALOG_VISIBILITY ] ); } // Purchase Note. - if ( isset( $args['purchase_note'] ) ) { - $product->set_purchase_note( wp_kses_post( wp_unslash( $args['purchase_note'] ) ) ); + if ( isset( $args[ FormElements::PURCHASE_NOTE ] ) ) { + $product->set_purchase_note( wp_kses_post( wp_unslash( $args[ FormElements::PURCHASE_NOTE ] ) ) ); } // Featured Product. - if ( isset( $args['featured'] ) ) { - $product->set_featured( $args['featured'] ); + if ( isset( $args[ FormElements::FEATURED ] ) ) { + $product->set_featured( $args[ FormElements::FEATURED ] ); } // Shipping data. $product = $this->save_product_shipping_data( $product, $args ); // SKU. - if ( isset( $args['sku'] ) ) { - $product->set_sku( wc_clean( $args['sku'] ) ); + if ( isset( $args[ FormElements::SKU ] ) ) { + $product->set_sku( wc_clean( $args[ FormElements::SKU ] ) ); } // Attributes. - if ( isset( $args['attributes'] ) ) { - $product->set_attributes( $args['attributes'] ); - } + $product = $this->save_product_attribute_data( $product, $args ); // Sales and prices. if ( in_array( $product->get_type(), [ 'variable', 'grouped' ], true ) ) { @@ -177,45 +180,45 @@ public function create( $args = [] ) { $product->set_price( '' ); } else { // Regular Price. - if ( isset( $args['regular_price'] ) ) { - $product->set_regular_price( $args['regular_price'] ); + if ( isset( $args[ FormElements::REGULAR_PRICE ] ) ) { + $product->set_regular_price( $args[ FormElements::REGULAR_PRICE ] ); } // Sale Price. - if ( isset( $args['sale_price'] ) ) { - $product->set_sale_price( $args['sale_price'] ); + if ( isset( $args[ FormElements::SALE_PRICE ] ) ) { + $product->set_sale_price( $args[ FormElements::SALE_PRICE ] ); } - if ( isset( $args['date_on_sale_from'] ) ) { - $product->set_date_on_sale_from( $args['date_on_sale_from'] ); + if ( isset( $args[ FormElements::DATE_ON_SALE_FROM ] ) ) { + $product->set_date_on_sale_from( $args[ FormElements::DATE_ON_SALE_FROM ] ); } - if ( isset( $args['date_on_sale_from_gmt'] ) ) { - $product->set_date_on_sale_from( $args['date_on_sale_from_gmt'] ? strtotime( $args['date_on_sale_from_gmt'] ) : null ); + if ( isset( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ) ) { + $product->set_date_on_sale_from( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ? strtotime( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ) : null ); } - if ( isset( $args['date_on_sale_to'] ) ) { - $product->set_date_on_sale_to( $args['date_on_sale_to'] ); + if ( isset( $args[ FormElements::DATE_ON_SALE_TO ] ) ) { + $product->set_date_on_sale_to( $args[ FormElements::DATE_ON_SALE_TO ] ); } - if ( isset( $args['date_on_sale_to_gmt'] ) ) { - $product->set_date_on_sale_to( $args['date_on_sale_to_gmt'] ? strtotime( $args['date_on_sale_to_gmt'] ) : null ); + if ( isset( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ) ) { + $product->set_date_on_sale_to( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ? strtotime( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ) : null ); } } // Product parent ID. - if ( isset( $args['parent_id'] ) ) { - $product->set_parent_id( $args['parent_id'] ); + if ( isset( $args[ FormElements::PARENT_ID ] ) ) { + $product->set_parent_id( $args[ FormElements::PARENT_ID ] ); } // Sold individually. - if ( isset( $args['sold_individually'] ) ) { - $product->set_sold_individually( $args['sold_individually'] ); + if ( isset( $args[ FormElements::SOLD_INDIVIDUALLY ] ) ) { + $product->set_sold_individually( $args[ FormElements::SOLD_INDIVIDUALLY ] ); } // Stock status; stock_status has priority over in_stock. - if ( isset( $args['stock_status'] ) ) { - $stock_status = $args['stock_status']; + if ( isset( $args[ FormElements::STOCK_STATUS ] ) ) { + $stock_status = $args[ FormElements::STOCK_STATUS ]; } else { $stock_status = $product->get_stock_status(); } @@ -223,13 +226,17 @@ public function create( $args = [] ) { // Stock data. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { // Manage stock. - if ( isset( $args['manage_stock'] ) ) { - $product->set_manage_stock( $args['manage_stock'] ); + if ( isset( $args[ FormElements::MANAGE_STOCK ] ) ) { + $product->set_manage_stock( $args[ FormElements::MANAGE_STOCK ] ); + } + + if ( isset( $args[ FormElements::LOW_STOCK_AMOUNT ] ) ) { + $product->set_low_stock_amount( $args[ FormElements::LOW_STOCK_AMOUNT ] ); } // Backorders. - if ( isset( $args['backorders'] ) ) { - $product->set_backorders( $args['backorders'] ); + if ( isset( $args[ FormElements::BACKORDERS ] ) ) { + $product->set_backorders( $args[ FormElements::BACKORDERS ] ); } if ( $product->is_type( 'grouped' ) ) { @@ -249,11 +256,11 @@ public function create( $args = [] ) { } // Stock quantity. - if ( isset( $args['stock_quantity'] ) ) { - $product->set_stock_quantity( wc_stock_amount( $args['stock_quantity'] ) ); - } elseif ( isset( $args['inventory_delta'] ) ) { + if ( isset( $args[ FormElements::STOCK_QUANTITY ] ) ) { + $product->set_stock_quantity( wc_stock_amount( $args[ FormElements::STOCK_QUANTITY ] ) ); + } elseif ( isset( $args[ FormElements::INVENTORY_DELTA ] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); - $stock_quantity += wc_stock_amount( $args['inventory_delta'] ); + $stock_quantity += wc_stock_amount( $args[ FormElements::INVENTORY_DELTA ] ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { @@ -270,9 +277,9 @@ public function create( $args = [] ) { $product = $this->maybe_update_stock_status( $product, $stock_status ); // Upsells. - if ( isset( $args['upsell_ids'] ) ) { + if ( isset( $args[ FormElements::UPSELL_IDS ] ) ) { $upsells = []; - $ids = $args['upsell_ids']; + $ids = $args[ FormElements::UPSELL_IDS ]; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { @@ -286,9 +293,9 @@ public function create( $args = [] ) { } // Cross sells. - if ( isset( $args['cross_sell_ids'] ) ) { + if ( isset( $args[ FormElements::CROSS_SELL_IDS ] ) ) { $crosssells = []; - $ids = $args['cross_sell_ids']; + $ids = $args[ FormElements::CROSS_SELL_IDS ]; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { @@ -302,47 +309,47 @@ public function create( $args = [] ) { } // Product categories. - if ( isset( $args['categories'] ) && is_array( $args['categories'] ) ) { - $product = $this->save_taxonomy_terms( $product, $args['categories'] ); + if ( isset( $args[ FormElements::CATEGORIES ] ) && is_array( $args[ FormElements::CATEGORIES ] ) ) { + $product = $this->save_taxonomy_terms( $product, $args[ FormElements::CATEGORIES ] ); } // Product tags. - if ( isset( $args['tags'] ) && is_array( $args['tags'] ) ) { - $product = $this->save_taxonomy_terms( $product, $args['tags'], 'tag' ); + if ( isset( $args[ FormElements::TAGS ] ) && is_array( $args[ FormElements::TAGS ] ) ) { + $product = $this->save_taxonomy_terms( $product, $args[ FormElements::TAGS ], 'tag' ); } // Downloadable. - if ( isset( $args['downloadable'] ) ) { - $product->set_downloadable( $args['downloadable'] ); + if ( isset( $args[ FormElements::DOWNLOADABLE ] ) ) { + $product->set_downloadable( $args[ FormElements::DOWNLOADABLE ] ); } // Downloadable options. if ( $product->get_downloadable() ) { // Downloadable files. - if ( isset( $args['downloads'] ) && is_array( $args['downloads'] ) ) { - $product = $this->save_downloadable_files( $product, $args['downloads'] ); + if ( isset( $args[ FormElements::DOWNLOADS ] ) && is_array( $args[ FormElements::DOWNLOADS ] ) ) { + $product = $this->save_downloadable_files( $product, $args[ FormElements::DOWNLOADS ] ); } // Download limit. - if ( isset( $args['download_limit'] ) ) { - $product->set_download_limit( $args['download_limit'] ); + if ( isset( $args[ FormElements::DOWNLOAD_LIMIT ] ) ) { + $product->set_download_limit( $args[ FormElements::DOWNLOAD_LIMIT ] ); } // Download expiry. - if ( isset( $args['download_expiry'] ) ) { - $product->set_download_expiry( $args['download_expiry'] ); + if ( isset( $args[ FormElements::DOWNLOAD_EXPIRY ] ) ) { + $product->set_download_expiry( $args[ FormElements::DOWNLOAD_EXPIRY ] ); } } // Product url and button text for external products. if ( $product->is_type( 'external' ) ) { - if ( isset( $args['external_url'] ) ) { - $product->set_product_url( $args['external_url'] ); + if ( isset( $args[ FormElements::EXTERNAL_URL ] ) ) { + $product->set_product_url( $args[ FormElements::EXTERNAL_URL ] ); } - if ( isset( $args['button_text'] ) ) { - $product->set_button_text( $args['button_text'] ); + if ( isset( $args[ FormElements::BUTTON_TEXT ] ) ) { + $product->set_button_text( $args[ FormElements::BUTTON_TEXT ] ); } } @@ -352,51 +359,95 @@ public function create( $args = [] ) { } // Set children for a grouped product. - if ( $product->is_type( 'grouped' ) && isset( $args['grouped_products'] ) ) { - $product->set_children( $args['grouped_products'] ); + if ( $product->is_type( 'grouped' ) && isset( $args[ FormElements::GROUP_PRODUCTS ] ) ) { + $product->set_children( $args[ FormElements::GROUP_PRODUCTS ] ); } // Set featured image id - if ( ! empty( $args['featured_image_id'] ) ) { - $product->set_image_id( $args['featured_image_id'] ); + if ( ! empty( $args[ FormElements::FEATURED_IMAGE_ID ] ) ) { + $product->set_image_id( $args[ FormElements::FEATURED_IMAGE_ID ] ); } // Set gallery image ids - if ( ! empty( $args['gallery_image_ids'] ) ) { - $product->set_gallery_image_ids( $args['gallery_image_ids'] ); + if ( ! empty( $args[ FormElements::GALLERY_IMAGE_IDS ] ) ) { + $product->set_gallery_image_ids( $args[ FormElements::GALLERY_IMAGE_IDS ] ); } // Allow set meta_data. - if ( ! empty( $args['meta_data'] ) && is_array( $args['meta_data'] ) ) { - foreach ( $args['meta_data'] as $meta ) { + if ( ! empty( $args[ FormElements::META_DATA ] ) && is_array( $args[ FormElements::META_DATA ] ) ) { + foreach ( $args[ FormElements::META_DATA ] as $meta ) { $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } - if ( ! empty( $args['date_created'] ) ) { - $date = rest_parse_date( $args['date_created'] ); + // Set total sales for newly created product + if ( ! $is_update ) { + // Set date created. + if ( ! empty( $args[ FormElements::DATE_CREATED ] ) ) { + $date = rest_parse_date( $args[ FormElements::DATE_CREATED ] ); - if ( $date ) { - $product->set_date_created( $date ); + if ( $date ) { + $product->set_date_created( $date ); + } } - } - if ( ! empty( $args['date_created_gmt'] ) ) { - $date = rest_parse_date( $args['date_created_gmt'], true ); + // set date created gmt + if ( ! empty( $args[ FormElements::DATE_CREATED_GMT ] ) ) { + $date = rest_parse_date( $args[ FormElements::DATE_CREATED_GMT ], true ); - if ( $date ) { - $product->set_date_created( $date ); + if ( $date ) { + $product->set_date_created( $date ); + } } - } - // Set total sales for newly created product - if ( ! empty( $id ) ) { + // Set total sales to zero $product->set_total_sales( 0 ); } $product_id = $product->save(); - return wc_get_product( $product_id ); + //call dokan hooks + if ( ! $is_update ) { + do_action( 'dokan_new_product_added', $product_id, [] ); + } else { + do_action( 'dokan_product_updated', $product_id, [] ); + } + + return $this->get( $product_id ); + } + + /** + * Update product data + * + * @since 3.0.0 + * + * @return WC_Product|WP_Error|false + */ + public function update( $args = [] ) { + $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0; + + if ( empty( $id ) ) { + return new WP_Error( 'no-id-found', __( 'No product ID found for updating', 'dokan-lite' ), [ 'status' => 401 ] ); + } + + return $this->create( $args ); + } + + /** + * Delete product data + * + * @since 3.0.0 + * + * @return bool + */ + public function delete( $product_id, $force = false ) { + $product = $this->get( $product_id ); + + if ( ! $product ) { + return false; + } + + return $product->delete( [ 'force_delete' => $force ] ); } /** @@ -404,7 +455,7 @@ public function create( $args = [] ) { * * @since 3.0.0 * - * @param WC_Product $product Product instance. + * @param WC_Product $product Product instance. * @param \WP_REST_Request $request Request data. * * @return WC_Product @@ -460,39 +511,6 @@ public function save_default_attributes( $product, $request ) { return $product; } - /** - * Update product data - * - * @since 3.0.0 - * - * @return WC_Product|WP_Error|false - */ - public function update( $args = [] ) { - $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0; - - if ( empty( $id ) ) { - return new WP_Error( 'no-id-found', __( 'No product ID found for updating', 'dokan-lite' ), [ 'status' => 401 ] ); - } - - return $this->create( $args ); - } - - /** - * Delete product data - * - * @since 3.0.0 - * - * @return WC_Product|null|false - */ - public function delete( $product_id, $force = false ) { - $product = $this->get( $product_id ); - if ( $product ) { - $product->delete( [ 'force_delete' => $force ] ); - } - - return $product; - } - /** * Save product shipping data. * @@ -503,42 +521,119 @@ public function delete( $product_id, $force = false ) { */ protected function save_product_shipping_data( $product, $data ) { // Virtual. - if ( isset( $data['virtual'] ) && true === $data['virtual'] ) { + if ( isset( $data[ FormElements::VIRTUAL ] ) && true === $data[ FormElements::VIRTUAL ] ) { $product->set_weight( '' ); $product->set_height( '' ); $product->set_length( '' ); $product->set_width( '' ); } else { - if ( isset( $data['weight'] ) ) { - $product->set_weight( $data['weight'] ); + if ( isset( $data[ FormElements::WEIGHT ] ) ) { + $product->set_weight( $data[ FormElements::WEIGHT ] ); } // Height. - if ( isset( $data['dimensions']['height'] ) ) { - $product->set_height( $data['dimensions']['height'] ); + if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_HEIGHT ] ) ) { + $product->set_height( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_HEIGHT ] ); } // Width. - if ( isset( $data['dimensions']['width'] ) ) { - $product->set_width( $data['dimensions']['width'] ); + if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_WIDTH ] ) ) { + $product->set_width( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_WIDTH ] ); } // Length. - if ( isset( $data['dimensions']['length'] ) ) { - $product->set_length( $data['dimensions']['length'] ); + if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_LENGTH ] ) ) { + $product->set_length( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_LENGTH ] ); } } // Shipping class. - if ( isset( $data['shipping_class'] ) ) { + if ( isset( $data[ FormElements::SHIPPING_CLASS ] ) ) { $data_store = $product->get_data_store(); - $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) ); + $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data[ FormElements::SHIPPING_CLASS ] ) ); $product->set_shipping_class_id( $shipping_class_id ); } return $product; } + /** + * Save product attribute data. + * + * @param WC_Product $product Product instance. + * @param array $args Product data's. + * + * @return WC_Product + */ + protected function save_product_attribute_data( $product, $args ) { + if ( isset( $args[ FormElements::ATTRIBUTES ] ) ) { + $attributes = []; + + foreach ( $args[ FormElements::ATTRIBUTES ] as $attribute ) { + $attribute_id = 0; + $attribute_name = ''; + + // Check ID for global attributes or name for product attributes. + if ( ! empty( $attribute[ FormElements::ATTRIBUTES_ID ] ) ) { + $attribute_id = absint( $attribute[ FormElements::ATTRIBUTES_ID ] ); + $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); + } elseif ( ! empty( $attribute[ FormElements::ATTRIBUTES_NAME ] ) ) { + $attribute_name = wc_clean( $attribute[ FormElements::ATTRIBUTES_NAME ] ); + } + + if ( ! $attribute_id && ! $attribute_name ) { + continue; + } + + if ( $attribute_id ) { + + if ( isset( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) { + $options = $attribute[ FormElements::ATTRIBUTES_OPTIONS ]; + + if ( ! is_array( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) { + // Text based attributes - Posted values are term names. + $options = explode( WC_DELIMITER, $options ); + } + + $values = array_map( 'wc_sanitize_term_text_based', $options ); + $values = array_filter( $values, 'strlen' ); + } else { + $values = []; + } + + if ( ! empty( $values ) ) { + // Add attribute to array, but don't set values. + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_id( $attribute_id ); + $attribute_object->set_name( $attribute_name ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) ? (string) absint( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) && $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) && $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) ? 1 : 0 ); + $attributes[] = $attribute_object; + } + } elseif ( isset( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) { + // Custom attribute - Add attribute to array and set the values. + if ( is_array( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) { + $values = $attribute[ FormElements::ATTRIBUTES_OPTIONS ]; + } else { + $values = explode( WC_DELIMITER, $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ); + } + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_name( $attribute_name ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) ? (string) absint( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) && $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) && $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) ? 1 : 0 ); + $attributes[] = $attribute_object; + } + } + $product->set_attributes( $attributes ); + } + + return $product; + } + /** * Save taxonomy terms. * @@ -549,10 +644,12 @@ protected function save_product_shipping_data( $product, $data ) { * @return WC_Product */ protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { + $term_ids = wp_list_pluck( $terms, 'id' ); + if ( 'cat' === $taxonomy ) { - $product->set_category_ids( $terms ); + $product->set_category_ids( $term_ids ); } elseif ( 'tag' === $taxonomy ) { - $product->set_tag_ids( $terms ); + $product->set_tag_ids( $term_ids ); } return $product; @@ -561,8 +658,8 @@ protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { /** * Save downloadable files. * - * @param WC_Product $product Product instance. - * @param array $downloads Downloads data. + * @param WC_Product $product Product instance. + * @param array $downloads Downloads data. * * @return WC_Product */ @@ -595,10 +692,7 @@ protected function save_downloadable_files( $product, $downloads ) { * @return mixed */ protected function maybe_update_stock_status( $product, $stock_status ) { - if ( $product->is_type( 'external' ) ) { - // External products are always in stock. - $product->set_stock_status( 'instock' ); - } elseif ( isset( $stock_status ) ) { + if ( isset( $stock_status ) ) { if ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) { // Stock status is determined by children. foreach ( $product->get_children() as $child_id ) { @@ -617,6 +711,37 @@ protected function maybe_update_stock_status( $product, $stock_status ) { return $product; } + /** + * Prepare downloads for save. + * + * @since DOKAN_SINCE + * + * @param array $file_names File names. + * @param array $file_urls File urls. + * @param array $file_hashes File hashes. + * + * @return array + */ + public function prepare_downloads( $file_names, $file_urls, $file_hashes ) { + $downloads = []; + + if ( ! empty( $file_urls ) ) { + $file_url_size = count( $file_urls ); + + for ( $i = 0; $i < $file_url_size; $i++ ) { + if ( ! empty( $file_urls[ $i ] ) ) { + $downloads[] = [ + 'name' => wc_clean( $file_names[ $i ] ), + 'file' => wp_unslash( trim( $file_urls[ $i ] ) ), + 'download_id' => wc_clean( $file_hashes[ $i ] ), + ]; + } + } + } + + return $downloads; + } + /** * Get featured products * @@ -681,7 +806,7 @@ public function best_selling( $args = [] ) { $args['orderby'] = 'meta_value_num'; $product_visibility_term_ids = wc_get_product_visibility_term_ids(); - $args['tax_query'][] = [ // phpcs:ignore + $args['tax_query'][] = [ // phpcs:ignore 'taxonomy' => 'product_visibility', 'field' => 'term_taxonomy_id', 'terms' => is_search() ? $product_visibility_term_ids['exclude-from-search'] : $product_visibility_term_ids['exclude-from-catalog'], diff --git a/includes/Product/functions.php b/includes/Product/functions.php index 8b2f44bdba..72e203de4c 100644 --- a/includes/Product/functions.php +++ b/includes/Product/functions.php @@ -10,158 +10,22 @@ /** * Dokan insert new product * - * @since 2.5.1 + * @since 2.5.1 * * @param array $args * - * @return int|bool|WP_Error + * @deprecated DOKAN_SINCE + * + * @return \WP_Error|\WC_Product */ function dokan_save_product( $args ) { - $defaults = [ - 'post_title' => '', - 'post_content' => '', - 'post_excerpt' => '', - 'post_status' => '', - 'post_type' => 'product', - 'product_tag' => [], - '_visibility' => 'visible', - ]; - - $data = wp_parse_args( $args, $defaults ); - - if ( empty( $data['post_title'] ) ) { - return new WP_Error( 'no-title', __( 'Please enter product title', 'dokan-lite' ) ); - } - - if ( ! isset( $data['chosen_product_cat'] ) ) { - if ( Helper::product_category_selection_is_single() ) { - if ( absint( $data['product_cat'] ) < 0 ) { - return new WP_Error( 'no-category', __( 'Please select a category', 'dokan-lite' ) ); - } - } else { - if ( ! isset( $data['product_cat'] ) && empty( $data['product_cat'] ) ) { - return new WP_Error( 'no-category', __( 'Please select at least one category', 'dokan-lite' ) ); - } - } - } elseif ( empty( $data['chosen_product_cat'] ) ) { - return new WP_Error( 'no-category', __( 'Please select a category', 'dokan-lite' ) ); - } - - $error = apply_filters( 'dokan_new_product_popup_args', '', $data ); - - if ( is_wp_error( $error ) ) { - return $error; - } - - $post_status = ! empty( $data['post_status'] ) ? sanitize_text_field( $data['post_status'] ) : dokan_get_default_product_status(); - - $post_arr = [ - 'post_type' => 'product', - 'post_status' => $post_status, - 'post_title' => sanitize_text_field( $data['post_title'] ), - 'post_content' => wp_kses_post( $data['post_content'] ), - 'post_excerpt' => wp_kses_post( $data['post_excerpt'] ), - ]; - - if ( ! empty( $data['ID'] ) ) { - $post_arr['ID'] = absint( $data['ID'] ); - - if ( ! dokan_is_product_author( $post_arr['ID'] ) ) { - return new WP_Error( 'not-own', __( 'Sorry, You can not modify another vendor\'s product !', 'dokan-lite' ) ); - } - - $is_updating = true; - } else { - $is_updating = false; - } - - $post_arr = apply_filters( 'dokan_insert_product_post_data', $post_arr, $data ); - - $post_data = [ - 'id' => $is_updating ? $post_arr['ID'] : '', - 'name' => $post_arr['post_title'], - 'type' => ! empty( $data['product_type'] ) ? $data['product_type'] : 'simple', - 'description' => $post_arr['post_content'], - 'short_description' => $post_arr['post_excerpt'], - 'status' => $post_status, - ]; - - if ( ! isset( $data['chosen_product_cat'] ) ) { - if ( Helper::product_category_selection_is_single() ) { - $cat_ids[] = $data['product_cat']; - } else { - if ( ! empty( $data['product_cat'] ) ) { - $cat_ids = array_map( 'absint', (array) $data['product_cat'] ); - } - } - $post_data['categories'] = $cat_ids; - } - - if ( isset( $data['feat_image_id'] ) ) { - $post_data['featured_image_id'] = ! empty( $data['feat_image_id'] ) ? absint( $data['feat_image_id'] ) : ''; - } - - if ( isset( $data['product_image_gallery'] ) ) { - $post_data['gallery_image_ids'] = ! empty( $data['product_image_gallery'] ) ? array_filter( explode( ',', wc_clean( $data['product_image_gallery'] ) ) ) : []; - } - - if ( isset( $data['product_tag'] ) ) { - /** - * Filter of maximun a vendor can add tags. - * - * @since 3.3.7 - * - * @param integer default -1 - */ - $maximum_tags_select_length = apply_filters( 'dokan_product_tags_select_max_length', -1 ); - - // Setting limitation for how many product tags that vendor can input. - if ( $maximum_tags_select_length !== -1 && count( $data['product_tag'] ) !== 0 && count( $data['product_tag'] ) > $maximum_tags_select_length ) { - /* translators: %s: maximum tag length */ - return new WP_Error( 'tags-limit', sprintf( __( 'You can only select %s tags', 'dokan-lite' ), number_format_i18n( $maximum_tags_select_length ) ) ); - } - - $post_data['tags'] = array_map( 'absint', (array) $data['product_tag'] ); - } - - if ( isset( $data['_regular_price'] ) ) { - $post_data['regular_price'] = $data['_regular_price'] === '' ? '' : wc_format_decimal( $data['_regular_price'] ); - } - - if ( isset( $data['_sale_price'] ) ) { - $post_data['sale_price'] = wc_format_decimal( $data['_sale_price'] ); - } - - if ( isset( $data['_sale_price_dates_from'] ) ) { - $post_data['date_on_sale_from'] = wc_clean( $data['_sale_price_dates_from'] ); - } - - if ( isset( $data['_sale_price_dates_to'] ) ) { - $post_data['date_on_sale_to'] = wc_clean( $data['_sale_price_dates_to'] ); - } - - if ( isset( $data['_visibility'] ) && array_key_exists( $data['_visibility'], dokan_get_product_visibility_options() ) ) { - $post_data['catalog_visibility'] = sanitize_text_field( $data['_visibility'] ); - } - - $product = dokan()->product->create( $post_data ); - - if ( $product ) { - $chosen_cat = Helper::product_category_selection_is_single() ? [ reset( $data['chosen_product_cat'] ) ] : $data['chosen_product_cat']; - Helper::set_object_terms_from_chosen_categories( $product->get_id(), $chosen_cat ); - } + wc_deprecated_function( __FUNCTION__, 'DOKAN_SINCE', 'dokan()->product->create()' ); - if ( ! $is_updating ) { - do_action( 'dokan_new_product_added', $product->get_id(), $data ); - } else { - do_action( 'dokan_product_updated', $product->get_id(), $data ); + try { + return dokan()->product->create( $args ); + } catch ( Exception $e ) { + return new WP_Error( $e->getCode(), $e->getMessage() ); } - - if ( $product ) { - return $product->get_id(); - } - - return false; } /** @@ -172,178 +36,163 @@ function dokan_save_product( $args ) { * @return void */ function dokan_product_output_variations() { - global $post, $wpdb; + global $post, $wpdb, $product_object; - // Get attributes - $attributes = maybe_unserialize( get_post_meta( $post->ID, '_product_attributes', true ) ); + $product_object = wc_get_product( $post->ID ); - // See if any are set - $variation_attribute_found = false; - - if ( $attributes ) { - foreach ( $attributes as $attribute ) { - if ( ! empty( $attribute['is_variation'] ) ) { - $variation_attribute_found = true; - break; - } - } - } + /* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */ + $variation_attributes = array_filter( + $product_object->get_attributes(), function ( $attribute ) { + return true === $attribute->get_variation(); + } + ); + $default_attributes = $product_object->get_default_attributes(); + $variations_count = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_count', $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'product_variation' AND post_status IN ('publish', 'private', 'pending')", $post->ID ) ), $post->ID ) ); + $variations_per_page = absint( apply_filters( 'dokan_product_variations_per_page', 15 ) ); + $variations_total_pages = ceil( $variations_count / $variations_per_page ); - $variations_count = absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'product_variation' AND post_status IN ('publish', 'private', 'pending')", $post->ID ) ) ); - $variations_per_page = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_per_page', 15 ) ); - $variations_total_pages = ceil( $variations_count / $variations_per_page ); ?> + // See if any are set + $variation_attribute_found = count( $variation_attributes ) > 0; + ?>
- Attributes section', 'dokan-lite' ), [ 'strong' => [] ] ); ?> -
-