From 0b75acf6668b358d02a48aea689007bc2b700257 Mon Sep 17 00:00:00 2001 From: Nurul Umbhiya Date: Fri, 10 Nov 2023 10:50:58 +0600 Subject: [PATCH] New: Product Review Email Template For Vendors (#2071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 fix(Hooks.php): remove unnecessary indentation and reorganize action hooks for better readability ✨ feat(Hooks.php): add support for filtering product review notification recipients to send to admin instead of vendor * ✨ feat(emails): add VendorProductReview email template and class 📦 Add new file `VendorProductReview.php` under `includes/Emails` directory to handle the VendorProductReview email functionality. 📦 Add new file `vendor-product-review.php` under `templates/emails/plain` directory to provide the plain text version of the VendorProductReview email template. 📦 Add new file `vendor-product-review.php` under `templates/emails` directory to provide the HTML version of the VendorProductReview email template. * 🔧 fix(Manager.php): add VendorProductReview email class to the list of available email classes in the Manager class ✨ feat(VendorProductReview.php): add VendorProductReview email class to send email to vendor when a product is reviewed 🔨 refactor(vendor-product-review.php): update plain text email template for vendor product review 🔨 refactor(vendor-product-review.php): update HTML email template for vendor product review * 🔥 refactor(VendorProductReview.php): remove unused {customer_email} placeholder to improve code readability and reduce complexity * 📦 chore(VendorProductReview.php): add missing class annotation for Dokan_Email_Vendor_Product_Review * 🐛 fix(VendorProductReview.php): set default placeholder values for subject and heading fields to improve user experience --- includes/Emails/Manager.php | 3 + includes/Emails/VendorProductReview.php | 229 ++++++++++++++++++ includes/Product/Hooks.php | 96 +++++--- .../emails/plain/vendor-product-review.php | 67 +++++ templates/emails/vendor-product-review.php | 61 +++++ 5 files changed, 426 insertions(+), 30 deletions(-) create mode 100644 includes/Emails/VendorProductReview.php create mode 100644 templates/emails/plain/vendor-product-review.php create mode 100644 templates/emails/vendor-product-review.php diff --git a/includes/Emails/Manager.php b/includes/Emails/Manager.php index 36b56d644f..4529c25124 100755 --- a/includes/Emails/Manager.php +++ b/includes/Emails/Manager.php @@ -92,6 +92,7 @@ public function load_dokan_emails( $wc_emails ) { $wc_emails['Dokan_Email_New_Order'] = new VendorNewOrder(); $wc_emails['Dokan_Email_Completed_Order'] = new VendorCompletedOrder(); $wc_emails['Dokan_Email_Reverse_Withdrawal_Invoice'] = new ReverseWithdrawalInvoice(); + $wc_emails['Dokan_Email_Vendor_Product_Review'] = new VendorProductReview(); return apply_filters( 'dokan_email_classes', $wc_emails ); } @@ -153,6 +154,8 @@ public function register_email_actions( $actions ) { 'dokan_withdraw_request_cancelled', 'dokan_pending_product_published_notification', 'dokan_trigger_contact_seller_mail', + 'wp_set_comment_status', + 'comment_post', ) ); diff --git a/includes/Emails/VendorProductReview.php b/includes/Emails/VendorProductReview.php new file mode 100644 index 0000000000..8ae7f380c2 --- /dev/null +++ b/includes/Emails/VendorProductReview.php @@ -0,0 +1,229 @@ +id = 'dokan_contact_seller'; + $this->title = __( 'Dokan Vendor Product Review', 'dokan-lite' ); + $this->description = __( 'After a product has been reviewed, an email is sent to the vendor containing information about the review. The email may include details such as the reviewer’s name, the product’s name and description, the review rating, and the review text. The email may also contain a link to the review page where the vendor can view the review and respond to it if necessary.', 'dokan-lite' ); + $this->template_html = 'emails/vendor-product-review.php'; + $this->template_plain = 'emails/plain/vendor-product-review.php'; + $this->template_base = DOKAN_DIR . '/templates/'; + $this->placeholders = [ + '{store_name}' => '', + '{product_name}' => '', + '{customer_name}' => '', + '{rating}' => '', + '{review_text}' => '', + '{review_link}' => '', + ]; + + // Triggers for this email + add_action( 'wp_set_comment_status', [ $this, 'trigger' ], 35, 1 ); + add_action( 'comment_post', [ $this, 'trigger' ], 35, 1 ); + + // Call parent constructor + parent::__construct(); + + // Other settings + $this->recipient = 'vendor@ofthe.product'; + } + + /** + * Get the email subject. + * + * @since DOKAN_SINCE + * + * @return string + */ + public function get_default_subject() { + return __( 'New Product Review Alert from {site_title}', 'dokan-lite' ); + } + + /** + * Get email heading. + * + * @since DOKAN_SINCE + * + * @return string + */ + public function get_default_heading() { + return __( 'New Product Review Alert From Your Store: {store_name} at - {site_title}', 'dokan-lite' ); + } + + /** + * Trigger this email. + * + * @since DOKAN_SINCE + * + * @param int $comment_id + * + * @return void + */ + public function trigger( $comment_id ) { + if ( ! $this->is_enabled() || ! $this->get_recipient() ) { + return; + } + + $comment = get_comment( $comment_id ); + + $product = wc_get_product( $comment->comment_post_ID ); + if ( ! $product ) { + // the review is not for a product + return; + } + + $this->setup_locale(); + + $this->from_email = get_option( 'admin_email' ); + + // get the vendor + $seller = dokan_get_vendor_by_product( $product->get_id() ); + + $this->placeholders['{store_name}'] = $seller->get_shop_name(); + $this->placeholders['{product_name}'] = $product->get_title(); + $this->placeholders['{customer_name}'] = $comment->comment_author; + $this->placeholders['{rating}'] = (int) get_comment_meta( $comment->comment_ID, 'rating', true ); + $this->placeholders['{review_text}'] = wp_specialchars_decode( $comment->comment_content ); + $this->placeholders['{review_link}'] = get_comment_link( $comment ); + $this->send( $seller->get_email(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() ); + $this->restore_locale(); + } + + /** + * Get the from address for outgoing emails. + * + * @since DOKAN_SINCE + * + * @return string + */ + public function get_from_address( $from_email = '' ) { + return $this->from_email; + } + + /** + * Get content html. + * + * @since DOKAN_SINCE + * + * @return string + */ + public function get_content_html() { + return wc_get_template_html( + $this->template_html, + [ + 'email_heading' => $this->get_heading(), + 'additional_content' => $this->get_additional_content(), + 'sent_to_admin' => false, + 'plain_text' => false, + 'email' => $this, + 'data' => $this->placeholders, + ], + 'dokan/', $this->template_base + ); + } + + /** + * Get content plain. + * + * @since DOKAN_SINCE + * + * @return string + */ + public function get_content_plain() { + return wc_get_template_html( + $this->template_plain, + [ + 'email_heading' => $this->get_heading(), + 'additional_content' => $this->get_additional_content(), + 'sent_to_admin' => false, + 'plain_text' => true, + 'email' => $this, + 'data' => $this->placeholders, + ], + 'dokan/', $this->template_base + ); + } + + /** + * Initialize settings form fields. + * + * @since DOKAN_SINCE + */ + public function init_form_fields() { + /* translators: %s: list of placeholders */ + $placeholder_text = sprintf( __( 'Available placeholders: %s', 'dokan-lite' ), '' . implode( ', ', array_keys( $this->placeholders ) ) . '' ); + $this->form_fields = [ + 'enabled' => [ + 'title' => __( 'Enable/Disable', 'dokan-lite' ), + 'type' => 'checkbox', + 'label' => __( 'Enable this email notification', 'dokan-lite' ), + 'default' => 'yes', + ], + + 'subject' => [ + 'title' => __( 'Subject', 'dokan-lite' ), + 'type' => 'text', + 'desc_tip' => true, + 'description' => $placeholder_text, + 'placeholder' => $this->get_default_subject(), + 'default' => $this->get_default_subject(), + ], + 'heading' => [ + 'title' => __( 'Email heading', 'dokan-lite' ), + 'type' => 'text', + 'desc_tip' => true, + 'description' => $placeholder_text, + 'placeholder' => $this->get_default_heading(), + 'default' => $this->get_default_heading(), + ], + 'additional_content' => [ + 'title' => __( 'Additional content', 'dokan-lite' ), + 'description' => __( 'Text to appear below the main email content.', 'dokan-lite' ) . ' ' . $placeholder_text, + 'css' => 'width:400px; height: 75px;', + 'placeholder' => __( 'N/A', 'dokan-lite' ), + 'type' => 'textarea', + 'default' => $this->get_default_additional_content(), + 'desc_tip' => false, + ], + 'email_type' => [ + 'title' => __( 'Email type', 'dokan-lite' ), + 'type' => 'select', + 'description' => __( 'Choose which format of email to send.', 'dokan-lite' ), + 'default' => 'html', + 'class' => 'email_type wc-enhanced-select', + 'options' => $this->get_email_type_options(), + 'desc_tip' => true, + ], + ]; + } +} diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php index 0e67bd006d..477038bfec 100644 --- a/includes/Product/Hooks.php +++ b/includes/Product/Hooks.php @@ -24,24 +24,9 @@ public function __construct() { add_action( 'dokan_bulk_product_status_change', [ $this, 'bulk_product_delete' ], 10, 2 ); add_action( 'dokan_store_profile_frame_after', [ $this, 'store_products_orderby' ], 30, 2 ); add_action( 'wp_ajax_dokan_store_product_search_action', [ $this, 'store_product_search_action' ], 10, 2 ); - add_action( - 'wp_ajax_nopriv_dokan_store_product_search_action', [ - $this, - 'store_product_search_action', - ], 10, 2 - ); - add_action( - 'woocommerce_product_quick_edit_save', [ - $this, - 'update_category_data_for_bulk_and_quick_edit', - ], 10, 1 - ); - add_action( - 'woocommerce_product_bulk_edit_save', [ - $this, - 'update_category_data_for_bulk_and_quick_edit', - ], 10, 1 - ); + add_action( 'wp_ajax_nopriv_dokan_store_product_search_action', [ $this, 'store_product_search_action' ], 10, 2 ); + add_action( 'woocommerce_product_quick_edit_save', [ $this, 'update_category_data_for_bulk_and_quick_edit' ], 10, 1 ); + add_action( 'woocommerce_product_bulk_edit_save', [ $this, 'update_category_data_for_bulk_and_quick_edit' ], 10, 1 ); 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 ); @@ -50,6 +35,9 @@ public function __construct() { // Remove product type filter if pro not exists. add_filter( 'dokan_product_listing_filter_args', [ $this, 'remove_product_type_filter' ] ); + // product review action hook + add_action( 'comment_notification_recipients', [ $this, 'product_review_notification_recipients' ], 10, 2 ); + // Init Product Cache Class new VendorStoreInfo(); new ProductCache(); @@ -58,8 +46,8 @@ public function __construct() { /** * Callback for Ajax Action Initialization * - * @return void * @since DOKAN_LITE_SINCE + * @return void */ public function store_product_search_action() { if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'dokan_store_product_search_nonce' ) ) { @@ -179,8 +167,8 @@ public function store_product_search_action() { /** * Output the store product sorting options * - * @return void * @since DOKAN_LITE_SINCE + * @return void */ public function store_products_orderby() { $store_products = dokan_get_option( 'store_products', 'dokan_appearance' ); @@ -196,11 +184,11 @@ public function store_products_orderby() {
+ placeholder="" autocomplete="off" + data-store_id="">
+ value="">