Changeset 3457384
- Timestamp:
- 02/09/2026 07:31:06 PM (7 weeks ago)
- Location:
- review-next-for-woocommerce
- Files:
-
- 155 added
- 19 edited
-
tags/1.0.3 (added)
-
tags/1.0.3/CHANGELOG.md (added)
-
tags/1.0.3/LICENSE.txt (added)
-
tags/1.0.3/README.md (added)
-
tags/1.0.3/README.txt (added)
-
tags/1.0.3/admin (added)
-
tags/1.0.3/admin/class-revnextwoo-admin.php (added)
-
tags/1.0.3/admin/class-revnextwoo-advanced-settings.php (added)
-
tags/1.0.3/admin/class-revnextwoo-analytics.php (added)
-
tags/1.0.3/admin/class-revnextwoo-cas-list-table.php (added)
-
tags/1.0.3/admin/class-revnextwoo-email-settings.php (added)
-
tags/1.0.3/admin/class-revnextwoo-social-settings.php (added)
-
tags/1.0.3/admin/class-revnextwoo-style-settings.php (added)
-
tags/1.0.3/admin/css (added)
-
tags/1.0.3/admin/css/freemium.css (added)
-
tags/1.0.3/admin/css/revnextwoo-admin.css (added)
-
tags/1.0.3/admin/images (added)
-
tags/1.0.3/admin/images/five_star.png (added)
-
tags/1.0.3/admin/images/full_star.png (added)
-
tags/1.0.3/admin/images/half_star.png (added)
-
tags/1.0.3/admin/images/no_rating.png (added)
-
tags/1.0.3/admin/images/review-next.png (added)
-
tags/1.0.3/admin/index.php (added)
-
tags/1.0.3/admin/js (added)
-
tags/1.0.3/admin/js/admin-tab.js (added)
-
tags/1.0.3/admin/js/revnextwoo-admin.js (added)
-
tags/1.0.3/admin/js/revnextwoo-pending-bell.js (added)
-
tags/1.0.3/admin/partials (added)
-
tags/1.0.3/admin/partials/analytics-dashboard.php (added)
-
tags/1.0.3/admin/partials/review-plugin-admin-display.php (added)
-
tags/1.0.3/admin/review-dashboard.php (added)
-
tags/1.0.3/assets (added)
-
tags/1.0.3/assets/css (added)
-
tags/1.0.3/assets/css/revnextwoo-styles.css (added)
-
tags/1.0.3/assets/images (added)
-
tags/1.0.3/assets/images/analytics-preview.png (added)
-
tags/1.0.3/assets/js (added)
-
tags/1.0.3/assets/js/revnextwoo-notifications.js (added)
-
tags/1.0.3/assets/js/revnextwoo-styles.js (added)
-
tags/1.0.3/assets/js/vendor (added)
-
tags/1.0.3/assets/js/vendor/script.js (added)
-
tags/1.0.3/bin (added)
-
tags/1.0.3/bin/make-zip.php (added)
-
tags/1.0.3/inc (added)
-
tags/1.0.3/inc/revnextwoo-product-views-table.php (added)
-
tags/1.0.3/includes (added)
-
tags/1.0.3/includes/class-revnextwoo-activator.php (added)
-
tags/1.0.3/includes/class-revnextwoo-admin-menus.php (added)
-
tags/1.0.3/includes/class-revnextwoo-coupon-generator.php (added)
-
tags/1.0.3/includes/class-revnextwoo-deactivator.php (added)
-
tags/1.0.3/includes/class-revnextwoo-dependency-checker.php (added)
-
tags/1.0.3/includes/class-revnextwoo-email-handler.php (added)
-
tags/1.0.3/includes/class-revnextwoo-email-integrations.php (added)
-
tags/1.0.3/includes/class-revnextwoo-i18n.php (added)
-
tags/1.0.3/includes/class-revnextwoo-loader.php (added)
-
tags/1.0.3/includes/class-revnextwoo-notification-bell.php (added)
-
tags/1.0.3/includes/class-revnextwoo-qa-db.php (added)
-
tags/1.0.3/includes/class-revnextwoo.php (added)
-
tags/1.0.3/includes/index.php (added)
-
tags/1.0.3/includes/revnextwoo-helper.php (added)
-
tags/1.0.3/index.php (added)
-
tags/1.0.3/public (added)
-
tags/1.0.3/public/class-revnextwoo-public.php (added)
-
tags/1.0.3/public/css (added)
-
tags/1.0.3/public/css/revnextwoo-plugin.css (added)
-
tags/1.0.3/public/css/vendor (added)
-
tags/1.0.3/public/css/vendor/all.min.css (added)
-
tags/1.0.3/public/css/vendor/jquery.fancybox.min.css (added)
-
tags/1.0.3/public/css/webfonts (added)
-
tags/1.0.3/public/css/webfonts/fa-brands-400.eot (added)
-
tags/1.0.3/public/css/webfonts/fa-brands-400.svg (added)
-
tags/1.0.3/public/css/webfonts/fa-brands-400.ttf (added)
-
tags/1.0.3/public/css/webfonts/fa-brands-400.woff (added)
-
tags/1.0.3/public/css/webfonts/fa-brands-400.woff2 (added)
-
tags/1.0.3/public/css/webfonts/fa-regular-400.eot (added)
-
tags/1.0.3/public/css/webfonts/fa-regular-400.svg (added)
-
tags/1.0.3/public/css/webfonts/fa-regular-400.ttf (added)
-
tags/1.0.3/public/css/webfonts/fa-regular-400.woff (added)
-
tags/1.0.3/public/css/webfonts/fa-regular-400.woff2 (added)
-
tags/1.0.3/public/css/webfonts/fa-solid-900.eot (added)
-
tags/1.0.3/public/css/webfonts/fa-solid-900.svg (added)
-
tags/1.0.3/public/css/webfonts/fa-solid-900.ttf (added)
-
tags/1.0.3/public/css/webfonts/fa-solid-900.woff (added)
-
tags/1.0.3/public/css/webfonts/fa-solid-900.woff2 (added)
-
tags/1.0.3/public/images (added)
-
tags/1.0.3/public/images/answer.png (added)
-
tags/1.0.3/public/images/problem.png (added)
-
tags/1.0.3/public/images/question.png (added)
-
tags/1.0.3/public/images/review-plugin-user.jpg (added)
-
tags/1.0.3/public/index.php (added)
-
tags/1.0.3/public/js (added)
-
tags/1.0.3/public/js/revnextwoo-filter-review.js (added)
-
tags/1.0.3/public/js/revnextwoo-form-validation.js (added)
-
tags/1.0.3/public/js/revnextwoo-main.js (added)
-
tags/1.0.3/public/js/revnextwoo-questions-pagination.js (added)
-
tags/1.0.3/public/js/revnextwoo-review-layout.js (added)
-
tags/1.0.3/public/js/revnextwoo-reviews.js (added)
-
tags/1.0.3/public/js/revnextwoo-social-share.js (added)
-
tags/1.0.3/public/js/vendor (added)
-
tags/1.0.3/public/js/vendor/jquery.fancybox.min.js (added)
-
tags/1.0.3/public/partials (added)
-
tags/1.0.3/public/partials/revnextwoo-public-display.php (added)
-
tags/1.0.3/review-next-for-woocommerce.php (added)
-
tags/1.0.3/template (added)
-
tags/1.0.3/template/admin (added)
-
tags/1.0.3/template/admin/all-question.php (added)
-
tags/1.0.3/template/admin/coupon-settings.php (added)
-
tags/1.0.3/template/admin/email-reminder.php (added)
-
tags/1.0.3/template/admin/top-order-customer.php (added)
-
tags/1.0.3/template/emails (added)
-
tags/1.0.3/template/frontend (added)
-
tags/1.0.3/template/frontend/_review-item-render.php (added)
-
tags/1.0.3/template/frontend/average-rating.php (added)
-
tags/1.0.3/template/frontend/comment-reply-display.php (added)
-
tags/1.0.3/template/frontend/product-review-show.php (added)
-
tags/1.0.3/template/frontend/review-success-message.php (added)
-
tags/1.0.3/template/frontend/user-question.php (added)
-
tags/1.0.3/template/frontend/waiting-for-review.php (added)
-
tags/1.0.3/uninstall.php (added)
-
trunk/CHANGELOG.md (modified) (2 diffs)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/admin/class-revnextwoo-admin.php (modified) (4 diffs)
-
trunk/admin/class-revnextwoo-advanced-settings.php (added)
-
trunk/admin/class-revnextwoo-analytics.php (added)
-
trunk/admin/class-revnextwoo-cas-list-table.php (added)
-
trunk/admin/class-revnextwoo-email-settings.php (added)
-
trunk/admin/class-revnextwoo-social-settings.php (added)
-
trunk/admin/class-revnextwoo-style-settings.php (added)
-
trunk/admin/css/freemium.css (added)
-
trunk/admin/css/revnextwoo-admin.css (modified) (3 diffs)
-
trunk/admin/images/review-next.png (added)
-
trunk/admin/js/admin-tab.js (added)
-
trunk/admin/js/revnextwoo-admin.js (modified) (3 diffs)
-
trunk/admin/js/revnextwoo-pending-bell.js (added)
-
trunk/admin/partials/analytics-dashboard.php (added)
-
trunk/admin/review-dashboard.php (added)
-
trunk/assets/css/revnextwoo-styles.css (modified) (7 diffs)
-
trunk/assets/images (added)
-
trunk/assets/images/analytics-preview.png (added)
-
trunk/assets/js/revnextwoo-notifications.js (added)
-
trunk/assets/js/vendor (added)
-
trunk/assets/js/vendor/script.js (added)
-
trunk/bin (added)
-
trunk/bin/make-zip.php (added)
-
trunk/inc (added)
-
trunk/inc/revnextwoo-product-views-table.php (added)
-
trunk/includes/class-revnextwoo-activator.php (modified) (1 diff)
-
trunk/includes/class-revnextwoo-admin-menus.php (added)
-
trunk/includes/class-revnextwoo-coupon-generator.php (added)
-
trunk/includes/class-revnextwoo-dependency-checker.php (added)
-
trunk/includes/class-revnextwoo-email-handler.php (added)
-
trunk/includes/class-revnextwoo-email-integrations.php (added)
-
trunk/includes/class-revnextwoo-notification-bell.php (added)
-
trunk/includes/class-revnextwoo-qa-db.php (added)
-
trunk/includes/class-revnextwoo.php (modified) (1 diff)
-
trunk/includes/revnextwoo-helper.php (added)
-
trunk/public/class-revnextwoo-public.php (modified) (9 diffs)
-
trunk/public/css/revnextwoo-plugin.css (modified) (28 diffs)
-
trunk/public/js/revnextwoo-filter-review.js (modified) (2 diffs)
-
trunk/public/js/revnextwoo-form-validation.js (modified) (1 diff)
-
trunk/public/js/revnextwoo-main.js (modified) (3 diffs)
-
trunk/public/js/revnextwoo-questions-pagination.js (added)
-
trunk/public/js/revnextwoo-review-layout.js (added)
-
trunk/public/js/revnextwoo-reviews.js (added)
-
trunk/public/js/revnextwoo-social-share.js (added)
-
trunk/review-next-for-woocommerce.php (modified) (35 diffs)
-
trunk/template/admin/all-question.php (modified) (5 diffs)
-
trunk/template/admin/coupon-settings.php (added)
-
trunk/template/admin/email-reminder.php (modified) (1 diff)
-
trunk/template/admin/top-order-customer.php (modified) (2 diffs)
-
trunk/template/emails (added)
-
trunk/template/frontend/_review-item-render.php (added)
-
trunk/template/frontend/average-rating.php (modified) (18 diffs)
-
trunk/template/frontend/user-question.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
review-next-for-woocommerce/trunk/CHANGELOG.md
r3317206 r3457384 3 3 All notable changes to the Review Next for WooCommerce plugin will be documented in this file. 4 4 5 ## [1.0.3] - 2026-02-10 6 ### Added 7 - **Pro Release:** Launched Review Next for WooCommerce Pro with premium features and functionality. 8 - **New:** Added support for Video Reviews! Customers can now upload MP4/WebM videos 9 - **New:** Implemented Automated Coupon Generation system to reward reviewers 10 - **New:** Added Advanced Review Filtering and Sorting options. 11 - **New:** Implemented Review Moderation Dashboard with bulk actions (Pro feature). 12 - **Enhancement:** Improved frontend review display and grid layout. 13 - **Enhancement:** Enhanced "Verified Owner" logic and display. 14 - **Enhancement:** Performance optimization for high-volume reviews. 15 - **Enhancement:** Improved UI/UX across the plugin interface. 16 17 ### Fixed 18 - Resolved compatibility issues with WooCommerce 8.x+ 19 - Fixed review display issues on mobile devices 20 - General bug fixes and improvements 21 22 ### Updated 23 - Updated plugin version to 1.0.3 24 - Updated documentation with Pro feature information 25 5 26 ## [1.0.2] - 2025-06-25 6 ### Added7 - Initial version bump with version number update8 - Created CHANGELOG.md to track all future changes9 10 ### Changed11 - Updated plugin version to 1.0.212 13 27 ### Fixed 14 28 - Fixed issue with review display on the frontend … … 19 33 - General bug fixes and improvements 20 34 21 ### Security22 - No security updates in this release23 24 35 --- 25 36 *Note: This is the initial changelog entry. Future updates will document all notable changes.* -
review-next-for-woocommerce/trunk/README.txt
r3317215 r3457384 1 1 === Review Next for WooCommerce === 2 2 Contributors: nazmul111 3 Tags: woocommerce, reviews, product reviews, customer reviews, image reviews3 Tags: woocommerce, reviews, product reviews, customer reviews, photo reviews, video reviews, review reminders, coupons 4 4 Requires at least: 5.0 5 5 Tested up to: 6.8.1 6 6 Requires PHP: 7.2 7 Stable tag: 1.0. 27 Stable tag: 1.0.3 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Advanced review system for WooCommerce products, allowing image uploads and enhanced display.11 Boost sales with Photo & Video reviews, automated Review Reminder emails, and Coupon incentives. The ultimate social proof solution for WooCommerce. 12 12 13 13 == Description == 14 14 15 Review Next for WooCommerce enhances the WooCommerce review system by allowing customers to upload images with their reviews, providing a richer user experience. This plugin offers advanced features for managing and displaying customer feedback, helping you build trust and social proof for your products.15 **Review Next for WooCommerce** is the ultimate social proof solution designed to transform how you collect and display customer feedback. In a world where visual trust drives sales, standard text reviews just aren’t enough. Review Next empowers your customers to share their real-life experiences through **Photo and Video Reviews**, creating authentic social proof that instantly builds trust with potential buyers. 16 16 17 Key Features: 17 But we don't just help you *display* reviews—we help you *get* them. With our powerful **Automated Review Reminders** and **Coupon Incentives**, you can turn every purchase into a 5-star opportunity. Automatically send follow-up emails requesting a review, and reward customers with a unique coupon code when they leave one. It’s a seamless loop that drives engagement, loyalty, and repeat sales. 18 18 19 * **Image Uploads with Reviews:** Customers can attach images to their product reviews. 20 * **WooCommerce Integration:** Seamlessly integrates with WooCommerce product pages. 21 * **Review Management:** Enhanced options for moderating and managing reviews. 22 * **Customizable Display:** Control how reviews are displayed on your site. 23 * **Schema Markup:** Optimized for SEO with appropriate review schema. 24 * **AJAX Powered:** Smooth review submission and display without page reloads. 19 Packed with premium features like smart filtering, voting systems (Helpful/Not Helpful), verified buyer badges, and a fully customizable design, Review Next looks stunning on any theme. Whether you are a small store or a large brand, Review Next gives you the tools to leverage the voice of your customers and skyrocket your conversion rates. 20 21 ### 🌟 Why Choose Review Next? 22 23 * **📸 Visual Social Proof:** Let customers upload **Photos and Videos** with their reviews. Seeing is believing! 24 * **🎁 Drive More Reviews:** Automatically generate and send **Discount Coupons** to customers who leave a review. 25 * **📧 Automated Emails:** Send fully customizable **Review Reminder Emails** to customers after purchase (Pro feature included). 26 * **🛡️ Trust Badges:** Highlight **Verified Owners** to show authenticity and eliminate doubt. 27 * **⚡ Smart Filtering:** Allow shoppers to filter reviews by Rating, With Images/Videos, or Most Recent/Helpful. 28 * **🎨 Beautiful Design:** A modern, responsive interface that looks perfect on every device and integrates with any theme. 29 30 ### 🔥 Key Features 31 32 #### 📸 Visual Reviews (Photo & Video) 33 Allow customers to attach multiple images and videos to their reviews. A picture is worth a thousand words, and a video is worth even more! 34 * **Drag & Drop Upload:** Easy interface for customers. 35 * **Video Support:** Customers can upload MP4/WebM videos directly. 36 * **Media Gallery:** Beautiful layout for review media. 37 38 #### 🎫 Automated Coupon System 39 Reward your loyal customers! The plugin tracks reviews and automatically generates unique coupon codes for customers who meet your review criteria. 40 * **Auto-Generation:** Creates unique WooCommerce coupons automatically. 41 * **Custom Rewards:** Set discount type (Percent/Fixed), amount, and expiry. 42 * **Email Notification:** Automatically emails the coupon code to the customer. 43 44 #### 📧 Email Notifications & SMTP 45 Ensure your emails land in the inbox, not the spam folder. 46 * **Admin Notifications:** Get notified instantly when a new review is submitted. 47 * **Coupon Emails:** Customizable emails delivering rewards to customers. 48 * **Built-in SMTP:** Configure SMTP settings directly within the plugin for reliable email delivery. 49 50 #### 🎛️ Advanced Review Management 51 Users can interact with reviews to help others make better decisions. 52 * **Voting System:** "Helpful" / "Not Helpful" buttons. 53 * **Sorting & Filtering:** Sort by High/Low rating, Most Recent, or Most Helpful. 54 * **Verified Badge:** Automatically marks reviews from actual purchasers. 55 56 #### 🎨 Customization & Control 57 * **Watermarking:** Protect your customer images by automatically adding your logo/text watermark. 58 * **Style Editor:** Customize colors, fonts, and button styles to match your brand identity. 59 * **Restrictions:** Control who can leave reviews (All, Logged-in, or Verified Buyers only). 25 60 26 61 == Installation == … … 28 63 1. Upload the `review-next` plugin folder to the `/wp-content/plugins/` directory, or install the plugin through the WordPress plugins screen directly. 29 64 2. Activate the plugin through the 'Plugins' screen in WordPress. 30 3. Navigate to WooCommerce > Settings > Products > Reviews (or a similar path created by the plugin) to configure review settings if available. 31 4. Customers can now submit reviews with images on product pages. 65 3. Navigate to **Review Next > Settings** to configure your review collection and display preferences. 66 4. Configure your **Email Settings** to ensure notifications and coupons are delivered. 67 5. Sit back and watch the reviews (and sales) roll in! 32 68 33 69 == Frequently Asked Questions == 34 70 35 = How do customers submit reviews with images? = 71 = How do video reviews work? = 72 Customers can upload video files (MP4, WebM) directly through the review form. You can control the maximum file size allowed. 36 73 37 After activating the plugin, the review form on WooCommerce product pages will include an option to upload images along with the review text and rating. 74 = Can I moderate reviews? = 75 Yes! Review Next respects the standard WordPress discussion settings. You can choose to manually approve reviews before they appear on your site. 38 76 39 = Is this plugin compatible with all WooCommerce themes? = 77 = Does it work with my theme? = 78 Review Next is designed to be compatible with all well-coded WooCommerce themes. It uses standard hooks to display the review section. 40 79 41 Review Next for WooCommerce is designed to be compatible with most WooCommerce themes that follow standard WooCommerce templating practices. However, minor styling adjustments might be needed for perfect integration with some themes. 42 43 = Can I moderate reviews before they are published? = 44 45 Yes, Review Next for WooCommerce respects WordPress's default comment moderation settings. You can set reviews to be held for moderation via Settings > Discussion in your WordPress admin area. 46 47 = Where are the uploaded review images stored? = 48 49 Review images are stored in your WordPress media library, making them easy to manage. 80 = How does the Coupon System work? = 81 You set a threshold (e.g., 1 review). When a customer's review is approved, the system generates a unique coupon code based on your settings (e.g., 10% off) and emails it to them. 50 82 51 83 == Screenshots == 52 84 53 1. The review submission form on a product page, showing the image upload field. 54 2. Example of how reviews with images are displayed on the product page. 55 3. Plugin settings page (if applicable). 85 1. **Stunning Review Display:** Modern grid layout showing customer photos and videos. 86 2. **Review Submission Form:** Easy-to-use form with drag-and-drop media upload. 87 3. **Smart Filtering:** Customers filtering reviews by "With Images" and "Verified Purchase". 88 4. **Coupon Email:** Example of the automated email customers receive with their reward. 89 5. **Settings Panel:** Powerful options to customize every aspect of the plugin. 56 90 57 91 == Changelog == 58 92 93 = 1.0.3 = 94 * **New:** Added support for Video Reviews! Customers can now upload MP4/WebM videos. 95 * **New:** Implemented Automated Coupon Generation system to reward reviewers. 96 * **New:** Added comprehensive Email Settings with built-in SMTP configuration. 97 * **New:** Added `CHANGELOG.md` for better version tracking. 98 * **Enhancement:** Improved frontend review display and grid layout. 99 * **Enhancement:** Enhanced "Verified Owner" logic and display. 100 59 101 = 1.0.2 = 60 * Fixed issue with review display on the frontend 61 * Resolved file not found issues 62 * Fixed PCP (Product Comparison Page) related issues 63 * Addressed prefix and other minor issues 64 * Fixed feedback submission issues 65 * General bug fixes and improvements 66 * Added: CHANGELOG.md file for better version tracking 67 * Updated: Plugin version number to 1.0.2 102 * Fixed issue with review display on the frontend 103 * Resolved file not found issues 104 * Fixed PCP (Product Comparison Page) related issues 105 * Addressed prefix and other minor issues 106 * Fixed feedback submission issues 107 * General bug fixes and improvements 68 108 69 = 1.0 = 70 * Initial release of Review Next for WooCommerce. 71 * Feature: Allow customers to upload images with their WooCommerce product reviews. 72 * Feature: Integration with WooCommerce product tabs. 73 * Feature: Basic review management and display. 74 75 == Upgrade Notice == 109 = 1.0.0 = 110 * Initial release of Review Next for WooCommerce. 111 * Feature: Photo reviews, verified badges, and voting system. -
review-next-for-woocommerce/trunk/admin/class-revnextwoo-admin.php
r3315734 r3457384 23 23 class Revnextwoo_Admin 24 24 { 25 public function __construct($plugin_name, $version) 26 { 27 $this->plugin_name = $plugin_name; 28 $this->version = $version; 29 add_action('admin_init', array( 30 $this, 31 'revnextwoo_register_triggers_settings' 32 )); 33 } 34 35 public function revnextwoo_register_triggers_settings() { 36 register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_enable_photo_upload'); 37 register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_max_photos'); 38 39 register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_enable_video_upload'); 40 41 } 42 25 43 26 44 /** … … 43 61 44 62 /** 45 * Initialize the class and set its properties.46 *47 * @since 1.0.048 * @param string $plugin_name The name of this plugin.49 * @param string $version The version of this plugin.50 */51 public function __construct($plugin_name, $version)52 {53 54 $this->plugin_name = $plugin_name;55 $this->version = $version;56 }57 58 /**59 63 * Register the stylesheets for the admin area. 60 64 * … … 75 79 * class. 76 80 */ 81 if (isset($_GET['page']) && $_GET['page'] === 'revnextwoo-coupon-settings') { 82 wp_enqueue_style('select2-css', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css'); 83 wp_enqueue_script('select2-js', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', ['jquery'], null, true); 84 } 77 85 78 86 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/revnextwoo-admin.css', array(), $this->version, 'all'); … … 102 110 wp_enqueue_script('quicktags', false, array(), $this->version, true); // Set version for quicktags 103 111 } 112 113 public function admin_footer_scripts() 114 { 115 if (isset($_GET['page']) && $_GET['page'] === 'revnextwoo-coupon-settings') { 116 ?> 117 <script> 118 jQuery(document).ready(function ($) { 119 $('#coupon_categories').select2({ 120 placeholder: 'Select Categories', 121 width: 'resolve', 122 allowClear: true 123 }); 124 }); 125 jQuery(document).ready(function ($) { 126 $('#coupon_brands').select2({ 127 placeholder: 'Select Brands', 128 width: 'resolve', 129 allowClear: true 130 }); 131 }); 132 </script> 133 <?php 134 } 135 } 104 136 } -
review-next-for-woocommerce/trunk/admin/css/revnextwoo-admin.css
r3315734 r3457384 1 .revnextwoo_logo_bg { 2 position: absolute; 3 bottom: 20px; 4 right: 20px; 5 font-size: 64px; 6 font-weight: 900; 7 opacity: 0.15; 8 user-select: none; 9 pointer-events: none; 10 font-family: "Helvetica", "Arial", sans-serif; 11 12 } 13 14 .logo-img { 15 position: absolute; 16 bottom: 120px; 17 right: 20px; 18 width: 120px; 19 opacity: 0.15; 20 user-select: none; 21 pointer-events: none; 22 } 23 24 1 25 .slide-left { 2 26 transition: transform 0.5s ease-in-out; … … 5 29 } 6 30 7 8 9 31 .input_group { 10 32 display: flex; … … 13 35 margin-bottom: 25px; 14 36 margin-right: 14px; 15 } 16 #toplevel_page_review-settings ul li:last-child{ 37 } 38 39 #toplevel_page_review-settings ul li:last-child { 17 40 display: none; 18 41 } 42 43 /* DEBUG WATERMARK */ 44 45 46 /* ReviewNextWoo Admin Tabs Styling */ 47 .reviewnextwoo-header-row { 48 display: flex; 49 align-items: flex-end; 50 justify-content: flex-start; 51 margin-bottom: 0; 52 } 53 54 .reviewnextwoo-dashboard-title { 55 text-align: left; 56 margin: 0 0 18px 0; 57 font-size: 2.1rem; 58 font-weight: 700; 59 color: #1a2433; 60 letter-spacing: -1px; 61 padding-left: 8px; 62 line-height: 1.2; 63 } 64 65 .reviewnextwoo-tabs { 66 display: flex; 67 flex-wrap: nowrap; 68 border-radius: 16px 16px 0 0; 69 box-shadow: 0 2px 8px rgba(60, 80, 180, 0.06); 70 overflow-x: auto; 71 overflow-y: hidden; 72 justify-content: flex-start; 73 background: #f8faff; 74 scrollbar-width: thin; 75 scrollbar-color: #bed2fd #f8faff; 76 } 77 78 .reviewnextwoo-tabs::-webkit-scrollbar { 79 height: 4px; 80 } 81 82 .reviewnextwoo-tabs::-webkit-scrollbar-thumb { 83 background: #bed2fd; 84 border-radius: 4px; 85 } 86 87 .reviewnextwoo-tabs::-webkit-scrollbar-track { 88 background: #f8faff; 89 border-radius: 4px; 90 } 91 92 .reviewnextwoo-tab { 93 background: linear-gradient(90deg, #f8faff 60%, #eaf6ff 100%); 94 border: none; 95 outline: none; 96 cursor: pointer; 97 padding: 10px 35px; 98 font-size: 1.2rem; 99 font-weight: 510; 100 color: #3b4d63; 101 /* border-radius: 12px 12px 0 0; */ 102 transition: color 0.25s, border-bottom 0.25s, background 0.25s, box-shadow 0.25s; 103 position: relative; 104 letter-spacing: 0.01em; 105 min-width: 120px; 106 box-shadow: 0 2px 4px rgba(60, 80, 180, 0.08); 107 /* flex: 0 0 auto; */ 108 display: inline-flex; 109 flex-direction: column; 110 align-items: center; 111 justify-content: center; 112 gap: 4px; 113 line-height: 1.2; 114 } 115 116 .reviewnextwoo-tab.active, 117 .reviewnextwoo-tab:focus { 118 background: #bed2fd; 119 color: #1a237e; 120 z-index: 2; 121 box-shadow: 0 4px 12px rgba(60, 80, 180, 0.10); 122 123 } 124 125 .reviewnextwoo-tab:hover { 126 box-shadow: inset 5px 5px 10px #aaaaaa; 127 } 128 129 .reviewnextwoo-tab-content { 130 display: none; 131 background: linear-gradient(120deg, #f8faff 60%, #e0f7fa 100%); 132 border-radius: 0 0 18px 18px; 133 box-shadow: 0 6px 32px rgba(60, 80, 180, 0.08); 134 min-height: 180px; 135 opacity: 0; 136 transform: translateX(40px); 137 transition: opacity 0.45s cubic-bezier(.4, 0, .2, 1), transform 0.45s cubic-bezier(.4, 0, .2, 1); 138 position: relative; 139 } 140 141 /* Active tab */ 142 .reviewnextwoo-tab-content.active { 143 display: block !important; 144 opacity: 1; 145 transform: translateX(0); 146 z-index: 2; 147 border: none; 148 background: linear-gradient(120deg, #f8faff 60%, #e0f7fa 100%); 149 animation: reviewnextwoo-fade-slide-in 0.4s ease-out; 150 } 151 152 /* Smooth fade + horizontal slide */ 153 @keyframes reviewnextwoo-fade-slide-in { 154 from { 155 opacity: 0; 156 transform: translateX(30px); 157 /* slight right slide */ 158 } 159 160 to { 161 opacity: 1; 162 transform: translateX(0); 163 } 164 } 165 166 /* Slide left transition */ 167 .sliding-left { 168 animation: reviewnextwoo-slide-left 0.35s ease-out; 169 } 170 171 /* Slide right transition */ 172 .sliding-right { 173 animation: reviewnextwoo-slide-right 0.35s ease-out; 174 } 175 176 /* Left slide */ 177 @keyframes reviewnextwoo-slide-left { 178 from { 179 opacity: 0; 180 transform: translateX(40px); 181 /* slide from right */ 182 } 183 184 to { 185 opacity: 1; 186 transform: translateX(0); 187 } 188 } 189 190 /* Right slide */ 191 @keyframes reviewnextwoo-slide-right { 192 from { 193 opacity: 0; 194 transform: translateX(-40px); 195 /* slide from left */ 196 } 197 198 to { 199 opacity: 1; 200 transform: translateX(0); 201 } 202 } 203 204 /* Dashboard Tab Typography (except analytics tab) */ 205 .reviewnextwoo-tab-content:not([data-tab='analytics']) label, 206 .reviewnextwoo-tab-content:not([data-tab='analytics']) .reviewnextwoo-section-title, 207 .reviewnextwoo-tab-content:not([data-tab='analytics']) th, 208 .reviewnextwoo-tab-content:not([data-tab='analytics']) legend { 209 font-size: 16px !important; 210 font-weight: 500; 211 } 212 213 .reviewnextwoo-tab-content:not([data-tab='analytics']) td, 214 .reviewnextwoo-tab-content:not([data-tab='analytics']) .reference, 215 .reviewnextwoo-tab-content:not([data-tab='analytics']) .small, 216 .reviewnextwoo-tab-content:not([data-tab='analytics']) .desc, 217 .reviewnextwoo-tab-content:not([data-tab='analytics']) .description, 218 .reviewnextwoo-tab-content:not([data-tab='analytics']) p.ref, 219 .reviewnextwoo-tab-content:not([data-tab='analytics']) p.small { 220 font-size: 14px !important; 221 font-weight: 350; 222 color: #444; 223 } 224 225 /* Triggers Tab Controls Styling */ 226 .revnextwoo-list-controls { 227 list-style: none; 228 padding: 0; 229 margin: 0; 230 display: flex; 231 flex-direction: column; 232 gap: 18px; 233 width: 100%; 234 max-width: 100%; 235 align-items: flex-start; 236 } 237 238 .revnextwoo-list-controls li { 239 padding: 14px 16px; 240 display: flex; 241 align-items: center; 242 justify-content: center; 243 gap: 18px; 244 transition: background 0.2s, opacity 0.2s; 245 } 246 247 .revnextwoo-fieldset { 248 border: 3px solid #bed2fd; 249 border-radius: 0px 0px 8px 8px; 250 padding: 3px 15px; 251 width: 100%; 252 max-width: 100%; 253 box-sizing: border-box; 254 text-align: left; 255 } 256 257 .revnextwoo-legend { 258 font-size: 1.5rem; 259 font-weight: 700; 260 color: #1a2433; 261 margin-bottom: 16px; 262 letter-spacing: -0.5px; 263 } 264 265 .feature-title { 266 font-weight: 700; 267 font-size: 1.18rem; 268 color: #222; 269 letter-spacing: -0.5px; 270 } 271 272 .feature-desc { 273 font-size: 1.06rem; 274 color: #444; 275 margin-left: 8px; 276 font-weight: 500; 277 letter-spacing: -0.2px; 278 } 279 280 .faded { 281 opacity: 0.6; 282 pointer-events: none; 283 } 284 285 .revnextwoo-upload-box { 286 border: 1px dashed #bbb; 287 border-radius: 6px; 288 min-height: 48px; 289 padding: 10px; 290 background: #fff; 291 display: flex; 292 align-items: center; 293 gap: 12px; 294 } 295 296 .revnextwoo-list-controls input[type="number"], 297 .revnextwoo-list-controls input[type="text"], 298 .revnextwoo-list-controls input[type="file"], 299 .revnextwoo-list-controls select, 300 .revnextwoo-list-controls button, 301 .revnextwoo-list-controls .button { 302 padding: 8px 14px; 303 border-radius: 5px; 304 border: 1px solid #c2c8d0; 305 min-width: 52px; 306 color: #1a2433; 307 transition: border-color 0.2s, box-shadow 0.2s; 308 } 309 310 .revnextwoo-list-controls button, 311 .revnextwoo-list-controls .button { 312 background: #0a69c7; 313 color: #fff; 314 font-weight: 700; 315 border: none; 316 cursor: pointer; 317 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); 318 } 319 320 .revnextwoo-list-controls button:hover, 321 .revnextwoo-list-controls .button:hover { 322 background: #094b8b; 323 } 324 325 .drag-drop-desc { 326 color: #888; 327 font-size: 13px; 328 margin-left: 8px; 329 } 330 331 /* Styling Settings Section */ 332 .style-settings .form-table { 333 margin-top: 0; 334 max-width: 800px; 335 } 336 337 .style-settings fieldset { 338 border: 1px solid #ddd; 339 border-radius: 4px; 340 padding: 20px 25px; 341 margin-bottom: 30px; 342 background: #fff; 343 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); 344 position: relative; 345 } 346 347 .style-settings legend { 348 font-size: 14px; 349 font-weight: 600; 350 color: #1d2327; 351 background: #f8f9fa; 352 border: 1px solid #ddd; 353 border-radius: 4px; 354 padding: 8px 15px; 355 margin-left: 15px; 356 position: relative; 357 top: -10px; 358 background: #fff; 359 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 360 } 361 362 /* Style for the field groups */ 363 .style-settings h3 { 364 font-size: 16px; 365 color: #1d2327; 366 margin: 25px 0 15px 0; 367 padding-bottom: 8px; 368 border-bottom: 1px solid #eee; 369 } 370 371 /* Color picker fields */ 372 .color-picker-field { 373 display: flex; 374 align-items: center; 375 margin: 10px 0; 376 } 377 378 .color-picker-field .wp-picker-container { 379 position: relative; 380 display: inline-block; 381 vertical-align: top; 382 } 383 384 .color-picker-field .wp-picker-input-wrap { 385 display: inline-block; 386 margin-right: 10px; 387 } 388 389 .color-picker-field .wp-color-result { 390 margin: 0; 391 height: 30px; 392 border-radius: 3px; 393 border: 1px solid #8c8f94; 394 box-shadow: 0 1px 0 #fff, 0 0 2px 1px rgba(0, 0, 0, 0.08); 395 } 396 397 .color-picker-field .wp-color-result-text { 398 line-height: 28px; 399 } 400 401 /* Form table improvements */ 402 .style-settings .form-table th { 403 width: 250px; 404 padding: 15px 10px 15px 0; 405 vertical-align: middle; 406 } 407 408 .style-settings .form-table td { 409 padding: 15px 10px; 410 vertical-align: middle; 411 border-bottom: 1px solid #f0f0f1; 412 } 413 414 .style-settings .form-table tr:last-child td { 415 border-bottom: none; 416 } 417 418 /* Description text */ 419 .style-settings .description { 420 color: #646970; 421 font-style: italic; 422 font-size: 13px; 423 margin-top: 5px; 424 display: block; 425 line-height: 1.4; 426 } 427 428 /* Submit button */ 429 .style-settings .submit { 430 margin-top: 20px; 431 padding: 15px 0; 432 border-top: 1px solid #eee; 433 } 434 435 /* Responsive adjustments */ 436 @media screen and (max-width: 782px) { 437 .style-settings .form-table th { 438 width: 100%; 439 display: block; 440 padding: 10px 0 5px; 441 } 442 443 .style-settings .form-table td { 444 display: block; 445 padding: 5px 0 15px; 446 } 447 448 .style-settings fieldset { 449 padding: 15px; 450 } 451 } 452 453 .style-settings .form-table th { 454 width: 250px; 455 padding: 20px 10px 20px 0; 456 vertical-align: middle; 457 } 458 459 .style-settings .form-table td { 460 padding: 15px 10px; 461 vertical-align: middle; 462 } 463 464 /* Color Picker Styling */ 465 .color-picker-field { 466 display: flex; 467 align-items: center; 468 gap: 10px; 469 } 470 471 .color-picker-field .wp-color-picker { 472 width: 100px !important; 473 height: 35px; 474 padding: 3px 5px; 475 border-radius: 4px; 476 border: 1px solid #8c8f94; 477 } 478 479 .color-picker-field .wp-picker-container { 480 position: relative; 481 } 482 483 .color-picker-field .wp-picker-holder { 484 position: absolute; 485 z-index: 100; 486 } 487 488 /* Form Table Adjustments */ 489 .form-table th { 490 font-weight: 500; 491 } 492 493 .form-table .description { 494 display: block; 495 margin-top: 5px; 496 font-style: italic; 497 color: #646970; 498 font-size: 13px; 499 } 500 501 /* Submit Button */ 502 .revnextwoo-save-btn-row { 503 text-align: right; 504 margin: 30px 0 32px; 505 } 506 507 /* Highlight questions with no replies */ 508 .no-reply-highlight, 509 .no-reply-highlight td { 510 background: #fbe3cc !important; 511 } 512 513 /* Remove styling that breaks row layout */ 514 .no-reply-highlight { 515 color: inherit; 516 border-radius: 0; 517 padding: 0; 518 display: table-row; 519 } 520 521 /* Make S/N (serial number) column narrow */ 522 .wp-list-table th.column-sn, 523 .wp-list-table td.column-sn { 524 width: 40px; 525 min-width: 40px; 526 max-width: 60px; 527 text-align: center; 528 padding-left: 0; 529 padding-right: 0; 530 } 531 532 /* Hide action links by default, show on row hover */ 533 .wp-list-table .question-actions { 534 opacity: 0; 535 transition: opacity 0.2s; 536 } 537 538 .wp-list-table tr:hover .question-actions { 539 opacity: 1; 540 } 541 542 /* ========================================================================== 543 Advanced Settings Page (CAS Settings) 544 ========================================================================== */ 545 546 /* Main Settings Wrapper */ 547 .revnextwoo-settings-wrap { 548 max-width: 1200px; 549 margin: 20px 20px 20px 0; 550 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 551 } 552 553 /* Settings Header */ 554 .revnextwoo-settings-header { 555 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 556 border-radius: 12px 12px 0 0; 557 padding: 28px 32px; 558 margin-bottom: 0; 559 } 560 561 .revnextwoo-settings-header-content { 562 max-width: 800px; 563 } 564 565 .revnextwoo-settings-title { 566 margin: 0 0 10px 0; 567 font-size: 24px; 568 font-weight: 600; 569 color: #fff; 570 display: flex; 571 align-items: center; 572 gap: 12px; 573 letter-spacing: -0.3px; 574 } 575 576 .revnextwoo-settings-title .dashicons { 577 font-size: 28px; 578 width: 28px; 579 height: 28px; 580 background: rgba(255, 255, 255, 0.2); 581 border-radius: 8px; 582 padding: 6px; 583 box-sizing: content-box; 584 } 585 586 .revnextwoo-settings-desc { 587 margin: 0; 588 font-size: 15px; 589 color: rgba(255, 255, 255, 0.9); 590 line-height: 1.5; 591 } 592 593 /* Settings Form */ 594 .revnextwoo-settings-form { 595 background: #f8f9fa; 596 border-radius: 0 0 12px 12px; 597 padding: 28px; 598 border: 1px solid #e1e5eb; 599 border-top: none; 600 } 601 602 /* Settings Grid Layout - Horizontal Flex */ 603 .revnextwoo-settings-grid { 604 display: flex; 605 flex-wrap: wrap; 606 gap: 24px; 607 margin-bottom: 24px; 608 } 609 610 .revnextwoo-settings-grid>.revnextwoo-settings-card:not(.revnextwoo-settings-card-full) { 611 flex: 1 1 calc(50% - 12px); 612 min-width: 380px; 613 } 614 615 /* Settings Cards */ 616 .revnextwoo-settings-card { 617 background: #fff; 618 border-radius: 10px; 619 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); 620 border: 1px solid #e8ecf1; 621 overflow: hidden; 622 transition: box-shadow 0.2s ease, transform 0.2s ease; 623 } 624 625 .revnextwoo-settings-card:hover { 626 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); 627 } 628 629 .revnextwoo-settings-card-full { 630 flex: 1 1 100%; 631 min-width: 100%; 632 } 633 634 /* Card Header */ 635 .revnextwoo-card-header { 636 background: linear-gradient(to right, #f8f9fa, #fff); 637 padding: 18px 24px; 638 border-bottom: 1px solid #e8ecf1; 639 display: flex; 640 align-items: center; 641 gap: 12px; 642 } 643 644 .revnextwoo-card-header .dashicons { 645 font-size: 20px; 646 width: 20px; 647 height: 20px; 648 color: #667eea; 649 background: rgba(102, 126, 234, 0.1); 650 border-radius: 6px; 651 padding: 8px; 652 box-sizing: content-box; 653 } 654 655 .revnextwoo-card-header h3 { 656 margin: 0; 657 font-size: 16px; 658 font-weight: 600; 659 color: #1d2327; 660 letter-spacing: -0.2px; 661 } 662 663 /* Card Body */ 664 .revnextwoo-card-body { 665 padding: 24px; 666 } 667 668 /* Form Rows */ 669 .revnextwoo-form-row { 670 margin-bottom: 24px; 671 } 672 673 .revnextwoo-form-row:last-child { 674 margin-bottom: 0; 675 } 676 677 /* Labels */ 678 .revnextwoo-label { 679 display: block; 680 font-size: 14px; 681 font-weight: 600; 682 color: #1d2327; 683 margin-bottom: 8px; 684 } 685 686 /* Field Wrapper */ 687 .revnextwoo-field-wrap { 688 position: relative; 689 } 690 691 /* Input Styling */ 692 .revnextwoo-input { 693 width: 100%; 694 padding: 12px 16px; 695 font-size: 15px; 696 line-height: 1.4; 697 color: #1d2327; 698 background: #fff; 699 border: 1.5px solid #d0d5dd; 700 border-radius: 8px; 701 transition: all 0.2s ease; 702 box-sizing: border-box; 703 } 704 705 .revnextwoo-input:hover { 706 border-color: #98a2b3; 707 } 708 709 .revnextwoo-input:focus { 710 outline: none; 711 border-color: #667eea; 712 box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15); 713 } 714 715 .revnextwoo-input-number { 716 font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", monospace; 717 font-size: 16px; 718 font-weight: 500; 719 } 720 721 /* Input Groups */ 722 .revnextwoo-input-group { 723 display: flex; 724 align-items: stretch; 725 } 726 727 .revnextwoo-input-prefix, 728 .revnextwoo-input-suffix { 729 display: flex; 730 align-items: center; 731 justify-content: center; 732 padding: 0 14px; 733 font-size: 15px; 734 font-weight: 600; 735 color: #667085; 736 background: #f9fafb; 737 border: 1.5px solid #d0d5dd; 738 } 739 740 .revnextwoo-input-prefix { 741 border-right: none; 742 border-radius: 8px 0 0 8px; 743 } 744 745 .revnextwoo-input-suffix { 746 border-left: none; 747 border-radius: 0 8px 8px 0; 748 color: #f59e0b; 749 font-size: 18px; 750 } 751 752 .revnextwoo-input-group .revnextwoo-input { 753 flex: 1; 754 } 755 756 .revnextwoo-input-group .revnextwoo-input-currency { 757 border-radius: 0 8px 8px 0; 758 } 759 760 .revnextwoo-input-group .revnextwoo-input-rating { 761 border-radius: 8px 0 0 8px; 762 } 763 764 /* Badge Input Styling */ 765 .revnextwoo-badge-input-wrap { 766 display: flex; 767 align-items: center; 768 gap: 12px; 769 } 770 771 .revnextwoo-badge-icon { 772 font-size: 28px; 773 filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1)); 774 } 775 776 .revnextwoo-badge-input-wrap .revnextwoo-input { 777 flex: 1; 778 max-width: 180px; 779 } 780 781 /* Badge Colors */ 782 .revnextwoo-badge-gold .revnextwoo-input:focus { 783 border-color: #f59e0b; 784 box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.15); 785 } 786 787 .revnextwoo-badge-silver .revnextwoo-input:focus { 788 border-color: #9ca3af; 789 box-shadow: 0 0 0 3px rgba(156, 163, 175, 0.15); 790 } 791 792 .revnextwoo-badge-bronze .revnextwoo-input:focus { 793 border-color: #d97706; 794 box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.15); 795 } 796 797 /* Date Input */ 798 .revnextwoo-input-date { 799 max-width: 220px; 800 cursor: pointer; 801 } 802 803 .revnextwoo-input-date::-webkit-calendar-picker-indicator { 804 cursor: pointer; 805 opacity: 0.6; 806 transition: opacity 0.2s; 807 } 808 809 .revnextwoo-input-date::-webkit-calendar-picker-indicator:hover { 810 opacity: 1; 811 } 812 813 /* Field Descriptions */ 814 .revnextwoo-field-desc { 815 margin: 8px 0 0 0; 816 font-size: 13px; 817 color: #667085; 818 line-height: 1.4; 819 } 820 821 /* Settings Footer */ 822 .revnextwoo-settings-footer { 823 padding-top: 24px; 824 border-top: 1px solid #e8ecf1; 825 text-align: right; 826 } 827 828 /* Submit Button */ 829 .revnextwoo-submit-btn { 830 display: inline-flex !important; 831 align-items: center; 832 justify-content: center; 833 min-width: 160px; 834 padding: 14px 28px !important; 835 font-size: 15px !important; 836 font-weight: 600 !important; 837 color: #fff !important; 838 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; 839 border: none !important; 840 border-radius: 8px !important; 841 cursor: pointer; 842 transition: all 0.2s ease; 843 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.35); 844 text-shadow: none !important; 845 } 846 847 .revnextwoo-submit-btn:hover { 848 transform: translateY(-1px); 849 box-shadow: 0 6px 20px rgba(102, 126, 234, 0.45); 850 background: linear-gradient(135deg, #5a6fd6 0%, #6a4190 100%) !important; 851 } 852 853 .revnextwoo-submit-btn:active { 854 transform: translateY(0); 855 box-shadow: 0 2px 8px rgba(102, 126, 234, 0.35); 856 } 857 858 .revnextwoo-submit-btn:focus { 859 outline: none !important; 860 box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.3), 0 4px 12px rgba(102, 126, 234, 0.35) !important; 861 } 862 863 /* Responsive Design for Settings Page */ 864 @media screen and (max-width: 1024px) { 865 .revnextwoo-settings-grid { 866 flex-direction: column; 867 } 868 869 .revnextwoo-settings-grid>.revnextwoo-settings-card:not(.revnextwoo-settings-card-full) { 870 flex: 1 1 100%; 871 min-width: 100%; 872 } 873 } 874 875 @media screen and (max-width: 782px) { 876 .revnextwoo-settings-wrap { 877 margin: 10px 10px 10px 0; 878 } 879 880 .revnextwoo-settings-header { 881 padding: 20px 20px; 882 border-radius: 10px 10px 0 0; 883 } 884 885 .revnextwoo-settings-title { 886 font-size: 20px; 887 flex-direction: column; 888 align-items: flex-start; 889 gap: 10px; 890 } 891 892 .revnextwoo-settings-desc { 893 font-size: 14px; 894 } 895 896 .revnextwoo-settings-form { 897 padding: 20px; 898 border-radius: 0 0 10px 10px; 899 } 900 901 .revnextwoo-settings-grid { 902 gap: 16px; 903 } 904 905 .revnextwoo-card-header { 906 padding: 14px 18px; 907 } 908 909 .revnextwoo-card-body { 910 padding: 18px; 911 } 912 913 .revnextwoo-badge-input-wrap .revnextwoo-input { 914 max-width: 100%; 915 } 916 917 .revnextwoo-settings-footer { 918 text-align: center; 919 } 920 921 .revnextwoo-submit-btn { 922 width: 100%; 923 } 924 } 925 926 @media screen and (max-width: 480px) { 927 .revnextwoo-settings-header { 928 padding: 16px; 929 } 930 931 .revnextwoo-settings-form { 932 padding: 16px; 933 } 934 935 .revnextwoo-card-header, 936 .revnextwoo-card-body { 937 padding: 14px; 938 } 939 940 .revnextwoo-input { 941 padding: 10px 14px; 942 font-size: 14px; 943 } 944 945 .revnextwoo-label { 946 font-size: 13px; 947 } 948 949 .revnextwoo-badge-icon { 950 font-size: 24px; 951 } 952 } -
review-next-for-woocommerce/trunk/admin/js/revnextwoo-admin.js
r3315734 r3457384 1 jQuery(document).ready(function($) { 2 3 $('.revnextwoo-color-field').wpColorPicker(); 4 5 $('.revnextwoo-advanced-setting-wrapper a').on('click', function (e) { 6 e.preventDefault(); 7 $('.nav-tab').removeClass('nav-tab-active'); 8 $(this).addClass('nav-tab-active'); 9 $('.tab-content').hide(); 10 $($(this).attr('href')).show(); 11 }); 12 13 // Open the "View" popup 14 $('.view-link').click(function(e) { 15 e.preventDefault(); 16 var commentId = $(this).data('comment-id'); 17 var commentContent = $('#comment-content-' + commentId).html(); 18 19 $('.popup-content').html(commentContent); 20 $('.popup').show(); 1 jQuery(document).ready(function ($) { 2 // Media uploader for avatar images 3 $(document).on('click', '.revnextwoo-upload-button', function (e) { 4 e.preventDefault(); 5 6 var button = $(this); 7 var wrapper = button.closest('.image-upload-wrapper'); 8 var inputField = wrapper.find('input[type="hidden"]'); 9 var previewContainer = wrapper.find('.avatar-preview'); 10 var previewImage = wrapper.find('.avatar-preview-image'); 11 12 // Create the media frame 13 var frame = wp.media({ 14 title: button.data('uploader_title') || 'Select Avatar', 15 button: { 16 text: button.data('uploader_button_text') || 'Use this avatar' 17 }, 18 library: { 19 type: 'image' 20 }, 21 multiple: false 21 22 }); 22 23 23 // Close the popup 24 $('.popup').click(function() { 25 $(this).hide(); 24 // When an image is selected 25 frame.on('select', function () { 26 var attachment = frame.state().get('selection').first().toJSON(); 27 28 // Update the hidden input with the image URL 29 inputField.val(attachment.url).trigger('change'); 30 31 // Update the preview 32 previewImage.html('<img src="' + attachment.url + '" style="max-width: 100%; max-height: 100%; object-fit: cover;" />'); 33 34 // Show the preview and hide the upload button 35 previewContainer.removeClass('hidden'); 36 button.addClass('hidden'); 37 38 // Show the remove button if hidden 39 wrapper.find('.remove-avatar').show(); 26 40 }); 27 41 28 // Prevent closing when clicking inside the popup 29 $('.popup-content').click(function(e) { 30 e.stopPropagation(); 31 }); 42 // Open the media frame 43 frame.open(); 44 }); 45 46 // Remove avatar 47 $(document).on('click', '.remove-avatar', function (e) { 48 e.preventDefault(); 49 50 var wrapper = $(this).closest('.image-upload-wrapper'); 51 var inputField = wrapper.find('input[type="hidden"]'); 52 var previewContainer = wrapper.find('.avatar-preview'); 53 var previewImage = wrapper.find('.avatar-preview-image'); 54 var uploadButton = wrapper.find('.revnextwoo-upload-button'); 55 56 // Clear the input 57 inputField.val('').trigger('change'); 58 59 // Reset the preview 60 previewImage.html('<span style="color: #999; font-size: 12px;">' + wp.i18n.__('No image selected', 'review-next-for-woocommerce') + '</span>'); 61 62 // Show upload button and hide preview 63 uploadButton.removeClass('hidden'); 64 previewContainer.addClass('hidden'); 65 66 // Hide the remove button 67 $(this).hide(); 68 }); 69 70 // Handle initial state on page load 71 $('.image-upload-wrapper').each(function () { 72 var wrapper = $(this); 73 var inputField = wrapper.find('input[type="hidden"]'); 74 var previewContainer = wrapper.find('.avatar-preview'); 75 var uploadButton = wrapper.find('.revnextwoo-upload-button'); 76 var removeButton = wrapper.find('.remove-avatar'); 77 78 if (inputField.val()) { 79 // If there's a value, show the preview and remove button, hide upload button 80 previewContainer.removeClass('hidden'); 81 uploadButton.addClass('hidden'); 82 removeButton.show(); 83 } else { 84 // If no value, show upload button, hide preview and remove button 85 previewContainer.addClass('hidden'); 86 uploadButton.removeClass('hidden'); 87 removeButton.hide(); 88 } 89 }); 90 91 92 93 // wpColorPicker logic start here 94 $('.revnextwoo-color-field').wpColorPicker(); 95 96 $('.revnextwoo-advanced-setting-wrapper a').on('click', function (e) { 97 e.preventDefault(); 98 $('.nav-tab').removeClass('nav-tab-active'); 99 $(this).addClass('nav-tab-active'); 100 $('.tab-content').hide(); 101 $($(this).attr('href')).show(); 102 }); 103 104 // Open the "View" popup 105 $('.view-link').click(function (e) { 106 e.preventDefault(); 107 var commentId = $(this).data('comment-id'); 108 var commentContent = $('#comment-content-' + commentId).html(); 109 110 $('.popup-content').html(commentContent); 111 $('.popup').show(); 112 }); 113 114 // Close the popup 115 $('.popup').click(function () { 116 $(this).hide(); 117 }); 118 119 // Prevent closing when clicking inside the popup 120 $('.popup-content').click(function (e) { 121 e.stopPropagation(); 122 }); 32 123 33 124 34 125 // question reply 35 $(document).on('click', '.vim-r-question-reply', function() {36 // Get comment and post IDs from data attributes37 var commentId = $(this).data('comment-id');38 var postId = $(this).data('post-id');39 40 // Find the table row of the clicked 'Reply' button41 var $row = $(this).closest('tr');42 43 // Check if a reply form already exists and hide it44 if ($row.next().hasClass('reply-form-row')) {45 $row.next().remove();46 } else {47 // Create a reply form48 var replyForm = '<tr class="reply-form-row">' +49 '<td colspan="6" class="colspanchange">' +50 '<fieldset class="comment-reply">' +51 '<legend>' +52 '<span class="hidden" id="editlegend" style="display: none;">Edit question</span>' +53 '<span class="hidden" id="replyhead" style="display: inline;">edit to question</span>' +54 '<span class="hidden" id="addhead" style="display: none;">Add new question</span>' +55 '</legend>' +56 '<div id="replycontainer">' +57 '<label for="replycontent" class="screen-reader-text">question</label>' +58 '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +59 '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' +60 '<div id="wp-replycontent-editor-container" class="wp-editor-container">' +61 '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' +62 '<!-- Quicktags buttons for text formatting -->' +63 '</div>' +64 '<textarea class="wp-editor-area" rows="5" cols="40" name="replycontent" id="qustion-reply-content"></textarea>' +65 '</div>' +66 '</div>' +67 '</div>' +68 '</fieldset>' +69 '<div id="replysubmit" class="submit">' +70 '<p class="reply-submit-buttons">' +71 '<button type="button" class="save button button-primary question-submit-reply" data-comment-id="' + commentId + '" data-post-id="' + postId + '" >Submit Reply</button>' +72 '<button type="button" class="question-cancel button">Cancel</button>' +73 '<span class="waiting spinner"></span>' +74 '</p>' +75 '<div class="notice notice-error notice-alt inline hidden">' +76 '<p class="error"></p>' +77 '</div>' +78 '</div>' +79 '</td>' +80 '</tr>';81 82 // Insert the reply form after the current row83 $row.after(replyForm);84 85 // Initialize quicktags for the textarea86 quicktags({ id: "replycontent" });87 }88 });89 90 // Event delegation for the "Submit Reply" button click91 $(document).on('click', '.question-submit-reply', function() {92 var questionId = $(this).data('comment-id');93 var replyContent = $('#qustion-reply-content').val();94 var postId = $(this).data('post-id');95 // Create an object to hold the data you want to send via AJAX96 var data = {97 action: 'revnextwoo_submit_question_reply',98 comment_id: questionId,99 post_id: postId,100 reply_content: replyContent101 };102 var that = this;103 // Send an AJAX POST request to the server104 $.ajax({105 url: ajaxurl,106 type: 'POST',107 data: data,108 success: function(response) {109 if(response.success){126 $(document).on('click', '.vim-r-question-reply', function () { 127 // Get comment and post IDs from data attributes 128 var commentId = $(this).data('comment-id'); 129 var postId = $(this).data('post-id'); 130 131 // Find the table row of the clicked 'Reply' button 132 var $row = $(this).closest('tr'); 133 134 // Check if a reply form already exists and hide it 135 if ($row.next().hasClass('reply-form-row')) { 136 $row.next().remove(); 137 } else { 138 // Create a reply form 139 var replyForm = '<tr class="reply-form-row">' + 140 '<td colspan="6" class="colspanchange">' + 141 '<fieldset class="comment-reply">' + 142 '<legend>' + 143 '<span class="hidden" id="editlegend" style="display: none;">Edit question</span>' + 144 '<span class="hidden" id="replyhead" style="display: inline;">edit to question</span>' + 145 '<span class="hidden" id="addhead" style="display: none;">Add new question</span>' + 146 '</legend>' + 147 '<div id="replycontainer">' + 148 '<label for="replycontent" class="screen-reader-text">question</label>' + 149 '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' + 150 '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' + 151 '<div id="wp-replycontent-editor-container" class="wp-editor-container">' + 152 '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' + 153 '<!-- Quicktags buttons for text formatting -->' + 154 '</div>' + 155 '<textarea class="wp-editor-area" rows="5" cols="40" name="replycontent" id="qustion-reply-content"></textarea>' + 156 '</div>' + 157 '</div>' + 158 '</div>' + 159 '</fieldset>' + 160 '<div id="replysubmit" class="submit">' + 161 '<p class="reply-submit-buttons">' + 162 '<button type="button" class="save button button-primary question-submit-reply" data-comment-id="' + commentId + '" data-post-id="' + postId + '" >Submit Reply</button>' + 163 '<button type="button" class="question-cancel button">Cancel</button>' + 164 '<span class="waiting spinner"></span>' + 165 '</p>' + 166 '<div class="notice notice-error notice-alt inline hidden">' + 167 '<p class="error"></p>' + 168 '</div>' + 169 '</div>' + 170 '</td>' + 171 '</tr>'; 172 173 // Insert the reply form after the current row 174 $row.after(replyForm); 175 176 // Initialize quicktags for the textarea 177 quicktags({ id: "replycontent" }); 178 } 179 }); 180 181 // Event delegation for the "Submit Reply" button click 182 $(document).on('click', '.question-submit-reply', function () { 183 var questionId = $(this).data('comment-id'); 184 var replyContent = $('#qustion-reply-content').val(); 185 var postId = $(this).data('post-id'); 186 // Create an object to hold the data you want to send via AJAX 187 var data = { 188 action: 'revnextwoo_submit_question_reply', 189 comment_id: questionId, 190 post_id: postId, 191 reply_content: replyContent 192 }; 193 var that = this; 194 // Send an AJAX POST request to the server 195 $.ajax({ 196 url: ajaxurl, 197 type: 'POST', 198 data: data, 199 success: function (response) { 200 if (response.success) { 110 201 // Handle the server response, e.g., display a success message 111 202 console.log('AJAX response:', response.success); 112 203 // After processing, you can remove the reply form row 113 204 $(that).closest('tr.reply-form-row').remove(); 114 } 115 116 }, 117 error: function(error) { 118 // Handle any errors that occur during the AJAX request 119 console.error('AJAX request error', error); 120 }, 121 }); 205 } 206 207 }, 208 error: function (error) { 209 // Handle any errors that occur during the AJAX request 210 console.error('AJAX request error', error); 211 }, 122 212 }); 123 124 $(document).on('click', '.question-cancel', function() { 125 // Remove the reply form 126 $(this).closest('tr.reply-form-row').remove(); 127 }); 128 129 130 $(document).on('click', '[data-action="question_edit"]', function() { 131 // Get comment and post IDs from data attributes 132 var commentId = $(this).data('comment-id'); 133 var postId = $(this).data('post-id'); 134 135 // Find the table row of the clicked 'Quick Edit' button 136 var $row = $(this).closest('tr'); 137 138 // Get the question content from the row 139 var questionContent = $row.find('#question_content').text(); 140 console.log(questionContent); 141 var user_name = $row.find('#username').text(); 142 var email = $row.find('#user-email').text(); 143 var ip = $row.find('#user-ip').text(); 144 145 146 if ($row.next().hasClass('update-question-form-row')) { 147 $row.next().remove(); 148 } else { 149 // Create a reply form 150 var replyForm = '<tr class="update-question-form-row">' + 151 '<td colspan="6" class="colspanchange">' + 152 '<fieldset class="update_question">' + 153 '<legend>' + 154 '<span class="hidden" id="editlegend" style="display: none;">Edit Question</span>' + 155 '<span class="hidden" id="replyhead" style="display: inline;">Edit to Question</span>' + 156 '<span class="hidden" id="addhead" style="display: none;">Update Question</span>' + 157 '</legend>' + 158 '<div id="container">' + 159 '<label for="replycontent" class="screen-reader-text">Question</label>' + 160 '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' + 161 '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' + 162 '<div id="wp-replycontent-editor-container" class="wp-editor-container">' + 163 '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' + 164 '<!-- Quicktags buttons for text formatting -->' + 165 '</div>' + 166 '<textarea class="wp-editor-area" rows="5" cols="40" name="update-qustion-content" id="update-qustion-content">' + questionContent + '</textarea>' + 167 '</div>' + 168 '</div>' + 169 '</div>' + 170 '<div id="edithead" style="">' + 171 '<div class="inside">' + 172 '<label for="author-name">Name</label>' + 173 '<input type="text" name="newcomment_author" value="' + user_name + '" size="50" value="" id="author-name">' + 174 '</div>' + 175 '<div class="inside">' + 176 '<label for="author-email">Email</label>' + 177 '<input type="text" name="newcomment_author_email" value="' + email + '" size="50" value="" id="author-email">' + 178 '</div>' + 179 '<div class="inside">' + 180 '<label for="author-url">URL</label>' + 181 '<input type="text" id="author-url" name="newcomment_author_url" value="' + ip + '" class="code" size="50" >' + 182 '</div>' + 183 '</div>' + 184 '</fieldset>' + 185 '<div id="replysubmit" class="submit">' + 186 '<p class="reply-submit-buttons">' + 187 '<button type="button" class="save button button-primary update-question" data-comment-id="' + commentId + '">Update Question</button>' + 188 '<button type="button" class="update-question-cancel button">Cancel</button>' + 189 '<span class="waiting spinner"></span>' + 190 '</p>' + 191 '<div class="notice notice-error notice-alt inline hidden">' + 192 '<p class="error"></p>' + 193 '</div>' + 194 '</div>' + 195 '</td>' + 196 '</tr>'; 197 198 // Insert the reply form after the current row 199 $row.after(replyForm); 200 201 // Initialize quicktags for the textarea 202 quicktags({ id: "replycontent" }); 213 }); 214 215 $(document).on('click', '.question-cancel', function () { 216 // Remove the reply form 217 $(this).closest('tr.reply-form-row').remove(); 218 }); 219 220 221 $(document).on('click', '[data-action="question_edit"]', function () { 222 // Get comment and post IDs from data attributes 223 var commentId = $(this).data('comment-id'); 224 var postId = $(this).data('post-id'); 225 226 // Find the table row of the clicked 'Quick Edit' button 227 var $row = $(this).closest('tr'); 228 229 // Get the question content from the row 230 var questionContent = $row.find('#question_content').text(); 231 console.log(questionContent); 232 var user_name = $row.find('#username').text(); 233 var email = $row.find('#user-email').text(); 234 var ip = $row.find('#user-ip').text(); 235 236 237 if ($row.next().hasClass('update-question-form-row')) { 238 $row.next().remove(); 239 } else { 240 // Create a reply form 241 var replyForm = '<tr class="update-question-form-row">' + 242 '<td colspan="6" class="colspanchange">' + 243 '<fieldset class="update_question">' + 244 '<legend>' + 245 '<span class="hidden" id="editlegend" style="display: none;">Edit Question</span>' + 246 '<span class="hidden" id="replyhead" style="display: inline;">Edit to Question</span>' + 247 '<span class="hidden" id="addhead" style="display: none;">Update Question</span>' + 248 '</legend>' + 249 '<div id="container">' + 250 '<label for="replycontent" class="screen-reader-text">Question</label>' + 251 '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' + 252 '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' + 253 '<div id="wp-replycontent-editor-container" class="wp-editor-container">' + 254 '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' + 255 '<!-- Quicktags buttons for text formatting -->' + 256 '</div>' + 257 '<textarea class="wp-editor-area" rows="5" cols="40" name="update-qustion-content" id="update-qustion-content">' + questionContent + '</textarea>' + 258 '</div>' + 259 '</div>' + 260 '</div>' + 261 '<div id="edithead" style="">' + 262 '<div class="inside">' + 263 '<label for="author-name">Name</label>' + 264 '<input type="text" name="newcomment_author" value="' + user_name + '" size="50" value="" id="author-name">' + 265 '</div>' + 266 '<div class="inside">' + 267 '<label for="author-email">Email</label>' + 268 '<input type="text" name="newcomment_author_email" value="' + email + '" size="50" value="" id="author-email">' + 269 '</div>' + 270 '<div class="inside">' + 271 '<label for="author-url">URL</label>' + 272 '<input type="text" id="author-url" name="newcomment_author_url" value="' + ip + '" class="code" size="50" >' + 273 '</div>' + 274 '</div>' + 275 '</fieldset>' + 276 '<div id="replysubmit" class="submit">' + 277 '<p class="reply-submit-buttons">' + 278 '<button type="button" class="save button button-primary update-question" data-comment-id="' + commentId + '">Update Question</button>' + 279 '<button type="button" class="update-question-cancel button">Cancel</button>' + 280 '<span class="waiting spinner"></span>' + 281 '</p>' + 282 '<div class="notice notice-error notice-alt inline hidden">' + 283 '<p class="error"></p>' + 284 '</div>' + 285 '</div>' + 286 '</td>' + 287 '</tr>'; 288 289 // Insert the reply form after the current row 290 $row.after(replyForm); 291 292 // Initialize quicktags for the textarea 293 quicktags({ id: "replycontent" }); 294 } 295 }); 296 297 // question update 298 $(document).on('click', '.update-question', function () { 299 // Get the updated values from the form fields 300 var updatedQuestionContent = $('#update-qustion-content').val(); 301 var updatedUserName = $('#author-name').val(); 302 var updatedEmail = $('#author-email').val(); 303 var updatedIP = $('#author-url').val(); 304 // Create an object to hold the data you want to send via AJAX 305 var data = { 306 action: 'revnextwoo_question_update', 307 commentId: $(this).data('comment-id'), 308 questionContent: updatedQuestionContent, 309 userName: updatedUserName, 310 email: updatedEmail, 311 ip: updatedIP 312 } 313 var that = this; 314 315 // Example AJAX request: 316 $.ajax({ 317 url: ajaxurl, 318 type: 'POST', 319 data: data, 320 success: function (response) { 321 // Handle the success response from the server 322 console.log('Data updated successfully:', response); 323 if (response.data) { 324 $(`.question_content_${response.comment_id}`).text(response.data); 325 $(that).closest('tr').remove(); 326 } 327 328 // Reference to the current row 329 330 }, 331 error: function (error) { 332 // Handle the error from the server 333 console.error('Error updating data:', error); 203 334 } 204 335 }); 205 206 // question update 207 $(document).on('click', '.update-question', function() { 208 // Get the updated values from the form fields 209 var updatedQuestionContent = $('#update-qustion-content').val(); 210 var updatedUserName = $('#author-name').val(); 211 var updatedEmail = $('#author-email').val(); 212 var updatedIP = $('#author-url').val(); 213 // Create an object to hold the data you want to send via AJAX 214 var data = { 215 action: 'revnextwoo_question_update', 216 commentId: $(this).data('comment-id'), 217 questionContent: updatedQuestionContent, 218 userName: updatedUserName, 219 email: updatedEmail, 220 ip: updatedIP 221 } 222 var that = this; 223 224 // Example AJAX request: 225 $.ajax({ 226 url: ajaxurl, 227 type: 'POST', 228 data: data, 229 success: function(response) { 230 // Handle the success response from the server 231 console.log('Data updated successfully:', response); 232 if(response.data){ 233 $(`.question_content_${response.comment_id}`).text(response.data); 234 $(that).closest('tr').remove(); 336 }); 337 338 $(document).on('click', '.update-question-cancel', function () { 339 // Remove the update-question-form 340 $(this).closest('tr.update-question-form-row').remove(); 341 }); 342 343 // Event handler for the "status_update_question" link 344 $('.status_update_question').on('click', function (e) { 345 e.preventDefault(); 346 347 // Get the comment ID and action from the data attributes 348 var questionID = $(this).data('question-id'); 349 var action = $(this).data('action'); 350 351 // Reference to the current row 352 var $row = $(this).closest('tr'); 353 354 // Call the handleCommentAction function 355 handleCommentAction(action, questionID, $row); 356 }); 357 358 // Function to handle unapprove, spam, and trash actions 359 function handleCommentAction(action, questionID, $row) { 360 // Confirm the action (you can customize this if needed) 361 if (!confirm('Are you sure you want to ' + action + ' this comment?')) { 362 return; 363 } 364 var comment_status = action; 365 366 // Create an object to pass to the server 367 var data = { 368 action: 'revnextwoo_status_update_question', 369 question_id: questionID, 370 status: comment_status, 371 }; 372 373 // Send the AJAX request 374 $.ajax({ 375 url: ajaxurl, 376 type: 'POST', 377 data: data, 378 success: function (response) { 379 if (response.success) { 380 // Handle the server response, e.g., display a success message 381 console.log('Comment ' + action + 'ed successfully:', response.success); 382 383 // Remove the row if the action is "Trash" or "Spam" 384 if (action === 'Trash' || action === 'Spam') { 385 $row.addClass('slide-left'); 386 387 // After the animation, remove the row 388 $row.on('transitionend', function () { 389 $(this).remove(); 390 }); 391 } else { 392 var change_status = action === 'Approved' ? 'Unapproved' : 'Approved'; 393 394 // Find the span element by class and update its data-action and text 395 const $findSpan = $row.find(`.${action} a`); 396 $findSpan.attr('data-action', change_status).text(change_status); 397 $row.find(`.${action}`).removeClass(action).addClass(change_status); 398 235 399 } 236 237 // Reference to the current row 238 239 }, 240 error: function(error) { 241 // Handle the error from the server 242 console.error('Error updating data:', error); 400 } else { 401 console.error('Comment ' + action + ' failed.'); 243 402 } 244 }); 403 }, 404 error: function (error) { 405 // Handle any errors that occur during the AJAX request 406 console.error('AJAX request error', error); 407 } 245 408 }); 246 247 $(document).on('click', '.update-question-cancel', function() { 248 // Remove the update-question-form 249 $(this).closest('tr.update-question-form-row').remove(); 250 }); 251 252 // Event handler for the "status_update_question" link 253 $('.status_update_question').on('click', function (e) { 254 e.preventDefault(); 255 256 // Get the comment ID and action from the data attributes 257 var questionID = $(this).data('question-id'); 258 var action = $(this).data('action'); 259 260 // Reference to the current row 261 var $row = $(this).closest('tr'); 262 263 // Call the handleCommentAction function 264 handleCommentAction(action, questionID, $row); 265 }); 266 267 // Function to handle unapprove, spam, and trash actions 268 function handleCommentAction(action, questionID, $row) { 269 // Confirm the action (you can customize this if needed) 270 if (!confirm('Are you sure you want to ' + action + ' this comment?')) { 271 return; 272 } 273 var comment_status = action; 274 275 // Create an object to pass to the server 276 var data = { 277 action: 'revnextwoo_status_update_question', 278 question_id: questionID, 279 status: comment_status, 280 }; 281 282 // Send the AJAX request 409 } 410 411 412 413 $('.view-reply').on('click', function (e) { 414 e.preventDefault(); 415 var questionId = $(this).data('question-id'); 416 var data = { 417 action: 'revnextwoo_get_question_replies', 418 question_id: questionId, 419 }; 420 var that = this; 421 var newRow = $(that).closest('tr').next('.replies-row'); 422 423 if (newRow.length) { 424 // Replies row already exists, toggle its visibility 425 newRow.toggle(); 426 } else { 427 // Replies row doesn't exist, fetch and append replies 283 428 $.ajax({ 284 429 url: ajaxurl, … … 286 431 data: data, 287 432 success: function (response) { 288 if (response.success) { 289 // Handle the server response, e.g., display a success message 290 console.log('Comment ' + action + 'ed successfully:', response.success); 291 292 // Remove the row if the action is "Trash" or "Spam" 293 if (action === 'Trash' || action === 'Spam') { 294 $row.addClass('slide-left'); 295 296 // After the animation, remove the row 297 $row.on('transitionend', function () { 298 $(this).remove(); 299 }); 300 }else { 301 var change_status = action === 'Approved' ? 'Unapproved' : 'Approved'; 302 303 // Find the span element by class and update its data-action and text 304 const $findSpan = $row.find(`.${action} a`); 305 $findSpan.attr('data-action', change_status).text(change_status); 306 $row.find(`.${action}`).removeClass(action).addClass(change_status); 307 308 } 309 } else { 310 console.error('Comment ' + action + ' failed.'); 311 } 312 }, 313 error: function (error) { 314 // Handle any errors that occur during the AJAX request 315 console.error('AJAX request error', error); 316 } 317 }); 318 } 319 320 321 322 $('.view-reply').on('click', function(e) { 323 e.preventDefault(); 324 var questionId = $(this).data('question-id'); 325 var data = { 326 action: 'revnextwoo_get_question_replies', 327 question_id: questionId, 328 }; 329 var that = this; 330 var newRow = $(that).closest('tr').next('.replies-row'); 331 332 if (newRow.length) { 333 // Replies row already exists, toggle its visibility 334 newRow.toggle(); 335 } else { 336 // Replies row doesn't exist, fetch and append replies 337 $.ajax({ 338 url: ajaxurl, 339 type: 'POST', 340 data: data, 341 success: function(response) { 342 // Append the replies in a new row below the clicked "View Reply" link 343 newRow = $('<tr class="replies-row"><td colspan="6">'+ 433 // Append the replies in a new row below the clicked "View Reply" link 434 newRow = $('<tr class="replies-row"><td colspan="6">' + 344 435 '<span class="hidden" id="replyhead" style="display: inline;">Question reply</span>' + 345 436 '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' + … … 352 443 '</div>' + 353 444 '</div>' + 354 '</div>' + 445 '</div>' + 355 446 '</td></tr>'); 356 $(that).closest('tr').after(newRow); 357 } 358 }); 359 } 360 }); 361 362 363 364 $('#sendEmailButton').on('click', function() { 365 var checkboxes = document.querySelectorAll('input[name="order_id[]"]:checked'); 366 if (checkboxes.length > 0) { 367 var orderIds = []; 368 checkboxes.forEach(function (checkbox) { 369 var orderId = checkbox.value; 370 orderIds.push(orderId); 371 }); 372 373 // AJAX request to send emails 374 var data = { 375 action: 'revnextwoo_send_emails', 376 order_ids: orderIds, 377 }; 378 379 // Use AJAX to trigger the server-side action 380 $.ajax({ 381 url: ajaxurl, 382 type: 'POST', 383 data: data, 384 success: function(response) { 385 // Display the response (e.g., success message) 386 alert(response); 387 }, 388 error: function(error) { 389 // Handle errors 390 console.error(error); 391 } 392 }); 447 $(that).closest('tr').after(newRow); 393 448 } 394 395 449 }); 396 397 398 399 400 401 402 450 } 451 }); 452 453 454 455 $('#sendEmailButton').on('click', function () { 456 var checkboxes = document.querySelectorAll('input[name="order_id[]"]:checked'); 457 if (checkboxes.length > 0) { 458 var orderIds = []; 459 checkboxes.forEach(function (checkbox) { 460 var orderId = checkbox.value; 461 orderIds.push(orderId); 462 }); 463 464 // AJAX request to send emails 465 var data = { 466 action: 'revnextwoo_send_emails', 467 order_ids: orderIds, 468 }; 469 470 // Use AJAX to trigger the server-side action 471 $.ajax({ 472 url: ajaxurl, 473 type: 'POST', 474 data: data, 475 success: function (response) { 476 // Display the response (e.g., success message) 477 alert(response); 478 }, 479 error: function (error) { 480 // Handle errors 481 console.error(error); 482 } 483 }); 484 } 485 486 }); 403 487 404 488 }); -
review-next-for-woocommerce/trunk/assets/css/revnextwoo-styles.css
r3315734 r3457384 1 /* Review Plugin Dynamic Styles */ 1 .revnextwoo_text_box { 2 max-width: 100%; 3 word-wrap: break-word; 4 overflow: hidden; 5 position: relative; 6 padding: 2px 10px; 7 } 8 9 .revnextwoo_review_text { 10 white-space: normal; 11 margin: 0; 12 line-height: 1.5; 13 } 14 15 .revnextwoo_review_text.truncated { 16 max-height: 4.5em; 17 /* 3 lines of text (1.5em line-height * 3) */ 18 overflow: hidden; 19 display: -webkit-box; 20 -webkit-line-clamp: 3; 21 -webkit-box-orient: vertical; 22 } 23 24 .revnextwoo_read_more { 25 color: #2271b1; 26 cursor: pointer; 27 font-size: 0.9em; 28 display: inline-block; 29 margin-top: 5px; 30 } 31 32 33 34 .revnextwoo_like_btn, 35 .revnextwoo_report_btn { 36 background: #f5f6fa; 37 border: none; 38 border-radius: 7px; 39 padding: 6px 13px; 40 color: #555; 41 font-size: 0.98rem; 42 cursor: pointer; 43 transition: background 0.15s; 44 display: flex; 45 align-items: center; 46 gap: 5px; 47 z-index: 10; 48 } 49 50 /* 51 .revnextwoo_like_count, 52 .revnextwoo_dislike_count { 53 font-weight: 600; 54 margin-left: 2px; 55 }*/ 56 57 58 59 60 .revnextwoo_review_actions { 61 display: flex; 62 gap: 16px; 63 width: 100%; 64 margin-top: auto; 65 padding: 12px 0px; 66 border: none; 67 color: #e0e0e0; 68 font-size: 1rem; 69 } 2 70 3 71 /* Rating Icons */ … … 6 74 } 7 75 8 .write-revnextwoo-wrapper .rating_field:checked +.icon,76 .write-revnextwoo-wrapper .rating_field:checked+.icon, 9 77 .write-revnextwoo-wrapper .rating-group .icon:hover { 10 78 color: var(--revnextwoo-rating-icon-active-color, #ffc700); … … 19 87 } 20 88 21 #full-stars .rating__input--none:checked +.rating__label .rating__icon--none {22 color: var(--revnextwoo-rating-icon-active-color, #ffc700); 23 } 24 25 #full-stars .rating__input:checked ~.rating__label .rating__icon--star {89 #full-stars .rating__input--none:checked+.rating__label .rating__icon--none { 90 color: var(--revnextwoo-rating-icon-active-color, #ffc700); 91 } 92 93 #full-stars .rating__input:checked~.rating__label .rating__icon--star { 26 94 color: var(--revnextwoo-rating-icon-color, #cccccc); 27 95 } … … 32 100 } 33 101 34 #full-stars .rating__input:hover ~.rating__label .rating__icon--star,35 #full-stars .rating__input:hover ~.rating__label--half .rating__icon--star {102 #full-stars .rating__input:hover~.rating__label .rating__icon--star, 103 #full-stars .rating__input:hover~.rating__label--half .rating__icon--star { 36 104 color: var(--revnextwoo-rating-icon-color, #cccccc); 37 105 } 38 106 39 #full-stars .rating__input--none:hover +.rating__label .rating__icon--none {107 #full-stars .rating__input--none:hover+.rating__label .rating__icon--none { 40 108 color: var(--revnextwoo-rating-icon-active-color, #ffc700); 41 109 } … … 68 136 } 69 137 70 .revnextwoo_product_show .revnextwoo-item, 138 .revnextwoo_review_item { 139 background-color: var(--revnextwoo-item-bg, #ffffff); 140 border: 1px solid #eee; 141 border-radius: 12px; 142 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); 143 margin-bottom: 24px; 144 padding: 24px; 145 transition: transform 0.2s ease, box-shadow 0.2s ease; 146 } 147 148 .revnextwoo_review_item:hover { 149 transform: translateY(-2px); 150 box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06); 151 } 152 71 153 .revnextwoo-reply-card { 72 border-bottom: 1px solid var(--revnextwoo-box-border, #ddd);73 } 74 75 .revnextwoo_product_show .revnextwoo-item:last-child { 76 border-bottom: none; 77 } 154 margin-top: 15px; 155 margin-left: 20px; 156 background-color: #f9f9f9; 157 } 158 159 78 160 79 161 /* Headers */ … … 81 163 .average_ratings .title { 82 164 background-color: var(--revnextwoo-header-bg, #f9f9f9); 165 } 166 167 .user_question_section h3 { 168 font-weight: 500; 169 margin: 0px 0px; 170 color: #232323; 171 padding: 10px 18px; 83 172 } 84 173 … … 151 240 font-weight: var(--revnextwoo-content-weight, 400); 152 241 font-family: var(--revnextwoo-content-family, 'Arial, sans-serif'); 242 line-height: 1.6; 243 color: #444; 244 } 245 246 .revnextwoo-item .comment-author .fn { 247 font-size: 1.1rem; 248 font-weight: 600; 249 color: #333; 250 } 251 252 .revnextwoo-item .comment-metadata { 253 font-size: 0.9rem; 254 color: #888; 255 margin-bottom: 10px; 153 256 } 154 257 -
review-next-for-woocommerce/trunk/includes/class-revnextwoo-activator.php
r3315734 r3457384 31 31 */ 32 32 public static function activate() { 33 34 } 33 // Ensure DB helper is available 34 if (!class_exists('Revnextwoo_QA_DB')) { 35 require_once dirname(__FILE__) . '/class-revnextwoo-qa-db.php'; 36 } 37 // Create custom QA table 38 if (class_exists('Revnextwoo_QA_DB')) { 39 Revnextwoo_QA_DB::create_table(); 40 } 41 } 35 42 36 43 } -
review-next-for-woocommerce/trunk/includes/class-revnextwoo.php
r3315734 r3457384 159 159 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); 160 160 $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); 161 $this->loader->add_action('admin_footer', $plugin_admin, 'admin_footer_scripts'); 162 161 163 } 162 164 -
review-next-for-woocommerce/trunk/public/class-revnextwoo-public.php
r3317206 r3457384 40 40 41 41 /** 42 * Freemium limit for total reviews 43 * 44 * @since 1.0.0 45 * @var int 46 */ 47 const FREEMIUM_REVIEW_LIMIT = 300; 48 49 /** 42 50 * Allowed file types for uploads 43 51 * … … 85 93 add_action('wp_ajax_revnextwoo_submit_review', [$this, 'revnextwoo_handle_review_submission']); 86 94 add_action('wp_ajax_nopriv_revnextwoo_submit_review', [$this, 'revnextwoo_handle_review_submission']); 95 96 97 87 98 } 99 88 100 89 101 /** … … 107 119 */ 108 120 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/revnextwoo-plugin.css', array('dashicons'), $this->version, 'all'); 109 // Fancybox CSS 121 122 // Add dynamic CSS for review form styling 123 $form_bg = get_option('revnextwoo_review_form_background', '#ffffff'); 124 $form_border = get_option('revnextwoo_review_form_border', '#e0e0e0'); 125 $submit_button_bg = get_option('revnextwoo_review_submit_button_bg', '#4a90e2'); 126 $rating_icon_color = get_option('revnextwoo_review_rating_icon_color', '#ffc107'); 127 $link_color = get_option('revnextwoo_review_link_color', '#4a90e2'); 128 $box_item_boxshadow = get_option('revnextwoo_review_box_item_boxshadow', 'rgba(0, 0, 0, 0.3) 0px 19px 38px, rgba(0, 0, 0, 0.22) 0px 15px 12px'); 129 130 $custom_css = ".revnextwoo_review_form_wrapper { 131 background-color: {$form_bg} !important; 132 border: 1px solid {$form_border} !important; 133 } 134 .revnextwoo_form_submit .button.alt { 135 background-color: {$submit_button_bg} !important; 136 border-color: {$submit_button_bg} !important; 137 } 138 .revnextwoo_review_rating { 139 color: {$rating_icon_color} !important; 140 } 141 .revnextwoo_read_more { 142 color: {$link_color} !important; 143 } 144 .revnextwoo_review_item { 145 box-shadow: {$box_item_boxshadow} !important; 146 }"; 147 148 wp_add_inline_style($this->plugin_name, $custom_css); 149 150 // Fancybox CSS 110 151 wp_enqueue_style('fancybox-css', plugin_dir_url(__FILE__) . 'css/vendor/jquery.fancybox.min.css', array(), $this->version); 111 152 wp_enqueue_style('font-awesome', plugin_dir_url(__FILE__) . 'css/vendor/all.min.css', array(), $this->version); … … 132 173 */ 133 174 134 wp_enqueue_script('revnextwoo-review-form-validation', plugin_dir_url(__FILE__) . 'js/revnextwoo-form-validation.js', array('jquery'), $this->version, false);135 175 wp_enqueue_script('revnextwoo-filter-review', plugin_dir_url(__FILE__) . 'js/revnextwoo-filter-review.js', array('jquery'), $this->version, true); 136 176 // Fancybox JS … … 139 179 // Pass the WordPress AJAX URL to your 'filter-review' script 140 180 wp_localize_script('revnextwoo-filter-review', 'revnextwooAjax', array('ajaxurl' => admin_url('admin-ajax.php'))); 181 182 // Enqueue and localize the reviews AJAX pagination script 183 wp_enqueue_script('revnextwoo-reviews', plugin_dir_url(__FILE__) . 'js/revnextwoo-reviews.js', array('jquery'), $this->version, true); 184 wp_localize_script('revnextwoo-reviews', 'revnextwoo_vars', array( 185 'ajax_url' => admin_url('admin-ajax.php'), 186 'nonce' => wp_create_nonce('revnextwoo_nonce') 187 )); 188 141 189 wp_enqueue_script('revnextwoo-review-main', plugin_dir_url(__FILE__) . 'js/revnextwoo-main.js', array('jquery'), $this->version, true); 142 190 } … … 178 226 $review_title = isset($_POST['review_title']) ? sanitize_text_field($_POST['review_title']) : ''; 179 227 $review_text = isset($_POST['review_text']) ? sanitize_textarea_field($_POST['review_text']) : ''; 228 229 180 230 181 231 // Validate rating … … 199 249 error_log('User has already reviewed this product'); 200 250 wp_send_json_error(array('message' => 'You have already reviewed this product.')); 251 return; 252 } 253 254 // Check freemium review limit 255 $total_reviews = $this->count_total_reviews(); 256 if ($total_reviews >= self::FREEMIUM_REVIEW_LIMIT) { 257 error_log('Review limit reached: ' . $total_reviews . '/' . self::FREEMIUM_REVIEW_LIMIT); 258 wp_send_json_error(array( 259 'message' => sprintf( 260 __('You have reached the limit of %d reviews in the free version. Upgrade to Pro to unlock unlimited review submissions.', 'review-next-for-woocommerce'), 261 self::FREEMIUM_REVIEW_LIMIT 262 ) 263 )); 201 264 return; 202 265 } … … 236 299 // Ensure comment_id is an integer 237 300 $comment_id = (int) $comment_id; 301 302 303 304 // Save video ID if present 305 if (isset($_POST['review_video_id']) && is_numeric($_POST['review_video_id'])) { 306 add_comment_meta($comment_id, 'review_video', intval($_POST['review_video_id'])); 307 } 238 308 239 309 // Handle image uploads if any … … 305 375 )); 306 376 } 307 377 378 /** 379 * Count total reviews submitted through the plugin for freemium limit 380 * 381 * @since 1.0.0 382 * @return int Total number of reviews 383 */ 384 private function count_total_reviews() { 385 global $wpdb; 386 387 $count = $wpdb->get_var( 388 "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_type = 'review'" 389 ); 390 391 return (int) $count; 392 } 393 308 394 /** 309 395 * Reorganize the $_FILES array for multiple uploads -
review-next-for-woocommerce/trunk/public/css/revnextwoo-plugin.css
r3317206 r3457384 24 24 -webkit-appearance: none; 25 25 } 26 .login_redirect a{ 27 color: #ffc700 !important; 26 .login_redirect button a{ 27 color: black !important; 28 padding: 5px 8px; 29 background-color: #2196f3; 28 30 } 29 31 .write-review-wrapper .rating-group .group{ … … 80 82 } 81 83 82 .write-review-wrapper .review_field { 83 overflow-y: hidden; 84 resize: none; 85 min-height: 120px; 86 max-height: 160px; 84 .write-review-wrapper .review_field, 85 .revnextwoo_review_form_wrapper input[type="text"], 86 .revnextwoo_review_form_wrapper textarea { 87 87 box-sizing: border-box; 88 width: 100%; 88 89 max-width: 100%; 89 height: auto; 90 padding: 4px 11px; 90 padding: 8px 12px; 91 91 color: rgba(0, 0, 0, 0.88); 92 92 font-size: 14px; 93 93 line-height: 1.5714285714285714; 94 list-style: none;95 94 background-color: #ffffff; 96 background-image: none; 97 border-width: 1px !important; 98 border-style: dashed !important; 99 border-color: #d9d9d9 !important; 95 border: 1px solid #d9d9d9; 100 96 border-radius: 6px; 101 97 transition: all 0.2s; 98 margin: 0; 99 } 100 101 .revnextwoo_review_form_wrapper textarea { 102 min-height: 120px; 103 max-height: 160px; 104 resize: vertical; 105 overflow-y: auto; 106 } 107 108 .revnextwoo_review_form_wrapper input[type="text"] { 109 height: 40px; 110 } 111 112 .revnextwoo_form_group { 113 margin-bottom: 15px; 114 width: 100%; 115 } 116 117 .revnextwoo_form_group label { 102 118 display: block; 119 margin-bottom: 5px; 120 font-weight: 500; 121 } 122 123 /* Ensure form wrapper has proper width and padding */ 124 .revnextwoo_review_form_wrapper { 103 125 width: 100%; 104 } 126 max-width: 100%; 127 box-sizing: border-box; 128 padding: 20px; 129 } 130 105 131 .write-review-wrapper textarea:focus { 106 132 outline: 0; … … 123 149 height: 74px; 124 150 transition: width 0.2s; 151 } 152 .revnextwoo_review_date,.review-location{ 153 font-size: 12px !important 125 154 } 126 155 … … 317 346 318 347 /* Review Form Styles */ 319 .revnextwoo_review_form_wrapper {348 /* .revnextwoo_review_form_wrapper { 320 349 background: #fff; 321 350 border-radius: 8px; … … 330 359 font-size: 18px; 331 360 color: #333; 332 } 361 } */ 333 362 334 363 .revnextwoo_form_group { … … 381 410 .revnextwoo_form_group input[type="text"], 382 411 .revnextwoo_form_group textarea { 383 width: 100%;412 /* width: 100%; */ 384 413 padding: 10px 12px; 385 414 border: 1px solid #ddd; … … 493 522 494 523 /* Review Item Styles */ 495 .revnextwoo_review_item {496 padding: 20px 0;497 border-bottom: 1px solid #eee;498 }499 524 500 525 .revnextwoo_review_item:last-child { … … 505 530 display: flex; 506 531 align-items: center; 507 margin-bottom: 10px; 532 padding:10px 30px; 533 border-radius: 15px; 508 534 } 509 535 510 536 .revnextwoo_reviewer_avatar { 511 537 margin-right: 15px; 512 } 513 514 .revnextwoo_reviewer_avatar img {538 object-fit: cover; 539 } 540 .revnextwoo_reviewer_avatar img{ 515 541 width: 50px; 516 542 height: 50px; 517 543 border-radius: 50%; 518 object-fit: cover;519 544 } 520 545 521 546 .revnextwoo_reviewer_details h4 { 522 margin: 0 0 5px;523 font-size: 16px;524 547 font-weight: 600; 525 548 color: #333; 549 margin:0px 0px; 526 550 } 527 551 … … 556 580 } 557 581 558 .revnextwoo_review_content {559 margin-left: 65px;560 }561 562 582 .revnextwoo_review_title { 563 583 font-size: 16px; … … 580 600 gap: 10px; 581 601 margin: 15px 0; 582 } 583 584 .revnextwoo_review_image { 602 padding-left: 10px; 603 } 604 605 .revnextwoo_review_image{ 585 606 width: 80px; 586 607 height: 80px; … … 615 636 } 616 637 617 /* Review Actions */618 .revnextwoo_review_actions {619 display: flex;620 gap: 15px;621 margin-top: 15px;622 padding-left: 65px;623 }624 625 638 .revnextwoo_like_btn, 626 639 .revnextwoo_report_btn { … … 628 641 border: none; 629 642 color: #666; 643 cursor: pointer; 630 644 font-size: 13px; 645 padding: 5px 10px; 646 margin-left: 5px; 647 border-radius: 3px; 648 transition: all 0.2s ease; 649 z-index: 15; 650 } 651 652 /* Social Media Sharing Buttons */ 653 .revnextwoo_social_sharing { 654 display: inline-flex; 655 gap: 12px; 656 } 657 658 .revnextwoo_share_btn { 659 display: inline-flex; 660 align-items: center; 661 justify-content: center; 662 width: 28px; 663 height: 28px; 664 border-radius: 50%; 665 background: #fff; 666 border: 1px solid #e0e0e0; 631 667 cursor: pointer; 632 display: flex; 633 align-items: center; 634 gap: 5px; 635 padding: 5px 10px; 636 border-radius: 4px; 637 transition: all 0.2s; 668 transition: all 0.2s ease; 669 padding: 0; 670 text-decoration: none; 671 } 672 673 .revnextwoo_share_btn:hover { 674 transform: translateY(-2px); 675 box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 676 } 677 678 .revnextwoo_facebook_share:hover { 679 background-color: #1877F2; 680 } 681 682 .revnextwoo_facebook_share:hover svg { 683 fill: #fff; 684 } 685 686 .revnextwoo_whatsapp_share:hover { 687 background-color: #25D366; 688 } 689 690 .revnextwoo_whatsapp_share:hover svg { 691 fill: #fff; 692 } 693 694 .revnextwoo_twitter_share:hover { 695 background-color: #1DA1F2; 696 } 697 698 .revnextwoo_twitter_share:hover svg { 699 fill: #fff; 700 } 701 702 .revnextwoo_share_btn svg { 703 width: 14px; 704 height: 14px; 705 transition: all 0.2s ease; 638 706 } 639 707 … … 656 724 /* Review Form Styles */ 657 725 .revnextwoo_review_form_wrapper { 658 margin-top: 30px; 659 background: #fff; 726 background-color:var(--revnextwoo-form-bg, #ffffff); 727 padding: 20px; 728 margin-top: 20px; 660 729 border-radius: 8px; 661 padding: 25px;662 730 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 663 731 } … … 782 850 783 851 .revnextwoo_upload_btn .dashicons { 784 font-size: 16px;785 width: 16px;786 height: 16px;852 font-size: 35px; 853 width: 35px; 854 height: 35px; 787 855 } 788 856 … … 804 872 } 805 873 806 /* Login Notice */807 .revnextwoo_login_notice {808 margin-top: 20px;809 padding: 15px;810 background: #fff8e1;811 border-left: 4px solid #ffc107;812 color: #5d4037;813 }814 815 .revnextwoo_login_notice a {816 color: #1976d2;817 text-decoration: none;818 }819 820 .revnextwoo_login_notice a:hover {821 text-decoration: underline;822 }823 824 874 /* No Reviews Message */ 825 875 .revnextwoo_no_reviews { … … 841 891 width: 100%; 842 892 justify-content: space-between; 843 }844 845 .revnextwoo_review_content {846 margin-left: 0;847 }848 849 .revnextwoo_review_actions {850 padding-left: 0;851 893 } 852 894 } … … 1394 1436 } 1395 1437 1396 1438 /* user question section start */ 1397 1439 .user_question_section { 1398 max-width: 10 50px;1440 max-width: 100%; 1399 1441 overflow: hidden; 1400 1442 width: 100%; … … 1404 1446 margin: 20px 0px; 1405 1447 } 1406 .user_question_section .title{ 1448 /* .user_question_section .title{ 1449 font-weight: 600; 1450 font-family: var(--wp--preset--font-family--inter); 1407 1451 background-color: #f2f2f2; 1408 1452 padding: 10px; 1409 1453 text-align: left !important; 1410 font-weight: 500 !important;1411 1454 font-size: 18px !important; 1412 1455 position: static !important; … … 1414 1457 margin-bottom: 3px !important; 1415 1458 1416 } 1459 } */ 1417 1460 .user_question_section .login_redirect{ 1418 1461 font-size: 14px; … … 1421 1464 margin-bottom: 0px !important; 1422 1465 } 1423 .user_question_section .login_redirect a{ 1424 color: #F85606; 1425 } 1466 1426 1467 .user_question_section form { 1427 1468 padding: 18px; … … 1429 1470 } 1430 1471 .user_question_section .input_group { 1431 display: flex;1432 text-align: center;1433 1472 margin: 0 auto; 1434 1473 justify-content: center; … … 1450 1489 1451 1490 .user_question_section .question_field { 1452 width: 100%; 1453 height: 40px; 1454 min-height: 40px; 1455 padding: 10px; 1491 width: 96%; 1492 height: 50px; 1456 1493 resize: vertical; 1457 1494 cursor: text; 1458 border: 1px solid # ccc;1495 border: 1px solid #8e8e8e; 1459 1496 resize: none; 1460 1497 overflow: hidden; 1461 background: none; 1462 box-shadow: none; 1463 outline: none; 1464 margin: 0px; 1498 border-radius: 5px; 1499 padding:20px; 1465 1500 } 1466 1501 1467 1502 .user_question_section .submit_btn { 1468 border-radius: 0; 1469 display: block; 1470 color: #fff; 1471 font-size: 17px; 1472 text-transform: uppercase; 1473 font-weight: 400; 1474 padding: 10px; 1475 line-height: 18px; 1476 transition: all ease 0.35s; 1477 -webkit-transition: all ease 0.35s; 1478 -moz-transition: all ease 0.35s; 1479 margin: 0px; 1503 background: #4a90e2; 1504 color: #fff; 1505 border: none; 1506 padding: 10px 20px; 1507 border-radius: 4px; 1508 font-size: 14px; 1509 font-weight: 500; 1510 cursor: pointer; 1511 transition: background 0.2s; 1480 1512 } 1481 1513 .write-review-wrapper .submit_btn:hover, .reply-comment-form .submit_btn:hover { … … 1483 1515 } 1484 1516 .user_question_show { 1485 padding: 20px; 1517 width:96%; 1518 padding: 24px 0; 1519 background: #fff; 1520 border-radius: 8px; 1521 box-shadow: 0 2px 8px rgba(0,0,0,0.06); 1522 margin: 20px 0; 1523 overflow-wrap: break-word; 1524 word-break: break-word; 1525 margin: 0 auto; 1486 1526 } 1487 1527 1488 1528 .user_question_show .item { 1489 align-content: center; 1490 align-items: center; 1491 display: grid; 1492 grid-template-columns: auto auto; 1493 justify-content: space-between; 1494 margin-top: -1px; 1495 border-top: 1px solid #ececec; 1496 padding: 5px; 1529 display: flex; 1530 flex-direction: column; 1531 gap: 8px; 1532 background: #f9f9f9; 1533 border-radius: 8px; 1534 padding: 18px 18px 12px 18px; 1535 margin-bottom: 18px; 1536 border: 1px solid #ececec; 1537 box-shadow: 0 1px 4px rgba(0,0,0,0.07); 1538 overflow: hidden; 1497 1539 } 1498 1540 .user_question_show .item:last-child { … … 1510 1552 .user_question_show .item p{ 1511 1553 margin-bottom: 0px !important; 1554 padding: 12px 10px 0 0; 1555 font-size: 15px; 1556 color: #222; 1557 line-height: 1.7; 1558 overflow-wrap: anywhere; 1559 word-break: break-word; 1512 1560 } 1513 1561 .user_question_show .short_title{ … … 1518 1566 } 1519 1567 .user_question_show .short_content { 1520 display: flex; 1568 display: block; 1569 position: relative; 1570 gap: 12px; 1521 1571 align-content: center; 1522 /* align-items: center; */ 1523 gap: 5px 17px; 1524 width: 90%; 1572 width: 100%; 1573 border: none; 1574 padding: 0; 1575 overflow-wrap: anywhere; 1576 word-break: break-word; 1577 } 1578 .revnext_que_info{ 1579 display: flex; 1580 align-items: center; 1581 gap: 10px; 1582 margin-bottom: 10px; 1583 border: none; 1584 } 1585 .revnextwoo_question_avatar { 1586 margin-right: 15px; 1587 min-width: 50px; 1588 min-height: 50px; 1589 } 1590 .revnextwoo_question_avatar img { 1591 width: 50px; 1592 height: 50px; 1593 border-radius: 50%; 1594 object-fit: cover; 1595 border: 2px solid #e3e3e3; 1596 background: #fafafa; 1597 } 1598 .revnextwoo_question_username h4 { 1599 margin: 0 0 2px; 1600 font-size: 16px; 1601 font-weight: 600; 1602 color: #333; 1603 line-height: 1.2; 1604 } 1605 .user_question_show .short_content p{ 1606 padding: 10px 0 0 0; 1607 margin: 0; 1608 width: 100%; 1609 background: none; 1610 font-size: 15px; 1611 color: #222; 1612 line-height: 1.7; 1613 overflow-wrap: anywhere; 1614 word-break: break-word; 1615 } 1616 .revnextwoo_question_username{ 1617 float: top; 1525 1618 } 1526 1619 .user_question_show .question_icon{ … … 1528 1621 height: 20px; 1529 1622 } 1623 1530 1624 /* responsive small, medium, large */ 1531 1625 … … 1539 1633 width: 100%; 1540 1634 } 1541 .user_question_show .short_content{ 1542 display: block; 1635 1636 .user_question_show { 1637 width: 100%; 1638 padding: 12px 0; 1543 1639 } 1544 1640 .user_question_show .item { 1545 align-items: first baseline; 1546 grid-template-columns: auto; 1641 flex-direction: column; 1642 padding: 14px 8px 10px 8px; 1643 gap: 6px; 1644 } 1645 .user_question_show .short_content { 1646 width: 100%; 1647 padding: 0; 1648 } 1649 .user_question_show .item p, 1650 .user_question_show .short_content p { 1651 font-size: 14px; 1652 padding: 8px 0 0 0; 1547 1653 } 1548 1654 .user_question_section #question-form { … … 1603 1709 } 1604 1710 @media screen and (max-width: 768px) { 1711 .user_question_show { 1712 width: 100%; 1713 padding: 8px 0; 1714 } 1715 .user_question_show .item { 1716 padding: 10px 4px 8px 4px; 1717 gap: 4px; 1718 } 1719 .user_question_show .item p, 1720 .user_question_show .short_content p { 1721 font-size: 13px; 1722 padding: 6px 0 0 0; 1723 } 1605 1724 #five-stars .rating-group { 1606 1725 display: block; -
review-next-for-woocommerce/trunk/public/js/revnextwoo-filter-review.js
r3317206 r3457384 1 1 jQuery(document).ready(function($) { 2 // Delegated event for thumbs-up 3 $(document).on('click', '.revnextwoo-thumb-up', function () { 4 var target = $(this).data('target'); 5 var $loader = $('#revnextwoo-loader'); 6 if (target) { 7 var voteCount = $('#revnextwoo-vote-count-' + target); 8 recordVote(target, 'like', voteCount, $loader); 9 } 10 }); 11 12 // Delegated event for thumbs-down 13 $(document).on('click', '.revnextwoo-thumb-down', function () { 14 var target = $(this).data('target'); 15 var $loader = $('#revnextwoo-loader'); 16 if (target) { 17 var voteCount = $('#revnextwoo-vote-count-' + target); 18 recordVote(target, 'dislike', voteCount, $loader); 19 } 20 }); 21 22 // Delegated event for comment reply form show/hide 23 $(document).on('click', '.revnextwoo-comment-reply-trigger', function () { 24 $('.revnextwoo-reply-dropdown').hide(); 25 var target = $(this).data('target'); 26 if (target) { 27 $('#revnextwoo-reply-comment-form' + target).toggle(); 28 } 29 }); 30 31 // Star ratings initialization function 32 function initStarRatings() { 33 var $fillRatings = $('.revnextwoo-fill-ratings span'); 34 if ($fillRatings.length) { 35 var starRatingWidth = $fillRatings.width(); 36 $('.revnextwoo-star-ratings').width(starRatingWidth); 37 } 38 } 39 40 // Call star ratings initialization on page load 41 initStarRatings(); 42 43 // Expose for AJAX success usage 44 window.revnextwooInitStarRatings = initStarRatings; 45 2 46 // Function to update comments based on selected filters 3 47 function updateComments() { … … 90 134 } 91 135 92 // When the thumbs-up icon is clicked93 $('.revnextwoo-thumb-up').on('click', function() {94 var target = $(this).data('target');95 if (target) {96 var voteCount = $('#revnextwoo-vote-count-' + target);97 recordVote(target, 'like', voteCount);98 if ($loader.length) {99 $loader.show();100 }101 }102 });103 104 // When the thumbs-down icon is clicked105 $('.revnextwoo-thumb-down').on('click', function() {106 var target = $(this).data('target');107 if (target) {108 var voteCount = $('#revnextwoo-vote-count-' + target);109 recordVote(target, 'dislike', voteCount);110 if ($loader.length) {111 $loader.show();112 }113 }114 });115 116 function recordVote(commentId, voteType, voteCount) {117 if (typeof revnextwooAjax === 'undefined' && typeof ajaxurl === 'undefined') {118 console.error('AJAX URL not defined');119 return;120 }121 122 $.ajax({123 type: 'POST',124 url: typeof revnextwooAjax !== 'undefined' ? revnextwooAjax.ajaxurl : ajaxurl,125 data: {126 action: 'revnextwoo_record_comment_vote',127 comment_id: commentId,128 vote_type: voteType,129 nonce: typeof revnextwooAjax !== 'undefined' ? revnextwooAjax.nonce : ''130 },131 success: function(response) {132 if ($loader.length) {133 $loader.hide();134 }135 136 if (response && response.success) {137 var data = response.data;138 if (voteCount.length) {139 voteCount.text(data.vote_count);140 }141 142 // Update active states143 if (data.increment) {144 $('#revnextwoo-thumb-up-' + data.comment_id).addClass('active');145 $('#revnextwoo-thumb-down-' + data.comment_id).removeClass('active');146 } else if (data.decrement) {147 $('#revnextwoo-thumb-down-' + data.comment_id).addClass('active');148 $('#revnextwoo-thumb-up-' + data.comment_id).removeClass('active');149 } else {150 $('#revnextwoo-thumb-up-' + data.comment_id).removeClass('active');151 $('#revnextwoo-thumb-down-' + data.comment_id).removeClass('active');152 }153 }154 },155 error: function(xhr, status, error) {156 console.error('Error recording vote:', error);157 if ($loader.length) {158 $loader.hide();159 }160 }161 });162 }163 164 136 // Initialize star ratings width 165 var $fillRatings = $('.revnextwoo-fill-ratings span'); 166 if ($fillRatings.length) { 167 var starRatingWidth = $fillRatings.width(); 168 $('.revnextwoo-star-ratings').width(starRatingWidth); 137 if (window.revnextwooInitStarRatings) { 138 window.revnextwooInitStarRatings(); 169 139 } 170 140 }, -
review-next-for-woocommerce/trunk/public/js/revnextwoo-form-validation.js
r3315734 r3457384 1 document.addEventListener('DOMContentLoaded', function () { 2 const imageUpload = document.getElementById('revnextwoo-image-upload'); 3 const imagePreview = document.getElementById('revnextwoo-image-preview'); 4 const selectedFileCount = document.getElementById('revnextwoo-selected-file-count'); 5 6 // Maximum allowed images 7 const maxImages = 6; 8 9 // Initialize the count 10 let selectedImages = 0; 11 12 // Listen for changes in the file input 13 if (imageUpload) { 14 imageUpload.addEventListener('change', handleImageUpload); 15 } 16 17 // Handle image removal using JavaScript 18 if (imagePreview) { 19 imagePreview.addEventListener('click', function (event) { 20 if (event.target.classList.contains('revnextwoo-remove-button')) { 21 const removedImage = event.target.parentNode.querySelector('img'); 22 if (removedImage) { 23 const imageName = removedImage.getAttribute('data-filename'); 24 25 // Remove the image preview 26 event.target.parentNode.remove(); 27 28 // Decrement the selected images count 29 selectedImages -= 1; 30 updateSelectedFileCount(); 31 } 32 } 33 }); 34 } 35 36 function handleImageUpload(event) { 37 const files = event.target.files; 38 39 // Ensure we don't exceed the maximum allowed images 40 if (selectedImages + files.length > maxImages) { 41 alert('Maximum of ' + maxImages + ' images allowed.'); 42 event.target.value = ''; // Clear the file input 43 return; 44 } 45 46 for (const file of files) { 47 if (file.type.startsWith('image/')) { 48 const imageItem = document.createElement('div'); 49 imageItem.classList.add('revnextwoo-image-item'); 50 51 const image = document.createElement('img'); 52 image.src = URL.createObjectURL(file); 53 image.alt = 'Image Preview'; 54 image.width = 100; 55 image.height = 100; 56 image.setAttribute('data-filename', file.name); // Store the filename 57 58 const removeButton = document.createElement('button'); 59 removeButton.classList.add('revnextwoo-remove-button'); 60 removeButton.innerHTML = '×'; 61 removeButton.type = 'button'; // Prevent form submission 62 63 imageItem.appendChild(image); 64 imageItem.appendChild(removeButton); 65 66 if (imagePreview) { 67 imagePreview.appendChild(imageItem); 68 69 // Increment the selected images count 70 selectedImages += 1; 71 updateSelectedFileCount(); 72 } 73 } 74 } 75 } 76 77 function updateSelectedFileCount() { 78 if (selectedFileCount) { 79 selectedFileCount.textContent = `Selected file: ${selectedImages}/${maxImages}`; 80 } 81 } 82 }); 1 // Disabled default image preview logic to prevent double-handling and interference with compression logic in average-rating.php 2 // document.addEventListener('DOMContentLoaded', function () { 3 // ... 4 // }); -
review-next-for-woocommerce/trunk/public/js/revnextwoo-main.js
r3315734 r3457384 4 4 // Cache jQuery objects 5 5 var $document = $(document); 6 7 6 8 7 9 // Initialize question form functionality … … 68 70 }); 69 71 70 // Question reply toggle handler71 $document.on('click', '.revnextwoo-question-reply-trigger', function(e) {72 e.preventDefault();73 var target = $(this).data('target');74 if (target) {75 $('.revnextwoo-reply-question-form').hide();76 $('#revnextwoo-reply-question-form-' + target).toggle();77 }78 });72 // // Question reply toggle handler 73 // $document.on('click', '.question-reply-trigger', function(e) { 74 // e.preventDefault(); 75 // var target = $(this).data('target'); 76 // if (target) { 77 // $('.reply_question_fm').hide(); 78 // $('reply_question_form' + target).toggle(); 79 // } 80 // }); 79 81 } 80 82 … … 104 106 init(); 105 107 }); 108 109 110 111 112 // Toggle reply form display when "Reply" is clicked (delegated for dynamic content) 113 $(document).on('click', '.question-reply-trigger', function (e) { 114 e.preventDefault(); 115 var commentID = $(this).attr('target'); 116 var $form = $('#reply_question_form' + commentID); 117 if ($form.length) { 118 if ($form.css('display') === 'none' || $form.css('display') === '') { 119 $form.show(); 120 } else { 121 $form.hide(); 122 } 123 } 124 }); -
review-next-for-woocommerce/trunk/review-next-for-woocommerce.php
r3317206 r3457384 1 1 <?php 2 3 2 /** 4 3 * The plugin bootstrap file … … 18 17 * Description: Review Next for WooCommerce is a plugin that adds a new tab to your WooCommerce product pages, allowing customers to leave reviews and ratings. 19 18 * Author: Nazmul Hosen 20 * Version: 1.0. 219 * Version: 1.0.3 21 20 * Requires at least: 5.0 22 21 * Tested up to: 6.8.1 23 22 * Requires PHP: 7.2 24 * Requires Plugins: woocommerce 23 * WC requires at least: 6.0 24 * WC tested up to: 8.0 25 * HPOS Compatible: Yes 26 * WC HPOS Compatible: Yes 25 27 * Author URI: https://profiles.wordpress.org/nazmul111/ 26 28 * License: GPLv2 or later … … 34 36 exit; // Exit if accessed directly 35 37 } 38 39 // --- Dependency Check --- 40 require_once plugin_dir_path( __FILE__ ) . 'includes/class-revnextwoo-dependency-checker.php'; 41 42 if ( ! Revnextwoo_Dependency_Checker::check_dependencies() ) { 43 return; 44 } 45 // --- END Dependency Check --- 46 47 // --- Ensure coupon generator is loaded and initialized --- 48 if (function_exists('error_log')) error_log('[revnextwoo][DEBUG] Main plugin file loaded'); 49 require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-coupon-generator.php'; 50 if (class_exists('Revnextwoo_Coupon_Generator')) { 51 if (function_exists('error_log')) error_log('[revnextwoo][DEBUG] Coupon generator class found, initializing'); 52 new Revnextwoo_Coupon_Generator(); 53 } else { 54 if (function_exists('error_log')) error_log('[revnextwoo][ERROR] Coupon generator class NOT found'); 55 } 56 // --- END coupon generator loading --- 36 57 /** 37 58 * Currently plugin version. … … 39 60 * Rename this for your plugin and update it as you release new versions. 40 61 */ 41 define('REVNEXTWOO_PLUGIN_VERSION', '1.0 ');62 define('REVNEXTWOO_PLUGIN_VERSION', '1.0.3'); 42 63 43 64 /** … … 69 90 */ 70 91 require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo.php'; 71 92 require plugin_dir_path(__FILE__) . 'includes/revnextwoo-helper.php'; 93 require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-qa-db.php'; 94 95 // Load email settings and handler 96 require plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-email-settings.php'; 97 require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-email-integrations.php'; 98 99 // Initialize email settings 100 if (is_admin()) { 101 RevNextWoo_Email_Settings::get_instance(); 102 } 72 103 /** 73 104 * Begins execution of the plugin. … … 89 120 90 121 /** 122 * Declare HPOS compatibility 123 */ 124 function revnextwoo_declare_hpos_compatibility() 125 { 126 if (class_exists('Automattic\WooCommerce\Internal\Features\FeaturesController')) { 127 $features_controller = wc_get_container()->get('Automattic\WooCommerce\Internal\Features\FeaturesController'); 128 $features_controller->declare_compatibility('custom_order_tables', plugin_basename(__FILE__), true); 129 } 130 } 131 add_action('before_woocommerce_init', 'revnextwoo_declare_hpos_compatibility'); 132 133 /** 91 134 * Enqueue plugin styles and scripts 92 135 */ 93 136 function revnextwoo_enqueue_plugin_assets() 94 137 { 138 // Enqueue Watermarker.js only on product page (where review form is shown) 139 if (is_product()) { 140 wp_enqueue_script( 141 'revnextwoo-watermarker', 142 plugin_dir_url(__FILE__) . 'assets/js/vendor/watermarker/protect-image-watermarker/Watermarker.js', 143 array('jquery'), 144 '1.0', 145 true 146 ); 147 // Pass watermark settings to JS 148 $watermark_enabled = get_option('revnextwoo_img_watermark', 'no') === 'yes'; 149 $watermark_text = get_option('revnextwoo_img_watermark_text', ''); 150 wp_localize_script('revnextwoo-watermarker', 'revnextwooWatermarkSettings', array( 151 'enabled' => $watermark_enabled, 152 'text' => $watermark_text, 153 )); 154 } 95 155 // Enqueue CSS 96 156 wp_enqueue_style( … … 98 158 plugin_dir_url(__FILE__) . 'assets/css/revnextwoo-styles.css', 99 159 array(), 100 REVNEXTWOO_PLUGIN_VERSION160 time() 101 161 ); 102 162 … … 106 166 plugin_dir_url(__FILE__) . 'assets/js/revnextwoo-styles.js', 107 167 array(), 108 REVNEXTWOO_PLUGIN_VERSION,168 time(), 109 169 true 110 170 ); … … 139 199 140 200 wp_localize_script('revnextwoo-plugin-styles-js', 'revnextwooPluginStyles', $style_data); 201 202 // Enqueue Social Sharing Script 203 if (is_product()) { 204 wp_enqueue_script( 205 'revnextwoo-social-share', 206 plugin_dir_url(__FILE__) . 'public/js/revnextwoo-social-share.js', 207 array('jquery'), 208 time(), 209 true 210 ); 211 } 141 212 } 142 213 add_action('wp_enqueue_scripts', 'revnextwoo_enqueue_plugin_assets'); … … 156 227 plugin_dir_url(__FILE__) . 'assets/css/revnextwoo-styles.css', 157 228 array(), 158 REVNEXTWOO_PLUGIN_VERSION229 time() 159 230 ); 160 231 } … … 243 314 function revnextwoo_add_reviews_and_comments_tab($tabs) 244 315 { 245 // Add a new tab with the title "Reviews & Comments" 246 $tabs['reviews_comments_tab'] = array( 247 'title' => __('Reviews', 'review-next-for-woocommerce'), 248 'priority' => 50, 249 'callback' => 'revnextwoo_reviews_and_comments_tab_content', 250 // Callback function to display tab content 251 ); 252 253 // Get the product ID 254 global $product; 255 $args = array( 256 'post_id' => $product->get_id(), 257 'type' => 'review', 258 'count' => true // return only the count 259 ); 260 // Get the number of reviews for the product 261 $review_count = get_comments($args); 262 263 // Modify the tab title to include the review count 264 $tabs['reviews_comments_tab']['title'] = sprintf( 265 /* translators: %d: number of reviews */ 266 __('Reviews (%d)', 'review-next-for-woocommerce'), 267 $review_count 268 ); 269 270 316 // Only add the review tab if the setting is enabled 317 if (get_option('revnextwoo_show_revnextwoo_box_singular', 1)) { 318 global $product; 319 $args = array( 320 'post_id' => $product->get_id(), 321 'type' => 'review', 322 'status' => 'approve', 323 'count' => true // return only the count 324 ); 325 $review_count = get_comments($args); 326 $tabs['reviews_comments_tab'] = array( 327 'title' => sprintf( 328 /* translators: %d: number of reviews */ 329 __('Reviews (%d)', 'review-next-for-woocommerce'), 330 $review_count 331 ), 332 'priority' => 2, 333 'callback' => 'revnextwoo_reviews_and_comments_tab_content', 334 ); 335 } 271 336 return $tabs; 272 337 } … … 277 342 { 278 343 global $product; 279 344 280 345 // Get the review box display setting 281 $show_review_box_singular = get_option('revnextwoo_show_rev iew_box_singular', 1);346 $show_review_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', 1); 282 347 283 348 // Get the current user's ID 284 $show_revnextwoo_box_singular = get_option('revnextwoo_show_rev iew_box_singular', 'registered_users_and_guests');349 $show_revnextwoo_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', 'registered_users_and_guests'); 285 350 // Get the product ID 286 351 $product_id = $product->get_id(); 287 352 288 353 // Initialize comment data array 289 354 $comment_data = array( … … 298 363 'comment_approved' => 1, 299 364 ); 300 301 // Check if the user has purchased the product 365 366 // Enforce 'Who is allowed to rate' setting 367 $allowed_to_rate = get_option('revnextwoo_allowed_to_rate', 'registered_users_and_guests'); 368 $current_user_id = get_current_user_id(); 369 $user_can_review = true; 370 $error_message = ''; 371 if ($allowed_to_rate === 'no_one') { 372 $user_can_review = false; 373 $error_message = __('Review submissions are currently disabled.', 'review-next-for-woocommerce'); 374 } elseif ($allowed_to_rate === 'completed_orders') { 375 // Only logged-in users who purchased the product 376 if (!$current_user_id) { 377 $user_can_review = false; 378 $error_message = __('You must be logged in and have purchased this product to leave a review.', 'review-next-for-woocommerce'); 379 } else { 380 $current_user = get_user_by('ID', $current_user_id); 381 $user_email = isset($current_user->user_email) ? $current_user->user_email : ''; 382 if (!wc_customer_bought_product($user_email, $current_user_id, $product->get_id())) { 383 $user_can_review = false; 384 $error_message = __('Only verified purchasers can leave a review for this product.', 'review-next-for-woocommerce'); 385 } 386 } 387 } 388 389 // Block review submission if not allowed 302 390 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['comment'])) { 391 if (!$user_can_review) { 392 wp_die(esc_html($error_message)); 393 } 303 394 304 395 // Verify the nonce … … 329 420 // $product_id is the post ID to attach the image to (the product) 330 421 $attachment_id = media_handle_upload('image_upload', $product_id); 331 422 332 423 // Check for errors 333 424 if (is_wp_error($attachment_id)) { … … 364 455 'comment_content' => $comment, 365 456 'comment_type' => 'review', 366 'comment_meta' => array( 367 'rating' => $rating, 368 // Save the rating as comment metadata 369 ), 457 'comment_approved' => 1, // Auto-approve reviews 370 458 'user_id' => $current_user_id, 371 459 ); … … 373 461 add_filter('duplicate_comment_id', '__return_false'); 374 462 375 463 // Insert the comment 464 $comment_id = wp_insert_comment($comment_data); 465 466 // Add rating meta after comment is created 467 if ($comment_id && !is_wp_error($comment_id)) { 468 update_comment_meta($comment_id, 'rating', $rating); 469 470 // Mark as verified owner if user has purchased the product 471 if (wc_customer_bought_product($user_email, $current_user_id, $product_id)) { 472 update_comment_meta($comment_id, 'verified', true); 473 } 474 } 376 475 377 476 if ($comment_id) { … … 420 519 } 421 520 422 if ($show_review_box_singular == 1) {423 // Show the review boxes424 include(plugin_dir_path(__FILE__) . 'template/frontend/review-ratting-form.php');425 }426 521 427 522 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['reply_comment'])) { … … 466 561 467 562 468 // }469 470 563 if ($show_review_box_singular == 1) { 471 564 // Display existing average ratings 472 565 include(plugin_dir_path(__FILE__) . 'template/frontend/average-rating.php'); 473 474 // Display existing reviews with uploaded images and videos 475 include(plugin_dir_path(__FILE__) . 'template/frontend/product-review-show.php'); 476 } 477 } 478 566 } 567 } 568 569 570 // Video Review AJAX handler (NEW) 571 add_action('wp_ajax_revnextwoo_upload_review_video', 'revnextwoo_upload_review_video'); 572 add_action('wp_ajax_nopriv_revnextwoo_upload_review_video', 'revnextwoo_upload_review_video'); 573 function revnextwoo_upload_review_video() { 574 check_ajax_referer('revnextwoo_nonce', 'security'); 575 if (!isset($_FILES['review_video']) || !is_user_logged_in()) { 576 wp_send_json_error(['message' => __('No video or not logged in.', 'review-next-for-woocommerce')]); 577 wp_die(); 578 } 579 $file = $_FILES['review_video']; 580 // Validate file type and size 581 $allowed_types = ['video/mp4', 'video/webm']; 582 if (!in_array($file['type'], $allowed_types)) { 583 wp_send_json_error(['message' => __('Invalid video type.', 'review-next-for-woocommerce')]); 584 wp_die(); 585 } 586 $max_video_size_mb = 50; // Hardcoded limit for free version 587 if ($file['size'] > $max_video_size_mb * 1024 * 1024) { 588 wp_send_json_error(['message' => sprintf(__('Video exceeds %dMB.', 'review-next-for-woocommerce'), $max_video_size_mb)]); 589 wp_die(); 590 } 591 // Upload to media library 592 require_once(ABSPATH . 'wp-admin/includes/file.php'); 593 require_once(ABSPATH . 'wp-admin/includes/media.php'); 594 require_once(ABSPATH . 'wp-admin/includes/image.php'); 595 $attachment_id = media_handle_upload('review_video', 0); 596 if (is_wp_error($attachment_id)) { 597 wp_send_json_error(['message' => __('Video upload failed.', 'review-next-for-woocommerce')]); 598 wp_die(); 599 } 600 wp_send_json_success(['video_id' => $attachment_id, 'url' => wp_get_attachment_url($attachment_id)]); 601 wp_die(); 602 } 603 604 // Save video attachment ID to comment meta (NEW, safe addition) 605 add_action('comment_post', function($comment_id) { 606 if (isset($_POST['review_video_id']) && is_numeric($_POST['review_video_id'])) { 607 add_comment_meta($comment_id, 'review_video', intval($_POST['review_video_id'])); 608 } 609 }, 20); 479 610 480 611 // Define the AJAX action for logged-in users … … 629 760 echo '</div>'; 630 761 echo '</div>'; 631 // Display star rating based on the "rating" value762 // Display star rating based on per-review rating value (fresh from meta) 632 763 echo '<div class="rating_start">'; 633 if ($rating > 0) { 764 $review_rating = intval(get_comment_meta($comment->comment_ID, 'rating', true)); 765 if ($review_rating > 0) { 634 766 for ($i = 1; $i <= 5; $i++) { 635 $iconType = ($i <= $r ating) ? 'filled' : 'empty';767 $iconType = ($i <= $review_rating) ? 'filled' : 'empty'; 636 768 $starClass = $icon_classes[$rating_images][$iconType]; 637 769 echo '<i class="' . esc_attr($starClass) . '"></i>'; … … 661 793 $comment_image_id = get_comment_meta($comment->comment_ID, 'revnextwoo_image_id', true); 662 794 $comment_image_url = get_comment_meta($comment->comment_ID, 'revnextwoo_image_url', true); 795 // Display uploaded video for this comment (NEW) 796 $review_video_id = get_comment_meta($comment->comment_ID, 'review_video', true); 797 if (!empty($review_video_id)) { 798 $video_url = wp_get_attachment_url($review_video_id); 799 if ($video_url) { 800 echo '<div class="revnextwoo_review_video"><video style="margin: auto;" width="320" controls><source src="' . esc_url($video_url) . '" type="video/mp4">'.__('Your browser does not support the video tag.','review-next-for-woocommerce').'</video></div>'; 801 } 802 } 663 803 664 804 if (!empty($comment_image_id)) { … … 778 918 echo '</div>'; // Close review-item 779 919 } 780 // Display comment pagination links781 // echo '<div class="comment-pagination">';782 // paginate_comments_links();783 // echo '</div>';784 920 } else { 785 921 echo '<p class="not_found"> No comments found.</p>'; … … 885 1021 // Get the product ID 886 1022 global $product; 887 $args = array( 888 'post_id' => $product->get_id(), 889 'type' => 'question', 890 'status' => 'approve', 891 'count' => true // return only the count 892 ); 893 // Get the number of reviews for the product 894 $question_count = get_comments($args); 1023 // Count questions from custom QA table 1024 $question_count = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::count_questions($product->get_id()) : 0; 895 1025 896 1026 // Modify the tab title to include the review count … … 904 1034 return $tabs; 905 1035 } 906 add_filter('woocommerce_product_tabs', 'revnextwoo_add_question_tab'); 907 908 function revnextwoo_question_tab_content() 909 { 910 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question'])) { 911 // Verify nonce 912 if (!isset($_POST['question_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_nonce'])), 'submit_question_nonce')) { 913 wp_die('Security check failed'); 914 return; 915 } 916 917 // Validate and sanitize question content 918 $question = isset($_POST['question']) ? wp_kses_post(wp_unslash($_POST['question'])) : ''; 919 if (empty($question)) { 920 wp_die('Question content is required'); 921 return; 922 } 923 924 $current_user_id = get_current_user_id(); 925 if (!$current_user_id) { 926 wp_die('You must be logged in to submit a question'); 927 return; 928 } // Create a new comment reply 929 $question = sanitize_text_field(wp_unslash($_POST['question'])); 930 $current_user_id = get_current_user_id(); 931 $current_user = get_user_by('ID', $current_user_id); 932 $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name; 933 $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email; 934 $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url; 935 $valid = revnextwoo_validate_question($question); 936 if ($valid) { 937 // Sanitize and validate data as needed 938 $comment_post_ID = isset($_POST['comment_post_ID']) ? intval($_POST['comment_post_ID']) : 0; 939 $comment_parent = isset($_POST['comment_parent']) ? intval($_POST['comment_parent']) : 0; 940 941 $comment_data = array( 942 'comment_content' => $question, 943 'comment_post_ID' => $comment_post_ID, 944 'comment_parent' => $comment_parent, 945 'comment_author' => $display_name, 946 'comment_author_email' => $user_email, 947 'comment_author_url' => $author_url, 948 'comment_type' => 'question', 949 'user_id' => $current_user_id, 950 ); 951 952 // Insert the reply comment 953 wp_insert_comment($comment_data); 954 955 echo '<p style="color:#F85606">Your question has been submitted successfully.</p>'; 956 wp_safe_redirect(esc_url(get_permalink())); 957 exit; 958 } else { 959 echo '<p style="color: #F85606;">Your question should not contain contact information.</p>'; 960 } 961 } 962 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question_reply'])) { 963 // Verify nonce 964 if (!isset($_POST['question_reply_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_reply_nonce'])), 'submit_question_reply_nonce')) { 965 wp_die('Security check failed'); 966 } 967 968 // Create a new comment reply 969 $question = sanitize_text_field(wp_unslash($_POST['question_reply'])); 970 $current_user_id = get_current_user_id(); 971 $current_user = get_user_by('ID', $current_user_id); 972 $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name; 973 $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email; 974 $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url; 975 $valid = revnextwoo_validate_question($question); 976 if ($valid) { 977 // Sanitize and validate data as needed 978 $comment_post_ID = isset($_POST['comment_post_ID']) ? intval($_POST['comment_post_ID']) : 0; 979 $comment_parent = isset($_POST['comment_parent']) ? intval($_POST['comment_parent']) : 0; 980 981 $comment_data = array( 982 'comment_content' => $question, 983 'comment_post_ID' => $comment_post_ID, 984 'comment_parent' => $comment_parent, 985 'comment_author' => $display_name, 986 'comment_author_email' => $user_email, 987 'comment_author_url' => $author_url, 988 'comment_type' => 'question_reply', 989 'user_id' => $current_user_id, 990 ); 991 992 // Insert the reply comment 993 wp_insert_comment($comment_data); 994 995 echo '<p style="color:#F85606">Your question reply has been submitted successfully.</p>'; 996 } else { 997 echo '<p style="color: #F85606;">Your question reply should not contain contact information.</p>'; 998 } 999 } 1036 1037 //-------AJAX handler for product questions pagination---------------------------- 1038 add_action('wp_ajax_revnextwoo_load_questions', 'revnextwoo_load_questions_ajax_handler'); 1039 add_action('wp_ajax_nopriv_revnextwoo_load_questions', 'revnextwoo_load_questions_ajax_handler'); 1040 function revnextwoo_load_questions_ajax_handler() { 1041 include plugin_dir_path(__FILE__) . 'template/frontend/user-question.php'; 1042 exit; 1043 } 1044 //-------Add the question tab to the product page--------------------------------- 1045 add_filter('woocommerce_product_tabs', function($tabs) { 1000 1046 $show_question_box_singular = get_option('revnextwoo_show_question_box_singular', '1'); 1001 1047 if ($show_question_box_singular == 1) { 1048 return revnextwoo_add_question_tab($tabs); 1049 } 1050 return $tabs; 1051 }); 1052 function revnextwoo_question_tab_content() { 1053 if (get_option('revnextwoo_show_question_box_singular', '1') == 1) { 1002 1054 include(plugin_dir_path(__FILE__) . 'template/frontend/user-question.php'); 1003 1055 } 1004 1056 } 1005 1057 1006 function revnextwoo_validate_question($question) 1007 { 1008 // Define regular expressions to match email, phone, and web links 1009 $emailRegex = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/'; 1010 $phoneRegex = '/\b\d{10,11}\b/'; 1011 $linkRegex = '/https?:\/\/[^\s\/$.?#]+[^\s]*/'; 1012 1013 // Check if the question matches any of the prohibited patterns 1014 if (preg_match($emailRegex, $question) || preg_match($phoneRegex, $question) || preg_match($linkRegex, $question)) { 1015 return false; // Question contains prohibited content 1016 } 1017 1018 return true; // Question is valid 1019 } 1020 1021 1022 1023 1024 // --------------------------- admin dashboard setting start ----------------------- 1025 function revnextwoo_custom_admin_menu() 1026 { 1027 // Create the main "Review" menu 1028 add_menu_page( 1029 'Review', 1030 'Review', 1031 'manage_options', // Capability required to access 1032 'review-settings', // Menu slug 1033 'revnextwoo_review_settings_page', // Callback function to display content 1034 'dashicons-star-filled' // Icon (change to your preferred icon) 1035 ); 1036 add_submenu_page( 1037 'review-settings', 1038 'Question', 1039 'Question', 1040 'manage_options', 1041 'review-question', 1042 'revnextwoo_question_setting_page' 1043 ); 1044 // Create submenu pages 1045 add_submenu_page( 1046 'review-settings', // Parent menu slug 1047 'General Setting', // Page title 1048 'General Setting', // Menu title 1049 'manage_options', 1050 'review-general-setting', // Submenu slug 1051 'revnextwoo_general_setting_page' // Callback function to display content 1052 ); 1053 1054 add_submenu_page( 1055 'review-settings', 1056 'Styling', 1057 'Styling', 1058 'manage_options', 1059 'review-styling-setting', 1060 'revnextwoo_styling_setting_page' 1061 ); 1062 1063 add_submenu_page( 1064 'review-settings', 1065 'Typography', 1066 'Typography', 1067 'manage_options', 1068 'review-typography-setting', 1069 'revnextwoo_typography_setting_page' 1070 ); 1071 1072 add_submenu_page( 1073 'review-settings', 1074 'Advanced Setting', 1075 'Advanced Setting', 1076 'manage_options', 1077 'review-advanced-setting', 1078 'revnextwoo_advanced_setting_page' 1079 ); 1080 // Add a submenu item under 'review-settings' 1081 add_submenu_page( 1082 'review-settings', 1083 'Order Details', 1084 'Order Details', 1085 'manage_options', 1086 'order-details', 1087 'revnextwoo_custom_order_details_callback' 1088 ); 1089 1090 // Add Email Reminder submenu 1091 add_submenu_page( 1092 'review-settings', 1093 'Email Reminder', 1094 'Email Reminder', 1095 'manage_options', 1096 'email-reminder', 1097 'revnextwoo_email_reminder_callback' 1098 ); 1099 } 1100 // Hook the menu creation function 1101 add_action('admin_menu', 'revnextwoo_custom_admin_menu'); 1102 // Callback functions to display content for each page 1103 function revnextwoo_review_settings_page() 1104 { 1105 echo '<div class="wrap"><h2>RevNextWoo Settings</h2>'; 1106 echo '<p>This is the General Setting submenu.</p>'; 1107 // You can add your HTML content here 1108 echo '</div>'; 1109 } 1110 1111 function revnextwoo_custom_order_details_callback() 1112 { 1113 include_once(plugin_dir_path(__FILE__) . 'template/admin/order-count-details.php'); 1058 1059 // -------------- admin dashboard setting moved to class ----------------------- 1060 require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-admin-menus.php'; 1061 if (class_exists('Revnextwoo_Admin_Menus')) { 1062 new Revnextwoo_Admin_Menus(); 1063 } 1064 1065 1066 // Enqueue admin-tab.js only for Review Dashboard 1067 function revnextwoo_enqueue_review_dashboard_assets($hook) { 1068 // Check if we are on the review dashboard admin page 1069 if (isset($_GET['page']) && $_GET['page'] === 'review-dashboard') { 1070 wp_enqueue_script( 1071 'reviewnextwoo-admin-tab', 1072 plugin_dir_url(__FILE__) . 'admin/js/admin-tab.js', 1073 array('jquery'), 1074 '1.0.0', 1075 true 1076 ); 1077 wp_enqueue_style( 1078 'reviewnextwoo-admin-css', 1079 plugin_dir_url(__FILE__) . 'admin/css/revnextwoo-admin.css', 1080 array(), 1081 '1.0.0' 1082 ); 1083 wp_enqueue_style( 1084 'revnextwoo-freemium-css', 1085 plugin_dir_url(__FILE__) . 'admin/css/freemium.css', 1086 array(), 1087 '1.0.0' 1088 ); 1089 } 1090 } 1091 add_action('admin_enqueue_scripts', 'revnextwoo_enqueue_review_dashboard_assets'); 1092 1093 // --- Notification Bell System for WooCommerce Account Dashboard --- 1094 require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-notification-bell.php'; 1095 if (class_exists('Revnextwoo_Notification_Bell')) { 1096 new Revnextwoo_Notification_Bell(); 1114 1097 } 1115 1098 … … 1122 1105 { 1123 1106 include(plugin_dir_path(__FILE__) . 'template/admin/all-question.php'); 1107 } 1108 1109 // Coupon Settings submenu callback 1110 function revnextwoo_coupon_settings_page() 1111 { 1112 1113 ob_start(); 1114 include_once(plugin_dir_path(__FILE__) . 'template/admin/coupon-settings.php'); 1115 $content = ob_get_clean(); 1116 echo $content; 1124 1117 } 1125 1118 … … 1138 1131 <?php 1139 1132 } 1140 // Callback for the "Show Review Box on Singular Pages" field 1141 function revnextwoo_show_review_box_singular_callback() 1142 { 1143 $show_revnextwoo_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', '1'); 1144 ?> 1145 <input type="checkbox" name="revnextwoo_show_revnextwoo_box_singular" id="revnextwoo_show_revnextwoo_box_singular" value="1" 1146 <?php checked($show_revnextwoo_box_singular, 1); ?> /> 1147 <label for="revnextwoo_show_revnextwoo_box_singular">Show review boxes in singular pages only</label> 1148 <?php 1133 // Callback for the "Show Review section enabling" field 1134 function revnextwoo_show_review_box_singular_callback() { 1135 $checked = checked(get_option('revnextwoo_show_revnextwoo_box_singular', 1), 1, false); 1136 echo '<input type="checkbox" name="revnextwoo_show_revnextwoo_box_singular" id="revnextwoo_show_revnextwoo_box_singular" value="1" ' . $checked . ' />'; 1137 echo '<label for="revnextwoo_show_revnextwoo_box_singular">Show Review Section in Product Page</label>'; 1149 1138 } 1150 1139 … … 1156 1145 $choices = array( 1157 1146 'no_one' => 'No one', 1158 'registered_users_and_guests' => 'Registered users and guests',1147 'registered_users_and_guests' => 'Registered users', 1159 1148 'completed_orders' => 'Completed Orders', 1160 1149 ); … … 1173 1162 $rating_images = get_option('revnextwoo_rating_images', 'star'); // Default is 'star' 1174 1163 $icon_choices = array( 1175 'star' => '<span class="dashicons dashicons-star-filled" style="color: gold;"></span> Star',1164 'star' => '<span class="dashicons dashicons-star-filled" style="color: #ffc107;"></span> Star', 1176 1165 'heart' => '<span class="dashicons dashicons-heart" style="color: red;"></span> Heart', 1177 'thumb' => '<span class="dashicons dashicons-thumbs-up" style="color: blue;"></span> Thumbs-up',1178 1166 ); 1179 1167 ?> … … 1186 1174 } 1187 1175 1188 1189 1190 function revnextwoo_review_images_video_upload_option_show_callback() 1191 { 1192 $revnextwoo_images_video_upload = get_option('revnextwoo_review_images_video_upload', '1'); 1193 ?> 1194 <input type="checkbox" name="revnextwoo_revnextwoo_images_video_upload" id="revnextwoo_revnextwoo_images_video_upload" value="1" 1195 <?php checked($revnextwoo_images_video_upload, 1); ?> /> 1196 <label for="revnextwoo_revnextwoo_images_video_upload">Review images video upload option show</label> 1197 <?php 1198 } 1176 function revnextwoo_show_location_callback() 1177 { 1178 ?> 1179 <div class="revnextwoo-upsell-box"> 1180 <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4> 1181 <p><?php _e('Upgrade to the Pro version to show customer location in reviews.', 'review-next-for-woocommerce'); ?></p> 1182 </div> 1183 <?php 1184 } 1185 1186 1187 1199 1188 function revnextwoo_show_question_box_singular_callback() 1200 1189 { … … 1203 1192 <input type="checkbox" name="revnextwoo_show_question_box_singular" id="revnextwoo_show_question_box_singular" value="1" 1204 1193 <?php checked($show_question_box_singular, 1); ?> /> 1205 <label for="revnextwoo_show_question_box_singular">Show Question Box on Singular</label>1194 <label for="revnextwoo_show_question_box_singular">Show Question Answer section in Product Page</label> 1206 1195 <?php 1207 1196 } … … 1211 1200 { 1212 1201 add_settings_section('general_settings_section', 'General Settings', 'revnextwoo_general_settings_section_callback', 'general-settings'); 1213 add_settings_field('show_revnextwoo_box_singular', 'Show Review Box on Singular Pages', 'revnextwoo_show_review_box_singular_callback', 'general-settings', 'general_settings_section'); 1202 1203 //show or hide Review section in prouct page 1204 add_settings_field('show_revnextwoo_box_singular', 'Show Review Section in Product Page', 'revnextwoo_show_review_box_singular_callback', 'general-settings', 'general_settings_section'); 1205 1214 1206 add_settings_field('revnextwoo_allowed_to_rate', 'Who is allowed to rate', 'revnextwoo_allowed_to_rate_callback', 'general-settings', 'general_settings_section'); 1215 1207 add_settings_field('revnextwoo_rating_images', 'Rating Images', 'revnextwoo_rating_images_callback', 'general-settings', 'general_settings_section'); 1216 add_settings_field('revnextwoo_review_images_video_upload', 'Review images video upload option show', 'revnextwoo_review_images_video_upload_option_show_callback', 'general-settings', 'general_settings_section'); 1217 add_settings_field('revnextwoo_show_question_box_singular', 'Show Question Box on Singular', 'revnextwoo_show_question_box_singular_callback', 'general-settings', 'general_settings_section'); 1208 add_settings_field('revnextwoo_show_question_box_singular', 'Show Question Answer section in Product Page', 'revnextwoo_show_question_box_singular_callback', 'general-settings', 'general_settings_section'); 1209 add_settings_field('revnextwoo_show_location', 'Show Reviewer Location', 'revnextwoo_show_location_callback', 'general-settings', 'general_settings_section'); 1210 1211 // Visitor Counter Checkbox 1212 add_settings_field( 1213 'revnextwoo_enable_visitor_counter', 1214 __('Enable Visitor Counter on Product Pages', 'review-next-for-woocommerce'), 1215 'revnextwoo_enable_visitor_counter_callback', 1216 'general-settings', 1217 'general_settings_section' 1218 ); 1218 1219 register_setting('general_settings_group', 'revnextwoo_show_revnextwoo_box_singular', array( 1219 1220 'sanitize_callback' => 'absint' … … 1225 1226 'sanitize_callback' => 'sanitize_text_field' 1226 1227 )); 1227 register_setting('general_settings_group', 'revnextwoo_review_images_video_upload', array( 1228 'sanitize_callback' => 'absint' 1229 )); 1228 1229 1230 1230 register_setting('general_settings_group', 'revnextwoo_show_question_box_singular', array( 1231 1231 'sanitize_callback' => 'absint' 1232 1232 )); 1233 } 1233 1234 // Register Visitor Counter Setting (radio) 1235 register_setting('general_settings_group', 'revnextwoo_visitor_counter_mode', array( 1236 'sanitize_callback' => 'sanitize_text_field' 1237 )); 1238 1239 // --- BEGIN: Review Pagination Settings --- 1240 add_settings_field( 1241 'revnextwoo_enable_review_pagination', 1242 __('Enable Review Pagination', 'review-next-for-woocommerce'), 1243 'revnextwoo_enable_review_pagination_callback', 1244 'general-settings', 1245 'general_settings_section' 1246 ); 1247 // --- END: Review Pagination Settings --- 1248 } 1249 1250 // Visitor Counter Checkbox Callback 1251 function revnextwoo_enable_visitor_counter_callback() { 1252 ?> 1253 <div class="revnextwoo-upsell-box"> 1254 <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4> 1255 <p><?php _e('Upgrade to the Pro version to unlock the Visitor Counter feature.', 'review-next-for-woocommerce'); ?></p> 1256 </div> 1257 <?php 1258 } 1259 1260 1261 1262 // --- BEGIN: Pagination Settings Callbacks --- 1263 function revnextwoo_enable_review_pagination_callback() { 1264 ?> 1265 <div class="revnextwoo-upsell-box"> 1266 <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4> 1267 <p><?php _e('Upgrade to the Pro version to unlock AJAX Review Pagination.', 'review-next-for-woocommerce'); ?></p> 1268 </div> 1269 <?php 1270 } 1271 // --- END: Pagination Settings Callbacks --- 1272 1234 1273 function revnextwoo_general_settings_section_callback() 1235 1274 { … … 1239 1278 // general setting end 1240 1279 1241 function revnextwoo_styling_setting_page() 1242 { 1243 ?> 1280 // Include product views table creation 1281 require_once plugin_dir_path(__FILE__) . 'inc/revnextwoo-product-views-table.php'; 1282 // Include style settings class 1283 require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-style-settings.php'; 1284 1285 // Include analytics class 1286 require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-analytics.php'; 1287 1288 // Include social settings class 1289 require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-social-settings.php'; 1290 1291 // Initialize social settings 1292 if (class_exists('Revnextwoo_Social_Settings')) { 1293 $revnextwoo_social_settings = new Revnextwoo_Social_Settings('review-next-for-woocommerce'); 1294 } 1295 1296 // Styling settings page callback 1297 function revnextwoo_styling_setting_page() { 1298 ?> 1244 1299 <div class="wrap"> 1245 1300 <div class="card" style='max-width:100%'> 1246 <form method="post" action="options.php"> 1301 <?php settings_errors(); ?> 1302 <form method="post" action="options.php" class="style-settings"> 1247 1303 <?php 1248 1304 settings_fields('style_settings_group'); 1249 1305 do_settings_sections('style-settings'); 1250 submit_button( );1306 submit_button('Save Changes', 'primary', 'submit', false); 1251 1307 ?> 1252 1308 </form> 1253 1309 </div> 1254 1310 </div> 1255 <?php 1256 } 1257 1258 function revnextwoo_review_rating_icon_color_callback() 1259 { 1260 $rating_icon_color = get_option('revnextwoo_rating_icon_color', '#cccccccc'); 1261 ?> 1262 <input type="text" name="revnextwoo_rating_icon_color" class="review-color-field" 1263 value="<?php echo esc_attr($rating_icon_color); ?>" data-default-color="#cccccc" /> 1264 <?php 1265 } 1266 function revnextwoo_review_rating_icon_active_color_callback() 1267 { 1268 $rating_icon_active_color = get_option('revnextwoo_rating_icon_active_color', '#ffc700'); 1269 ?> 1270 <input type="text" name="revnextwoo_rating_icon_active_color" class="review-color-field" 1271 value="<?php echo esc_attr($rating_icon_active_color); ?>" data-default-color="#ffc700" /> 1272 <?php 1273 } 1274 function revnextwoo_review_rating_input_box_border_color_callback() 1275 { 1276 $rating_input_box_border_color = get_option('revnextwoo_rating_input_box_border_color', '#F85606'); 1277 ?> 1278 <input type="text" name="revnextwoo_rating_input_box_border_color" class="review-color-field" 1279 value="<?php echo esc_attr($rating_input_box_border_color); ?>" data-default-color="#F85606" /> 1280 <?php 1281 } 1282 function revnextwoo_review_rating_form_button_bg_callback() 1283 { 1284 $rating_form_button_bg = get_option('revnextwoo_rating_form_button_bg', '#F85606'); 1285 ?> 1286 <input type="text" name="revnextwoo_rating_form_button_bg" class="review-color-field" 1287 value="<?php echo esc_attr($rating_form_button_bg); ?>" data-default-color="#F85606" /> 1288 <?php 1289 } 1290 function revnextwoo_review_box_outer_border_callback() 1291 { 1292 $box_outer_border = get_option('revnextwoo_box_outer_border', '#e2eded'); 1293 ?> 1294 <input type="text" name="revnextwoo_box_outer_border" class="review-color-field" 1295 value="<?php echo esc_attr($box_outer_border); ?>" data-default-color="#e2eded" /> 1296 <?php 1297 } 1298 function revnextwoo_review_box_header_footer_bg_callback() 1299 { 1300 $box_header_footer_bg = get_option('revnextwoo_box_header_footer_bg', '#f2f2f2'); 1301 ?> 1302 <input type="text" name="revnextwoo_box_header_footer_bg" class="review-color-field" 1303 value="<?php echo esc_attr($box_header_footer_bg); ?>" data-default-color="#f2f2f2" /> 1304 <?php 1305 } 1306 function revnextwoo_review_item_background_callback() 1307 { 1308 $item_bg = get_option('revnextwoo_item_bg', '#fff'); 1309 ?> 1310 <input type="text" name="revnextwoo_item_bg" class="review-color-field" 1311 value="<?php echo esc_attr($item_bg); ?>" data-default-color="#fff" /> 1312 <?php 1313 } 1314 function revnextwoo_final_score_percentage_bar_background_callback() 1315 { 1316 $final_score_percentage_bar_bg = get_option('revnextwoo_final_score_percentage_bar_bg', '030303'); 1317 ?> 1318 <input type="text" name="revnextwoo_final_score_percentage_bar_bg" class="review-color-field" 1319 value="<?php echo esc_attr($final_score_percentage_bar_bg); ?>" data-default-color="#ff0000" /> 1320 <?php 1321 } 1322 function revnextwoo_link_color_callback() 1323 { 1324 $link_color = get_option('revnextwoo_link_color', '#030303'); 1325 ?> 1326 <input type="text" name="revnextwoo_link_color" class="review-color-field" 1327 value="<?php echo esc_attr($link_color); ?>" data-default-color="#030303" /> 1328 <?php 1329 } 1330 function revnextwoo_link_decoration_callback() 1331 { 1332 $link_decoration = get_option('revnextwoo_review_link_decoration'); 1333 ?> 1334 <input type="text" name="revnextwoo_review_link_decoration" class="review-color-field" 1335 value="<?php echo esc_attr($link_decoration); ?>" data-default-color="#ff0000" /> 1336 <?php 1337 } 1338 function revnextwoo_link_color_on_mouse_over_callback() 1339 { 1340 $revnextwoo_link_color_on_mouse_over = get_option('revnextwoo_link_color_on_mouse_over', '#ffc700'); 1341 ?> 1342 <input type="text" name="revnextwoo_link_color_on_mouse_over" class="review-color-field" 1343 value="<?php echo esc_attr($revnextwoo_link_color_on_mouse_over); ?>" data-default-color="#ffc700" /> 1344 <?php 1345 } 1346 function revnextwoo_link_decoration_on_mouse_over_callback() 1347 { 1348 $link_decoration_hover = get_option('revnextwoo_review_link_decoration_on_mouse_over'); 1349 ?> 1350 <input type="text" name="revnextwoo_review_link_decoration_on_mouse_over" class="review-color-field" 1351 value="<?php echo esc_attr($link_decoration_hover); ?>" data-default-color="#ff0000" /> 1352 <?php 1353 } 1354 1355 function revnextwoo_style_settings_fields() 1356 { 1357 add_settings_section('style_settings_section', 'Style Settings', 'revnextwoo_style_settings_section_callback', 'style-settings'); 1358 add_settings_field('revnextwoo_review_rating_icon_color', 'Review rating form icon Color', 'revnextwoo_review_rating_icon_color_callback', 'style-settings', 'style_settings_section'); 1359 add_settings_field('revnextwoo_review_rating_icon_active_color', 'Review rating form icon active Color', 'revnextwoo_review_rating_icon_active_color_callback', 'style-settings', 'style_settings_section'); 1360 add_settings_field('revnextwoo_review_rating_input_box_border_color', 'Review rating form input box border color', 'revnextwoo_review_rating_input_box_border_color_callback', 'style-settings', 'style_settings_section'); 1361 add_settings_field('revnextwoo_review_rating_form_button_bg', 'Review rating form button Color', 'revnextwoo_review_rating_form_button_bg_callback', 'style-settings', 'style_settings_section'); 1362 add_settings_field('revnextwoo_review_box_outer_border', 'Review Box Outer Border', 'revnextwoo_review_box_outer_border_callback', 'style-settings', 'style_settings_section'); 1363 add_settings_field('revnextwoo_review_box_header_footer_bg', 'Review Box Header & Footer Background', 'revnextwoo_review_box_header_footer_bg_callback', 'style-settings', 'style_settings_section'); 1364 add_settings_field('revnextwoo_review_item_bg', 'Review Item Background', 'revnextwoo_review_item_background_callback', 'style-settings', 'style_settings_section'); 1365 add_settings_field('revnextwoo_final_score_percentage_bar_bg', 'Final Score and Percentage bar Background', 'revnextwoo_final_score_percentage_bar_background_callback', 'style-settings', 'style_settings_section'); 1366 add_settings_field('revnextwoo_link_color', 'Link Color', 'revnextwoo_link_color_callback', 'style-settings', 'style_settings_section'); 1367 add_settings_field('revnextwoo_review_link_decoration', 'Link Decoration', 'revnextwoo_link_decoration_callback', 'style-settings', 'style_settings_section'); 1368 add_settings_field('revnextwoo_link_color_on_mouse_over', 'Link color on mouse over', 'revnextwoo_link_color_on_mouse_over_callback', 'style-settings', 'style_settings_section'); 1369 add_settings_field('revnextwoo_review_link_decoration_on_mouse_over', 'Link decoration on mouse over', 'revnextwoo_link_decoration_on_mouse_over_callback', 'style-settings', 'style_settings_section'); 1370 // Helper function to sanitize color values 1371 $sanitize_color = function ($color) { 1372 $color = sanitize_text_field($color); 1373 // Remove any whitespace 1374 $color = trim($color); 1375 // Validate hex color format 1376 if ( 1377 preg_match('/#([a-fA-F0-9]{3}){1,2}\b/', $color) || 1378 preg_match('/^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(?:,\s*[\d.]+\s*)?\)$/', $color) 1379 ) { 1380 return $color; 1381 } 1382 return ''; 1383 }; 1384 1385 register_setting('style_settings_group', 'revnextwoo_review_rating_icon_color', array( 1386 'sanitize_callback' => $sanitize_color 1387 )); 1388 register_setting('style_settings_group', 'revnextwoo_review_rating_icon_active_color', array( 1389 'sanitize_callback' => $sanitize_color 1390 )); 1391 register_setting('style_settings_group', 'revnextwoo_review_rating_input_box_border_color', array( 1392 'sanitize_callback' => $sanitize_color 1393 )); 1394 register_setting('style_settings_group', 'revnextwoo_review_rating_form_button_bg', array( 1395 'sanitize_callback' => $sanitize_color 1396 )); 1397 register_setting('style_settings_group', 'revnextwoo_review_box_outer_border', array( 1398 'sanitize_callback' => $sanitize_color 1399 )); 1400 register_setting('style_settings_group', 'revnextwoo_review_box_header_footer_bg', array( 1401 'sanitize_callback' => $sanitize_color 1402 )); 1403 register_setting('style_settings_group', 'revnextwoo_review_item_bg', array( 1404 'sanitize_callback' => $sanitize_color 1405 )); 1406 register_setting('style_settings_group', 'revnextwoo_final_score_percentage_bar_bg', array( 1407 'sanitize_callback' => $sanitize_color 1408 )); 1409 register_setting('style_settings_group', 'revnextwoo_link_color', array( 1410 'sanitize_callback' => $sanitize_color 1411 )); 1412 register_setting('style_settings_group', 'revnextwoo_review_link_decoration', array( 1413 'sanitize_callback' => 'sanitize_text_field' 1414 )); 1415 register_setting('style_settings_group', 'revnextwoo_link_color_on_mouse_over', array( 1416 'sanitize_callback' => $sanitize_color 1417 )); 1418 register_setting('style_settings_group', 'revnextwoo_review_link_decoration_on_mouse_over', array( 1419 'sanitize_callback' => 'sanitize_text_field' 1420 )); 1421 } 1422 function revnextwoo_style_settings_section_callback() 1423 { 1424 echo '<p>Customize the appearance of your review plugin.</p>'; 1425 } 1426 add_action('admin_init', 'revnextwoo_style_settings_fields'); 1427 1428 function revnextwoo_typography_setting_page() 1429 { 1430 ?> 1431 <div class="wrap"> 1432 <div class="card" style='max-width:100%'> 1433 <form method="post" action="options.php"> 1434 <?php 1435 settings_fields('typography_settings_group'); 1436 do_settings_sections('typography-settings'); 1437 ?> 1438 <?php submit_button(); ?> 1439 </form> 1440 </div> 1441 </div> 1442 <?php 1443 } 1444 function revnextwoo_review_box_title_callback() 1445 { 1446 $box_title_family = get_option('revnextwoo_box_title_family', 'Arial, sans-serif'); 1447 $google_fonts = array( 1448 'Arial, sans-serif' => 'Arial', 1449 'Helvetica, sans-serif' => 'Helvetica', 1450 'Times New Roman, serif' => 'Times New Roman', 1451 // Add more Google Font families here 1452 ); 1453 $box_title_size = get_option('revnextwoo_box_title_size', '25'); 1454 $box_title_weight = get_option('revnextwoo_box_title_weight', '600'); 1455 ?> 1456 1457 <label for="revnextwoo_box_title_size">Size(px):</label> 1458 <input type="number" name="revnextwoo_box_title_size" min="10" max="50" 1459 value="<?php echo esc_attr($box_title_size); ?>" /> 1460 1461 <label for="revnextwoo_box_title_weight">Weight:</label> 1462 <input type="text" name="revnextwoo_box_title_weight" class="review_box_title_weight" 1463 value="<?php echo esc_attr($box_title_weight); ?>" /> 1464 <label for="revnextwoo_box_title_family">Select Family:</label> 1465 <select name="revnextwoo_box_title_family"> 1466 <?php 1467 foreach ($google_fonts as $font_value => $font_name) { 1468 $is_selected = $box_title_family === $font_value ? ' selected="selected"' : ''; 1469 echo sprintf( 1470 '<option value="%s"%s>%s</option>', 1471 esc_attr($font_value), 1472 esc_attr($is_selected), 1473 esc_html($font_name) 1474 ); 1475 } 1476 ?> 1477 </select> 1478 1479 <?php 1480 } 1481 1482 function revnextwoo_review_box_header_footer_callback() 1483 { 1484 $header_footer_family = get_option('revnextwoo_box_header_footer_family', 'Arial, sans-serif'); 1485 $google_fonts = array( 1486 'Arial, sans-serif' => 'Arial', 1487 'Helvetica, sans-serif' => 'Helvetica', 1488 'Times New Roman, serif' => 'Times New Roman', 1489 // Add more Google Font families here 1490 ); 1491 $header_footer_size = get_option('revnextwoo_box_header_footer_size', '19'); 1492 $header_footer_weight = get_option('revnextwoo_box_header_footer_weight', '500'); 1493 ?> 1494 <label for="revnextwoo_box_header_footer_size">Size(px):</label> 1495 <input type="number" name="revnextwoo_box_header_footer_size" min="10" max="50" 1496 value="<?php echo esc_attr($header_footer_size); ?>" /> 1497 1498 <label for="revnextwoo_box_header_footer_weight">Weight:</label> 1499 <input type="text" name="revnextwoo_box_header_footer_weight" class="review_box_header_footer_weight" 1500 value="<?php echo esc_attr($header_footer_weight); ?>" /> 1501 <label for="revnextwoo_box_header_footer_family">Select Family:</label> 1502 <select name="revnextwoo_box_header_footer_family"> 1503 <?php 1504 foreach ($google_fonts as $font_value => $font_name) { 1505 $is_selected = $header_footer_family === $font_value ? ' selected="selected"' : ''; 1506 echo sprintf( 1507 '<option value="%s"%s>%s</option>', 1508 esc_attr($font_value), 1509 esc_attr($is_selected), 1510 esc_html($font_name) 1511 ); 1512 } 1513 ?> 1514 </select> 1515 1516 <?php 1517 } 1518 function revnextwoo_review_summary_callback() 1519 { 1520 $summary_family = get_option('revnextwoo_summary_family', 'Arial, sans-serif'); 1521 $google_fonts = array( 1522 'Arial, sans-serif' => 'Arial', 1523 'Helvetica, sans-serif' => 'Helvetica', 1524 'Times New Roman, serif' => 'Times New Roman', 1525 // Add more Google Font families here 1526 ); 1527 $summary_size = get_option('revnextwoo_summary_size', '15'); 1528 $summary_weight = get_option('revnextwoo_summary_weight', '400'); 1529 ?> 1530 1531 <label for="revnextwoo_summary_size">Size(px):</label> 1532 <input type="number" name="revnextwoo_summary_size" min="10" max="50" 1533 value="<?php echo esc_attr($summary_size); ?>" /> 1534 1535 <label for="revnextwoo_summary_weight">Weight:</label> 1536 <input type="text" name="revnextwoo_summary_weight" class="review_summary_weight" 1537 value="<?php echo esc_attr($summary_weight); ?>" /> 1538 <label for="revnextwoo_summary_family">Select Family:</label> 1539 <select name="revnextwoo_summary_family"> 1540 <?php 1541 foreach ($google_fonts as $font_value => $font_name) { 1542 $is_selected = $summary_family === $font_value ? ' selected="selected"' : ''; 1543 echo sprintf( 1544 '<option value="%s"%s>%s</option>', 1545 esc_attr($font_value), 1546 esc_attr($is_selected), 1547 esc_html($font_name) 1548 ); 1549 } 1550 ?> 1551 </select> 1552 1553 <?php 1554 } 1555 function revnextwoo_total_score_callback() 1556 { 1557 $total_score_family = get_option('revnextwoo_total_score_family', 'Arial, sans-serif'); 1558 $google_fonts = array( 1559 'Arial, sans-serif' => 'Arial', 1560 'Helvetica, sans-serif' => 'Helvetica', 1561 'Times New Roman, serif' => 'Times New Roman', 1562 // Add more Google Font families here 1563 ); 1564 $total_score_size = get_option('revnextwoo_total_score_size', '48'); 1565 $total_score_weight = get_option('revnextwoo_total_score_weight', '400'); 1566 ?> 1567 1568 <label for="revnextwoo_total_score_size">Size(px):</label> 1569 <input type="text" name="revnextwoo_total_score_size" class="total_score_size" 1570 value="<?php echo esc_attr($total_score_size); ?>" /> 1571 1572 <label for="revnextwoo_total_score_weight">Weight:</label> 1573 <input type="text" name="revnextwoo_total_score_weight" class="total_score_weight" 1574 value="<?php echo esc_attr($total_score_weight); ?>" /> 1575 <label for="revnextwoo_total_score_family">Select Family:</label> 1576 <select name="revnextwoo_total_score_family"> 1577 <?php 1578 foreach ($google_fonts as $font => $font_name) { 1579 $selected = ($total_score_family === $font) ? 'selected' : ''; 1580 echo sprintf( 1581 '<option value="%s"%s>%s</option>', 1582 esc_attr($font), 1583 esc_attr($selected), 1584 esc_html($font_name) 1585 ); 1586 } 1587 ?> 1588 </select> 1589 1590 <?php 1591 } 1592 function revnextwoo_user_rating_icon_callback() 1593 { 1594 $user_rating_icon_family = get_option('revnextwoo_user_rating_icon_family', 'Arial, sans-serif'); 1595 $google_fonts = array( 1596 'Arial, sans-serif' => 'Arial', 1597 'Helvetica, sans-serif' => 'Helvetica', 1598 'Times New Roman, serif' => 'Times New Roman', 1599 // Add more Google Font families here 1600 ); 1601 $user_rating_icon_size = get_option('revnextwoo_user_rating_icon_size', '15'); 1602 $user_rating_icon_weight = get_option('revnextwoo_user_rating_icon_weight', '900'); 1603 ?> 1604 1605 <label for="revnextwoo_user_rating_icon_size">Size(px):</label> 1606 <input type="text" name="revnextwoo_user_rating_icon_size" class="user_rating_icon_size" 1607 value="<?php echo esc_attr($user_rating_icon_size); ?>" /> 1608 1609 <label for="revnextwoo_user_rating_icon_weight">Weight:</label> 1610 <input type="text" name="revnextwoo_user_rating_icon_weight" class="user_rating_icon_weight" 1611 value="<?php echo esc_attr($user_rating_icon_weight); ?>" /> 1612 <label for="revnextwoo_user_rating_icon_family">Select Family:</label> 1613 <select name="revnextwoo_user_rating_icon_family"> 1614 <?php 1615 foreach ($google_fonts as $font => $font_name) { 1616 $selected = ($user_rating_icon_family === $font) ? 'selected' : ''; 1617 echo sprintf( 1618 '<option value="%s"%s>%s</option>', 1619 esc_attr($font), 1620 esc_attr($selected), 1621 esc_html($font_name) 1622 ); 1623 } 1624 ?> 1625 </select> 1626 1627 <?php 1628 } 1629 function revnextwoo_typography_settings_fields() 1630 { 1631 add_settings_section('typography_settings_section', 'Typography Settings', 'revnextwoo_typography_settings_section_callback', 'typography-settings'); 1632 add_settings_field('user_rating_icon', 'User Rating icon', 'revnextwoo_user_rating_icon_callback', 'typography-settings', 'typography_settings_section'); 1633 add_settings_field('review_box_title', 'Review box title', 'revnextwoo_review_box_title_callback', 'typography-settings', 'typography_settings_section'); 1634 add_settings_field('review_box_header_footer', 'Review box header & footer', 'revnextwoo_review_box_header_footer_callback', 'typography-settings', 'typography_settings_section'); 1635 add_settings_field('total_score', 'Average Total Score', 'revnextwoo_total_score_callback', 'typography-settings', 'typography_settings_section'); 1636 add_settings_field('review_summary', 'Review Summary', 'revnextwoo_review_summary_callback', 'typography-settings', 'typography_settings_section'); 1637 1638 // Helper function to sanitize font family 1639 $sanitize_font_family = function ($font) { 1640 $allowed_fonts = array( 1641 'Arial, sans-serif', 1642 'Helvetica, sans-serif', 1643 'Times New Roman, serif' 1644 ); 1645 $font = sanitize_text_field($font); 1646 return in_array($font, $allowed_fonts) ? $font : 'Arial, sans-serif'; 1647 }; 1648 1649 // Helper function to sanitize font size and weight 1650 $sanitize_numeric = function ($value) { 1651 return absint($value); 1652 }; 1653 1654 // Register settings for typography 1655 register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_size', array( 1656 'sanitize_callback' => $sanitize_numeric 1657 )); 1658 register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_weight', array( 1659 'sanitize_callback' => $sanitize_numeric 1660 )); 1661 register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_family', array( 1662 'sanitize_callback' => $sanitize_font_family 1663 )); 1664 register_setting('typography_settings_group', 'revnextwoo_review_box_title_size', array( 1665 'sanitize_callback' => $sanitize_numeric 1666 )); 1667 register_setting('typography_settings_group', 'revnextwoo_review_box_title_weight', array( 1668 'sanitize_callback' => $sanitize_numeric 1669 )); 1670 register_setting('typography_settings_group', 'revnextwoo_review_box_title_family', array( 1671 'sanitize_callback' => $sanitize_font_family 1672 )); 1673 register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_size', array( 1674 'sanitize_callback' => $sanitize_numeric 1675 )); 1676 register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_weight', array( 1677 'sanitize_callback' => $sanitize_numeric 1678 )); 1679 register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_family', array( 1680 'sanitize_callback' => $sanitize_font_family 1681 )); 1682 register_setting('typography_settings_group', 'revnextwoo_total_score_size', array( 1683 'sanitize_callback' => $sanitize_numeric 1684 )); 1685 register_setting('typography_settings_group', 'revnextwoo_total_score_weight', array( 1686 'sanitize_callback' => $sanitize_numeric 1687 )); 1688 register_setting('typography_settings_group', 'revnextwoo_total_score_family', array( 1689 'sanitize_callback' => $sanitize_font_family 1690 )); 1691 register_setting('typography_settings_group', 'revnextwoo_summary_size', array( 1692 'sanitize_callback' => $sanitize_numeric 1693 )); 1694 register_setting('typography_settings_group', 'revnextwoo_summary_weight', array( 1695 'sanitize_callback' => $sanitize_numeric 1696 )); 1697 register_setting('typography_settings_group', 'revnextwoo_summary_family', array( 1698 'sanitize_callback' => $sanitize_font_family 1699 )); 1700 } 1701 function revnextwoo_typography_settings_section_callback() 1702 { 1703 echo '<p>Customize the typography of your review plugin.</p>'; 1704 } 1705 add_action('admin_init', 'revnextwoo_typography_settings_fields'); 1706 1707 1708 function revnextwoo_advanced_setting_page() 1709 { 1710 echo '<div class="wrap">'; 1711 echo '<h2>Advanced Settings</h2>'; 1712 1713 echo '<h2 class="nav-tab-wrapper review-advanced-setting-wrapper">'; 1714 echo '<a class="nav-tab nav-tab-active" href="#tab-1">Review Format</a>'; 1715 echo '<a class="nav-tab" href="#tab-2">Email Reminder</a>'; 1716 echo '<a class="nav-tab" href="#tab-3">Order Count</a>'; 1717 echo '<a class="nav-tab" href="#tab-4">Top Order Customer</a>'; 1718 echo '</h2>'; 1719 1720 echo '<div class="tab-content">'; 1721 echo '<div id="tab-1" class="tab-pane active">'; 1722 revnextwoo_advanced_tab_1_settings_page(); 1723 echo '</div>'; 1724 1725 echo '<div id="tab-2" class="tab-pane">'; 1726 revnextwoo_advanced_tab_2_settings_page(); 1727 echo '</div>'; 1728 1729 echo '<div id="tab-3" class="tab-pane">'; 1730 revnextwoo_advanced_tab_3_settings_page(); 1731 echo '</div>'; 1732 1733 echo '<div id="tab-4" class="tab-pane">'; 1734 revnextwoo_advanced_tab_4_settings_page(); 1735 echo '</div>'; 1736 echo '</div>'; 1737 1738 echo '</div>'; 1739 } 1740 1741 function revnextwoo_advanced_tab_1_settings_page() 1742 { 1743 ?> 1744 <div class="wrap"> 1745 <form method="post" action="options.php"> 1746 <?php 1747 settings_fields('advanced_tab1_settings_group'); 1748 do_settings_sections('advanced-tab1-settings'); 1749 submit_button(); 1750 ?> 1751 </form> 1752 </div> 1753 <?php 1754 } 1755 function revnextwoo_advanced_tab1_settings_fields() 1756 { 1757 // Tab 1 1758 add_settings_section('advanced_setting_tab1_section', 'Review Formate Setting', 'revnextwoo_advanced_setting_tab1_section_callback', 'advanced-tab1-settings'); 1759 add_settings_field('review_rating_formate', 'Review Formate', 'revnextwoo_review_rating_formate_callback', 'advanced-tab1-settings', 'advanced_setting_tab1_section'); 1760 register_setting('advanced_tab1_settings_group', 'revnextwoo_review_rating_formate', array( 1761 'sanitize_callback' => 'sanitize_text_field' 1762 )); 1763 } 1764 1765 add_action('admin_init', 'revnextwoo_advanced_tab1_settings_fields'); 1766 function revnextwoo_advanced_setting_tab1_section_callback() 1767 { 1768 echo '<p>Choose your preferred rating format:</p>'; 1769 } 1770 1771 function revnextwoo_review_rating_formate_callback() 1772 { 1773 $image_url = plugins_url('admin/images/', __FILE__); 1774 1775 1776 $rating_formate = get_option('revnextwoo_review_rating_formate', 'half_star'); // Default is 'star' 1777 // Get image attachments for review formats 1778 $icon_choices = array( 1779 'no_rating_star' => wp_get_attachment_image(get_option('no_rating_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')), 1780 'half_star' => wp_get_attachment_image(get_option('half_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')), 1781 'full_star' => wp_get_attachment_image(get_option('full_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')), 1782 'five_star' => wp_get_attachment_image(get_option('five_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')), 1783 ); 1784 1785 // Fallback to direct image URLs if attachment IDs not set 1786 if (empty($icon_choices['no_rating_star'])) { 1787 $attachment_id = attachment_url_to_postid($image_url . 'no_rating.png'); 1788 if ($attachment_id) { 1789 $icon_choices['no_rating_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')); 1790 } else { 1791 // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images 1792 $icon_choices['no_rating_star'] = '<img class="user_img" src="' . esc_url($image_url . 'no_rating.png') . '" alt="review-format-img">'; 1793 } 1794 } 1795 if (empty($icon_choices['half_star'])) { 1796 $attachment_id = attachment_url_to_postid($image_url . 'half_star.png'); 1797 if ($attachment_id) { 1798 $icon_choices['half_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')); 1799 } else { 1800 // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images 1801 $icon_choices['half_star'] = '<img class="user_img" src="' . esc_url($image_url . 'half_star.png') . '" alt="review-format-img">'; 1802 } 1803 } 1804 if (empty($icon_choices['full_star'])) { 1805 $attachment_id = attachment_url_to_postid($image_url . 'full_star.png'); 1806 if ($attachment_id) { 1807 $icon_choices['full_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')); 1808 } else { 1809 // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images 1810 $icon_choices['full_star'] = '<img class="user_img" src="' . esc_url($image_url . 'full_star.png') . '" alt="review-format-img">'; 1811 } 1812 } 1813 if (empty($icon_choices['five_star'])) { 1814 $attachment_id = attachment_url_to_postid($image_url . 'five_star.png'); 1815 if ($attachment_id) { 1816 $icon_choices['five_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')); 1817 } else { 1818 // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images 1819 $icon_choices['five_star'] = '<img class="user_img" src="' . esc_url($image_url . 'five_star.png') . '" alt="review-format-img">'; 1820 } 1821 } 1822 ?> 1823 <?php foreach ($icon_choices as $value => $label) { ?> 1824 <div class="input_group"> 1825 <input type="radio" id="revnextwoo_review_rating_formate<?php echo esc_attr($value); ?>" name="revnextwoo_review_rating_formate" 1826 value="<?php echo esc_attr($value); ?>" <?php checked($rating_formate, $value); ?> /> 1827 <label for="revnextwoo_review_rating_formate<?php echo esc_attr($value); ?>"><?php echo wp_kses_post($label); ?></label><br> 1828 </div> 1829 <?php } ?> 1830 <?php 1831 } 1832 function revnextwoo_advanced_tab_2_settings_page() 1833 { 1834 ?> 1835 <div class="wrap"> 1836 <form method="post" action="options.php"> 1837 <?php 1838 settings_fields('advanced_tab2_settings_group'); 1839 do_settings_sections('advanced-tab2-settings'); 1840 submit_button(); 1841 ?> 1842 </form> 1843 </div> 1844 <?php 1845 } 1846 1311 <?php 1312 } 1847 1313 function revnextwoo_advanced_tab_3_settings_page() 1848 1314 { … … 1850 1316 } 1851 1317 1318 // Advanced Settings (CAS) - moved to class-revnextwoo-advanced-settings.php 1319 require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-advanced-settings.php'; 1320 if (class_exists('RevNextWoo_Advanced_Settings')) { 1321 new RevNextWoo_Advanced_Settings(); 1322 } 1323 1324 1852 1325 function revnextwoo_advanced_tab_4_settings_page() 1853 1326 { 1854 1327 include plugin_dir_path(__FILE__) . 'template/admin/top-order-customer.php'; 1855 }1856 function revnextwoo_advanced_tab2_settings_fields()1857 {1858 // Tab 21859 add_settings_section('advanced_setting_tab2_section', 'Email Reminder', 'revnextwoo_advanced_setting_tab2_section_callback', 'advanced-tab2-settings');1860 add_settings_field('review_email_reminder', 'Set Reminder Time', 'revnextwoo_review_email_reminder_callback', 'advanced-tab2-settings', 'advanced_setting_tab2_section');1861 register_setting('advanced_tab2_settings_group', 'revnextwoo_review_email_reminder', array(1862 'sanitize_callback' => function ($value) {1863 $allowed_values = array('3', '7');1864 $value = sanitize_text_field($value);1865 // Allow custom values that are positive integers1866 if (!in_array($value, $allowed_values) && !ctype_digit($value)) {1867 return '3'; // Default value1868 }1869 return $value;1870 }1871 ));1872 }1873 1874 add_action('admin_init', 'revnextwoo_advanced_tab2_settings_fields');1875 function revnextwoo_advanced_setting_tab2_section_callback()1876 {1877 echo '<p>Set your no review user reminder time:</p>';1878 1328 } 1879 1329 … … 1948 1398 add_action('admin_enqueue_scripts', 'revnextwoo_enqueue_color_picker'); 1949 1399 1950 // Add this in your theme's functions.php or a custom plugin file 1951 add_action('wp_ajax_revnextwoo_submit_question_reply', 'revnextwoo_submit_question_reply'); 1952 add_action('wp_ajax_nopriv_revnextwoo_submit_question_reply', 'revnextwoo_submit_question_reply'); 1953 1954 function revnextwoo_submit_question_reply() 1955 { 1956 // Verify nonce 1957 if (!isset($_POST['question_reply_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_reply_nonce'])), 'submit_question_reply_nonce')) { 1958 wp_send_json_error('Security check failed'); 1959 return; 1960 } 1961 1962 // Validate required fields 1963 if (!isset($_POST['comment_id'], $_POST['reply_content'], $_POST['post_id'])) { 1964 wp_send_json_error('Missing required fields'); 1965 return; 1966 } 1967 1968 // Sanitize and validate input 1969 $post_id = isset($_POST['post_id']) ? intval(wp_unslash($_POST['post_id'])) : 0; 1970 $comment_id = isset($_POST['comment_id']) ? intval(wp_unslash($_POST['comment_id'])) : 0; 1971 $reply_content = isset($_POST['reply_content']) ? wp_kses_post(wp_unslash($_POST['reply_content'])) : ''; 1972 1973 if (!$post_id || !$comment_id || empty($reply_content)) { 1974 wp_send_json_error('Invalid input data'); 1975 return; 1976 } 1977 $current_user_id = get_current_user_id(); 1978 $current_user = get_user_by('ID', $current_user_id); 1979 $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name; 1980 $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email; 1981 $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url; 1982 // Sanitize and validate data as needed 1983 $commentdata = array( 1984 'comment_post_ID' => $post_id, 1985 'comment_parent' => $comment_id, 1986 'comment_content' => $reply_content, 1987 'comment_approved' => 1, // Approve the comment by default 1988 'comment_author' => $display_name, 1989 'comment_author_email' => $user_email, 1990 'comment_author_url' => $author_url, 1991 'comment_type' => 'question_reply', 1992 'user_id' => $current_user_id, 1993 ); 1994 // Insert the reply 1995 $reply_id = wp_insert_comment($commentdata); 1996 1997 if ($reply_id) { 1998 // Return a response, e.g., a success message 1999 wp_send_json_success('question reply added successfully'); 2000 } else { 2001 // Return a response, e.g., a success message 2002 wp_send_json_success('Failed to add reply.'); 2003 } 2004 wp_die(); 2005 } 2006 2007 2008 2009 function revnextwoo_status_update_question() 2010 { 2011 // Verify nonce 2012 if (!isset($_POST['status_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['status_nonce'])), 'question_status_nonce')) { 2013 wp_send_json_error('Security check failed'); 2014 return; 2015 } 2016 2017 // Validate required fields 2018 if (!isset($_POST['question_id']) || !isset($_POST['status'])) { 2019 wp_send_json_error('Missing required fields'); 2020 return; 2021 } 2022 2023 // Sanitize and validate input 2024 $comment_id = isset($_POST['question_id']) ? intval(wp_unslash($_POST['question_id'])) : 0; 2025 $action = isset($_POST['status']) ? sanitize_text_field(wp_unslash($_POST['status'])) : ''; 2026 2027 if (!$comment_id || empty($action)) { 2028 wp_send_json_error('Invalid input data'); 2029 return; 2030 } 2031 $status_mapping = array( 2032 'Approved' => '0', 2033 'Unapproved' => '1', 2034 'Spam' => 'spam', 2035 'Trash' => 'trash', 2036 ); 2037 2038 // Check if the provided action is valid. 2039 if (array_key_exists($action, $status_mapping)) { 2040 $comment_status = $status_mapping[$action]; 2041 2042 // Create an array with multiple parameters to send back. 2043 $response_data = array( 2044 'message' => 'Question status updated successfully.', 2045 'comment_id' => $comment_id, 2046 'status' => $action, 2047 ); 2048 2049 // Update the comment status. 2050 if (wp_set_comment_status($comment_id, $comment_status)) { 2051 $response_data['success'] = true; 2052 } else { 2053 $response_data['success'] = false; 2054 $response_data['message'] = 'Failed to update question status.'; 2055 } 2056 2057 // Send the JSON response with the array. 2058 wp_send_json($response_data); 2059 } else { 2060 wp_send_json_error('Invalid status action.'); 2061 } 2062 } 2063 2064 // Hook the above function to WordPress AJAX action 2065 add_action('wp_ajax_revnextwoo_status_update_question', 'revnextwoo_status_update_question'); 2066 add_action('wp_ajax_nopriv_revnextwoo_status_update_question', 'revnextwoo_status_update_question'); 2067 2068 2069 2070 // Add AJAX action to handle the comment update 2071 add_action('wp_ajax_revnextwoo_question_update', 'revnextwoo_question_update_callback'); 2072 add_action('wp_ajax_nopriv_revnextwoo_question_update', 'revnextwoo_question_update_callback'); 2073 2074 // Callback function to handle the comment update 2075 function revnextwoo_question_update_callback() 2076 { 2077 // Verify nonce 2078 if (!isset($_POST['update_question_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['update_question_nonce'])), 'update_question_nonce')) { 2079 wp_send_json_error('Security check failed'); 2080 } 2081 2082 // Get the data sent via AJAX 2083 $comment_id = isset($_POST['commentId']) ? intval(wp_unslash($_POST['commentId'])) : 0; 2084 $question_content = isset($_POST['questionContent']) ? sanitize_text_field(wp_unslash($_POST['questionContent'])) : ''; 2085 $user_name = isset($_POST['userName']) ? sanitize_text_field(wp_unslash($_POST['userName'])) : ''; 2086 $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : ''; 2087 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : ''; 2088 2089 // Update the comment data (replace with your own logic) 2090 if ($comment_id > 0) { 2091 $updated_data = array( 2092 'comment_ID' => $comment_id, 2093 'comment_content' => $question_content, 2094 'comment_author' => $user_name, 2095 'comment_author_email' => $email, 2096 'comment_author_IP' => $ip, 2097 ); 2098 2099 $updated = wp_update_comment($updated_data); 2100 // Create an array with multiple parameters to send back. 2101 $response_data = array( 2102 'message' => 'Question updated successfully.', 2103 'data' => $question_content, 2104 'comment_id' => $comment_id, 2105 ); 2106 if ($updated) { 2107 $response_data['success'] = true; 2108 } else { 2109 $response_data['success'] = false; 2110 $response_data['message'] = 'Failed to update question status.'; 2111 } 2112 // Send the JSON response with the array. 2113 wp_send_json($response_data); 2114 } else { 2115 wp_send_json_error(array('message' => esc_html__('Invalid comment ID.', 'review-next-for-woocommerce'))); 2116 } 2117 2118 wp_die(); 2119 } 2120 2121 2122 2123 // view reply 2124 add_action('wp_ajax_revnextwoo_get_question_replies', 'revnextwoo_get_question_replies'); 2125 add_action('wp_ajax_nopriv_revnextwoo_get_question_replies', 'revnextwoo_get_question_replies'); 2126 2127 function revnextwoo_get_question_replies() 2128 { 2129 // Verify nonce 2130 if (!isset($_POST['question_replies_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_replies_nonce'])), 'get_question_replies_nonce')) { 2131 wp_send_json_error('Security check failed'); 2132 return; 2133 } 2134 2135 if (isset($_POST['question_id'])) { 2136 $questionId = intval(wp_unslash($_POST['question_id'])); 2137 $args = array( 2138 'parent' => $questionId, 2139 'status' => 'approve', 2140 'order' => 'ASC', 2141 'type' => 'question_reply', 2142 ); 2143 $replies = get_comments($args); 2144 if (!empty($replies)) { 2145 foreach ($replies as $reply) { 2146 echo '<textarea class="wp-editor-area" 2147 rows="2" cols="20" 2148 name="update-qustion-content" 2149 id="update-qustion-content"> 2150 ' . esc_textarea($reply->comment_content) . ' 2151 </textarea>'; 2152 } 2153 } else { 2154 echo 'No replies found.'; 2155 } 2156 } 2157 wp_die(); 2158 } 2159 2160 // function revnextwoo_mailtrap($phpmailer) 2161 // { 2162 // // Only configure mailtrap if credentials are defined 2163 // if (!defined('REVIEW_SMTP_HOST') || !defined('REVIEW_SMTP_USER') || !defined('REVIEW_SMTP_PASS')) { 2164 // return; 2165 // } 2166 2167 // $phpmailer->isSMTP(); 2168 // $phpmailer->Host = REVIEW_SMTP_HOST; 2169 // $phpmailer->SMTPAuth = true; 2170 // $phpmailer->Port = defined('REVIEW_SMTP_PORT') ? REVIEW_SMTP_PORT : 2525; 2171 // $phpmailer->Username = REVIEW_SMTP_USER; 2172 // $phpmailer->Password = REVIEW_SMTP_PASS; 2173 // } 2174 2175 // add_action('phpmailer_init', 'revnextwoo_mailtrap'); 2176 2177 1400 //send email callback start here-------- 2178 1401 function revnextwoo_send_emails_callback() 2179 1402 { … … 2248 1471 2249 1472 add_action('wp_ajax_revnextwoo_send_emails', 'revnextwoo_send_emails_callback'); 1473 1474 // Enqueue frontend scripts for AJAX review sorting 1475 add_action('wp_enqueue_scripts', 'revnextwoo_enqueue_review_scripts'); 1476 function revnextwoo_enqueue_review_scripts() { 1477 if (is_product()) { 1478 wp_enqueue_script( 1479 'revnextwoo-reviews', 1480 plugin_dir_url(__FILE__) . 'public/js/revnextwoo-reviews.js', 1481 array('jquery'), 1482 '1.0.0', 1483 true 1484 ); 1485 wp_enqueue_script( 1486 'revnextwoo-review-layout', 1487 plugin_dir_url(__FILE__) . 'public/js/revnextwoo-review-layout.js', 1488 array('jquery'), 1489 '1.0.0', 1490 true 1491 ); 1492 // Ensure questions pagination + submission JS is present on product pages 1493 wp_enqueue_script( 1494 'revnextwoo-questions-pagination', 1495 plugin_dir_url(__FILE__) . 'public/js/revnextwoo-questions-pagination.js', 1496 array('jquery'), 1497 '1.0.0', 1498 true 1499 ); 1500 // Localize ajax url and product id for questions script 1501 global $product; 1502 $product_id = is_object($product) ? $product->get_id() : (get_the_ID() ?: 0); 1503 wp_localize_script('revnextwoo-questions-pagination', 'revnextwoo_questions_ajax', array( 1504 'ajax_url' => admin_url('admin-ajax.php'), 1505 'product_id' => $product_id, 1506 )); 1507 wp_localize_script('revnextwoo-reviews', 'revnextwoo_ajax', array( 1508 'ajax_url' => admin_url('admin-ajax.php'), 1509 'nonce' => wp_create_nonce('revnextwoo_vote_nonce') 1510 )); 1511 // Add a second object for video upload nonce (do not remove the above) 1512 wp_localize_script('revnextwoo-reviews', 'revnextwoo_vars', array( 1513 'ajax_url' => admin_url('admin-ajax.php'), 1514 'nonce' => wp_create_nonce('revnextwoo_nonce') 1515 )); 1516 // Pass layout to JS via body class 1517 add_filter('body_class', function($classes) { 1518 $layout = get_option('revnextwoo_review_layout_style', 'default'); 1519 $classes[] = 'revnextwoo-layout-' . esc_attr($layout); 1520 return $classes; 1521 }); 1522 // Add data attribute for JS detection 1523 add_action('wp_head', function() { 1524 $layout = esc_attr(get_option('revnextwoo_review_layout_style', 'default')); 1525 echo "<script>document.body.setAttribute('data-revnextwoo-layout', '" . $layout . "');</script>"; 1526 }); 1527 } 1528 } 1529 1530 1531 // AJAX handler for sorting product reviews 1532 add_action('wp_ajax_revnextwoo_sort_reviews', 'revnextwoo_sort_reviews_ajax'); 1533 add_action('wp_ajax_nopriv_revnextwoo_sort_reviews', 'revnextwoo_sort_reviews_ajax'); 1534 function revnextwoo_sort_reviews_ajax() { 1535 // Security check 1536 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'revnextwoo_vote_nonce')) { 1537 wp_send_json_error(['message' => __('Invalid request.', 'review-next-for-woocommerce')]); 1538 wp_die(); 1539 } 1540 $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0; 1541 $sort = isset($_POST['sort']) ? sanitize_text_field($_POST['sort']) : 'newest'; 1542 if (!$product_id) { 1543 wp_send_json_error(['message' => __('Invalid product.', 'review-next-for-woocommerce')]); 1544 wp_die(); 1545 } 1546 // Fetch approved reviews 1547 $args = array( 1548 'post_id' => $product_id, 1549 'status' => 'approve', 1550 'type' => 'review', 1551 'meta_query' => array(), 1552 'orderby' => 'comment_date', 1553 'order' => 'DESC', 1554 ); 1555 // Sort logic 1556 if ($sort === 'highest') { 1557 $args['meta_key'] = 'rating'; 1558 $args['orderby'] = 'meta_value_num'; 1559 $args['order'] = 'DESC'; 1560 } elseif ($sort === 'lowest') { 1561 $args['meta_key'] = 'rating'; 1562 $args['orderby'] = 'meta_value_num'; 1563 $args['order'] = 'ASC'; 1564 } elseif ($sort === 'helpful') { 1565 $args['meta_key'] = 'likes'; 1566 $args['orderby'] = 'meta_value_num'; 1567 $args['order'] = 'DESC'; 1568 } elseif ($sort === 'media') { 1569 $args['meta_query'][] = array( 1570 'key' => 'review_images', 1571 'compare' => 'EXISTS', 1572 ); 1573 } 1574 $reviews = get_comments($args); 1575 ob_start(); 1576 if (!empty($reviews)) : ?> 1577 <?php $layout = get_option('revnextwoo_review_layout_style', 'default'); ?> 1578 <div class="revnextwoo_reviews_container revnextwoo_layout_<?php echo esc_attr($layout); ?>" data-product-id="<?php echo esc_attr($product_id); ?>"> 1579 <?php foreach ($reviews as $review) : 1580 $review_rating = intval(get_comment_meta($review->comment_ID, 'rating', true)); 1581 $verified = wc_review_is_from_verified_owner($review->comment_ID); 1582 $review_timestamp = strtotime($review->comment_date_gmt ?: $review->comment_date); 1583 if (!$review_timestamp) { 1584 // Fallback to WP helper if parsing fails 1585 $review_timestamp = (int) get_comment_time('U', false, true, $review->comment_ID); 1586 } 1587 $review_date = human_time_diff($review_timestamp, current_time('timestamp')) . ' ago'; 1588 $review_images = get_comment_meta($review->comment_ID, 'review_images', true); 1589 ?> 1590 <?php 1591 $grid_colors = ['#ffb6b6','#6a7ee7','#8df7b1','#f9d56e','#f97e7e','#7ee8fa','#b6ffb6','#c6a1ff']; 1592 $masonry_color = ''; 1593 $style_attr = ''; 1594 if($layout === 'default'){ 1595 $style_attr = 'margin-bottom:20px;'; 1596 $style_attr .= 'border-radius:10px;'; 1597 $style_attr .= 'border:1px solid #ccc;'; 1598 $style_attr .= 'overflow:hidden;'; 1599 $item_bg = get_option('revnextwoo_review_item_bg', '#fff'); 1600 $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';'; 1601 } 1602 if ($layout === 'grid') { 1603 $masonry_color = $grid_colors[array_rand($grid_colors)]; 1604 $style_attr = '--masonry-color: ' . $masonry_color . ';'; 1605 } 1606 if ($layout === 'card') { 1607 $item_bg = get_option('revnextwoo_review_item_bg', '#fff'); 1608 $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';'; 1609 } 1610 ?> 1611 <div class="revnextwoo_review_item"<?php echo !empty($style_attr) ? ' style="' . $style_attr . '"' : ''; ?>> 1612 <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?> 1613 <div class="revnextwoo_review_header" style="background-color: <?php echo esc_attr($header_bg); ?>;"> 1614 <div class="revnextwoo_reviewer_info"> 1615 <div class="revnextwoo_reviewer_avatar"> 1616 <?php 1617 $user_avatar_url = ''; 1618 if (!empty($review->user_id)) { 1619 $user_avatar_url = get_user_meta($review->user_id, 'revnextwoo_profile_avatar', true); 1620 } 1621 if (!$user_avatar_url) { 1622 $settings = get_option('revnextwoo_social_settings'); 1623 $user_avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : ''; 1624 } 1625 if ($user_avatar_url) { 1626 echo '<img src="' . esc_url($user_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />'; 1627 } else { 1628 $avatar_key = !empty($review->user_id) ? intval($review->user_id) : (isset($review->comment_author_email) ? $review->comment_author_email : ''); 1629 echo get_avatar($avatar_key, 50); 1630 } 1631 ?> 1632 </div> 1633 <div class="revnextwoo_reviewer_details"> 1634 <?php if ($verified) : ?> 1635 <span class="revnextwoo_verified_badge">Verified Purchase</span> 1636 <?php endif; ?> 1637 <h4 class="revnextwoo_reviewer_name"><b><?php echo esc_html($review->comment_author); ?></b></h4> 1638 <div class="revnextwoo_review_meta"> 1639 <div class="revnextwoo_review_rating"> 1640 <?php 1641 $rating_images = get_option('revnextwoo_rating_images', 'star'); 1642 $icon_classes = array( 1643 'star' => array( 1644 'filled' => 'fas fa-star icon_filled', 1645 'empty' => 'far fa-star icon_empty', 1646 ), 1647 'heart' => array( 1648 'filled' => 'fas fa-heart icon_filled', 1649 'empty' => 'far fa-heart icon_empty', 1650 ), 1651 'thumb' => array( 1652 'filled' => 'fas fa-thumbs-up icon_filled', 1653 'empty' => 'far fa-thumbs-up icon_empty', 1654 ), 1655 ); 1656 $current_icon_set = isset($icon_classes[$rating_images]) ? $icon_classes[$rating_images] : $icon_classes['star']; 1657 for ($i = 1; $i <= 5; $i++) : 1658 $iconType = ($i <= $review_rating) ? 'filled' : 'empty'; 1659 $iconClass = isset($current_icon_set[$iconType]) ? $current_icon_set[$iconType] : ''; 1660 ?> 1661 <i class="<?php echo esc_attr($iconClass); ?>"></i> 1662 <?php endfor; ?> 1663 </div> 1664 </div> 1665 <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span> 1666 <?php 1667 // Location display logic (same as comment-item-display.php) 1668 $reviewer_city = get_comment_meta($review->comment_ID, 'reviewer_city', true); 1669 $reviewer_country = get_comment_meta($review->comment_ID, 'reviewer_country', true); 1670 $location_display = ''; 1671 if (!empty($reviewer_city) || !empty($reviewer_country)) { 1672 $location_parts = array(); 1673 if (!empty($reviewer_city)) { 1674 $location_parts[] = esc_html($reviewer_city); 1675 } 1676 if (!empty($reviewer_country)) { 1677 $location_parts[] = esc_html($reviewer_country); 1678 } 1679 $location_display = '<span class="review-location">' . implode(', ', $location_parts) . '</span>'; 1680 } 1681 if (!empty($location_display)) { 1682 echo $location_display; 1683 } 1684 ?> 1685 </div> 1686 </div> 1687 </div> 1688 <div class="revnextwoo_review_content"> 1689 <?php 1690 // Get the review title from comment meta 1691 $review_title = get_comment_meta($review->comment_ID, 'title', true); 1692 // If no title exists (for backward compatibility), use a default 1693 if (empty($review_title)) { 1694 $review_title = 'Great Product'; 1695 } 1696 ?> 1697 <div class="revnextwoo_text_box"> 1698 <h3 class="revnextwoo_review_title"><b><?php echo esc_html($review_title); ?></b></h3> 1699 <p class="revnextwoo_review_text" data-full-text="<?php echo esc_attr(wp_strip_all_tags($review->comment_content)); ?>"> 1700 <?php 1701 $content = wp_strip_all_tags($review->comment_content); 1702 $words = explode(' ', $content); 1703 if (count($words) > 12) { 1704 $short_content = implode(' ', array_slice($words, 0, 12)); 1705 echo esc_html($short_content . '...'); 1706 } else { 1707 echo esc_html($content); 1708 } 1709 ?> 1710 </p> 1711 <?php if (count($words) > 12) : ?> 1712 <span class="revnextwoo_read_more">Read More</span> 1713 <?php endif; ?> 1714 </div> 1715 1716 <?php if (!empty($review_images) && is_array($review_images)) : ?> 1717 <div class="revnextwoo_review_images"> 1718 <?php foreach ($review_images as $image_id) : ?> 1719 <div class="revnextwoo_review_image"> 1720 <a href="<?php echo esc_url(wp_get_attachment_url($image_id)); ?>" data-lightbox="example-set" ><?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?></a> 1721 </div> 1722 <?php endforeach; ?> 1723 </div> 1724 <?php endif; ?> 1725 <?php 1726 $video_id = get_comment_meta($review->comment_ID, 'review_video', true); 1727 if ($video_id) { 1728 $video_url = wp_get_attachment_url($video_id); 1729 if ($video_url) { 1730 echo '<div class="revnextwoo_review_video">'; 1731 echo '<video controls width="320" height="240" style="max-width:100%;margin-top:10px;">'; 1732 echo '<source src="' . esc_url($video_url) . '" type="video/mp4">'; 1733 echo __('Your browser does not support the video tag.', 'review-next-for-woocommerce'); 1734 echo '</video>'; 1735 echo '</div>'; 1736 } 1737 } 1738 ?> 1739 </div> 1740 <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?> 1741 <div class="revnextwoo_review_actions" style="background-color: <?php echo esc_attr($header_bg); ?>"> 1742 <?php 1743 $settings = get_option('revnextwoo_social_settings'); 1744 $show_thumbs_up = isset($settings['enable_thumbs_up']) && $settings['enable_thumbs_up'] === 'yes'; 1745 $show_thumbs_down = isset($settings['enable_thumbs_down']) && $settings['enable_thumbs_down'] === 'yes'; 1746 ?> 1747 <?php if ($show_thumbs_up): ?> 1748 <button class="revnextwoo_like_btn revnextwoo_vote_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>" data-vote-type="up"> 1749 <span class="dashicons dashicons-thumbs-up"></span> 1750 <span class="revnextwoo_like_count"><?php echo get_comment_meta($review->comment_ID, 'likes', true) ?: 0; ?></span> 1751 </button> 1752 <?php endif; ?> 1753 1754 1755 <div class="revnextwoo_social_sharing"> 1756 <a href="#" class="revnextwoo_share_btn revnextwoo_facebook_share" title="Share on Facebook"> 1757 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="#1877F2"><path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z"/></svg> 1758 </a> 1759 1760 </div> 1761 1762 1763 </div> 1764 <span class="revnextwoo_vote_message" style="display:none;"></span> 1765 </div> 1766 <?php endforeach; ?> 1767 </div> 1768 <?php else : ?> 1769 <p class="revnextwoo_no_reviews">No reviews yet. Be the first to review!</p> 1770 <?php endif; ?> 1771 <?php 1772 $html = ob_get_clean(); 1773 wp_send_json_success(['html' => $html]); 1774 wp_die(); 1775 } 1776 1777 // AJAX handler for review voting (like/dislike) 1778 add_action('wp_ajax_revnextwoo_vote_review', 'revnextwoo_vote_review_handler'); 1779 function revnextwoo_vote_review_handler() { 1780 // Only allow for logged-in users 1781 if (!is_user_logged_in()) { 1782 wp_send_json_error(['message' => __('You must be logged in to vote.', 'review-next-for-woocommerce')]); 1783 } 1784 // Nonce check 1785 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'revnextwoo_vote_nonce')) { 1786 wp_send_json_error(['message' => __('Invalid request.', 'review-next-for-woocommerce')]); 1787 } 1788 $user_id = get_current_user_id(); 1789 $review_id = isset($_POST['review_id']) ? intval($_POST['review_id']) : 0; 1790 $vote_type = isset($_POST['vote_type']) ? sanitize_text_field($_POST['vote_type']) : ''; 1791 if (!$review_id || !in_array($vote_type, ['up', 'down'])) { 1792 wp_send_json_error(['message' => __('Invalid data.', 'review-next-for-woocommerce')]); 1793 } 1794 // Track users who voted in comment meta as an array of user IDs 1795 $voted_users = get_comment_meta($review_id, 'revnextwoo_voted_users', true); 1796 if (!is_array($voted_users)) { 1797 $voted_users = []; 1798 } 1799 if (in_array($user_id, $voted_users)) { 1800 wp_send_json_error(['message' => __('You have already voted once, Thanks', 'review-next-for-woocommerce')]); 1801 } 1802 // Update vote count 1803 if ($vote_type === 'up') { 1804 $likes = intval(get_comment_meta($review_id, 'likes', true)); 1805 $likes++; 1806 update_comment_meta($review_id, 'likes', $likes); 1807 } else { 1808 $dislikes = intval(get_comment_meta($review_id, 'dislikes', true)); 1809 $dislikes++; 1810 update_comment_meta($review_id, 'dislikes', $dislikes); 1811 } 1812 // Store user as voted 1813 $voted_users[] = $user_id; 1814 update_comment_meta($review_id, 'revnextwoo_voted_users', $voted_users); 1815 // Return new counts 1816 $likes = intval(get_comment_meta($review_id, 'likes', true)); 1817 $dislikes = intval(get_comment_meta($review_id, 'dislikes', true)); 1818 wp_send_json_success([ 1819 'likes' => $likes, 1820 'dislikes' => $dislikes 1821 ]); 1822 } 1823 // AJAX handler for hard delete of question and replies 1824 add_action('wp_ajax_revnextwoo_hard_delete_question', function() { 1825 if (!current_user_can('administrator')) { 1826 wp_send_json_error('Unauthorized'); 1827 } 1828 global $wpdb; 1829 if (!isset($_POST['question_id'])) { 1830 wp_send_json_error('No question ID'); 1831 } 1832 $question_id = intval($_POST['question_id']); 1833 $qa_table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : ''; 1834 if (!$qa_table) { 1835 wp_send_json_error('Table not found'); 1836 } 1837 // Delete the question 1838 $wpdb->delete($qa_table, ['id' => $question_id]); 1839 // Delete all replies to this question 1840 $wpdb->delete($qa_table, ['parent_id' => $question_id]); 1841 wp_send_json_success('Deleted'); 1842 }); 1843 -
review-next-for-woocommerce/trunk/template/admin/all-question.php
r3315734 r3457384 1 1 <?php 2 // Handle actions (approve, unapprove, spam, trash) for admin table 3 if (isset($_GET['action'], $_GET['id']) && in_array($_GET['action'], ['approve','unapprove','spam','trash'])) { 4 global $wpdb; 5 $table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : ''; 6 $id = intval($_GET['id']); 7 $status = $_GET['action']; 8 if ($status === 'trash') { 9 $wpdb->delete($table, ['id'=>$id]); 10 $wpdb->delete($table, ['parent_id'=>$id]); // delete all replies 11 } else { 12 $wpdb->update($table, ['status'=>$status], ['id'=>$id]); 13 } 14 wp_redirect(remove_query_arg(['action','id'])); 15 exit; 16 } 17 2 18 // Exit if accessed directly 3 19 if (! defined('ABSPATH')) … … 42 58 'pending' => 0, // Initialize to 0 43 59 'approved' => 0, // Initialize to 0 44 'spam' => 0, // Initialize to 045 'trash' => 0, // Initialize to 046 60 ); 47 48 // Calculate counts for statuses other than 'all'49 foreach ($data as $item) {50 if ($item['status'] === 'hold') {51 $question_statuses['pending']++;52 } elseif ($item['status'] === 'approve') {53 $question_statuses['approved']++;54 } elseif ($item['status'] === 'spam') {55 $question_statuses['spam']++;56 } elseif ($item['status'] === 'trash') {57 $question_statuses['trash']++;58 }59 }60 61 61 62 // Output the sub-navigation menu … … 116 117 { 117 118 return array( 118 ' id' => 'ID',119 'sn' => 'S/N', 119 120 'user_name' => 'User Name', 120 'question_content' => 'Question Content', 121 'question_content' => 'Questions', 122 'view_reply' => 'View Reply', 121 123 'product' => 'Product', 122 'question_date' => ' QuestionDate',123 ' view_reply' => 'View Reply',124 'question_date' => 'Date', 125 'status' => 'Status', 124 126 ); 125 127 } … … 128 130 { 129 131 return array( 130 'id' => array('id', false),131 132 'user_name' => array('user_name', false), 132 'product' => 'Product',133 133 'question_date' => array('question_date', false), 134 134 'view_reply' => array('view_reply', false), 135 'status' => array('status', false), 135 136 ); 136 137 } … … 138 139 private function table_data() 139 140 { 140 $ args = array(141 'status' => array('approve', 'hold'),142 'order' => 'ASC',143 'type' => 'question',144 );145 $questions = get_comments($args);141 $rows = array(); 142 if (class_exists('Revnextwoo_QA_DB')) { 143 global $wpdb; 144 $qa_table = Revnextwoo_QA_DB::table_name(); 145 $rows = $wpdb->get_results("SELECT * FROM {$qa_table} WHERE type = 'question' ORDER BY created_at DESC LIMIT 500"); 146 } 146 147 $data = array(); 147 foreach ($questions as $question) { 148 $product = wc_get_product($question->comment_post_ID); 149 $questionDate = gmdate('F j, Y', strtotime($question->comment_date)); 150 $authorUsername = get_comment_author($question->comment_ID); 151 $authorEmail = get_comment_author_email($question->comment_ID); 152 $authorIP = get_comment_author_IP($question->comment_ID); 153 $product = wc_get_product($question->comment_post_ID); 148 foreach ($rows as $row) { 149 $product = wc_get_product($row->product_id); 154 150 $product_name = $product ? $product->get_name() : 'N/A'; 155 // Get the product URL156 151 $product_url = $product ? get_permalink($product->get_id()) : ''; 157 152 $view_product_link = $product_url ? '<a href="' . esc_url($product_url) . '">View Product</a>' : 'N/A'; 158 $args = array( 159 'parent' => $question->comment_ID, 160 'status' => 'approve', 161 'order' => 'ASC', 162 'type' => 'question_reply', 163 'count' => true 153 $replyCount = ($row->type === 'question' && class_exists('Revnextwoo_QA_DB')) ? Revnextwoo_QA_DB::count_replies($row->id) : 0; 154 $data[] = array( 155 'id' => $row->id, 156 'type' => ($row->type === 'question' && $replyCount == 0) 157 ? '<span class="no-reply-highlight">' . esc_html($row->type) . '</span>' 158 : esc_html($row->type), 159 'user_name' => esc_html($row->author_name) . '<br><span style="font-size:11px;color:#888">' . esc_html($row->author_email) . '</span>', 160 'question_content' => '<span id="question_content" class="question_content_' . $row->id . '">' . esc_html($row->content) . '</span>', 161 'product' => $product_name . '<br>' . $view_product_link, 162 'question_date' => $row->created_at, 163 'status' => $row->status, 164 'view_reply' => $row->type === 'question' 165 ? '<span class="post-com-count-wrapper post-com-count-' . $row->id . '"> 166 <a href="#" class="post-com-count post-com-count-approved"> 167 <span data-question-id="' . $row->id . '" class="view-reply dashicons dashicons-visibility"></span> 168 <span class="dashicons dashicons-admin-comments"></span> 169 ' . $replyCount . ' 170 </a> 171 </span>' 172 : '', 164 173 ); 165 $replyCount = get_comments($args); 166 $data[] = array( 167 'id' => $question->comment_ID, 168 'product_id' => $product->get_id(), 169 'user_name' => '<span id="username">' . $authorUsername . '</span>' . '<br>' . '<span id="user-email">' . $authorEmail . '</span>' . '<br>' . '<span id="user-ip">' . $authorIP . '</span>', 170 'question_content' => '<span id="question_content" class="question_content_' . $question->comment_ID . '">' . $question->comment_content . '</span>', 171 'product' => $product_name . '<br>' . $view_product_link, 172 'question_date' => $questionDate, 173 'status' => $question->comment_approved, 174 'view_reply' => '<span class="post-com-count-wrapper post-com-count-' . $question->comment_ID . '"> 175 <a href="#" class="post-com-count post-com-count-approved"> 176 <span data-question-id="' . $question->comment_ID . '" class="view-reply dashicons dashicons-visibility"></span> 177 <span class="dashicons dashicons-admin-comments"></span> 178 ' . $replyCount . ' 179 </a> 180 </span>', 181 182 ); 183 } 184 174 } 185 175 return $data; 186 176 } 187 177 188 public function column_default($item, $column_name) 189 { 190 191 return $item[$column_name]; 192 } 193 194 public function column_product_name($item) 195 { 196 return $item['product_name']; 197 } 198 199 public function column_question_date($item) 200 { 201 return $item['question_date']; 202 } 203 204 205 178 public function column_sn($item) { 179 // Calculate serial number based on pagination 180 $current_page = $this->get_pagenum(); 181 $items_per_page = $this->get_items_per_page($this->_args['plural'] . '_per_page', 10); 182 static $sn = 0; 183 if ($sn == 0) { 184 $sn = ($current_page - 1) * $items_per_page + 1; 185 } 186 return $sn++; 187 } 188 public function column_user_name($item) { 189 return $item['user_name']; 190 } 206 191 public function column_question_content($item) 207 192 { 208 $comment_status = $item['status'] === '1' ? 'Approved' : 'Unapproved'; 209 210 $actions = array( 211 $comment_status => '<a href="#" class="status_update_question" data-question-id="' . $item['id'] . '" data-action="' . $comment_status . '">' . $comment_status . '</a>', 212 'reply' => '<button type="button" data-comment-id="' . $item['id'] . '" data-post-id="' . $item['product_id'] . '" data-action="replyto" class="vim-r-question-reply comment-inline button-link" aria-expanded="false" aria-label="Reply to this review">Reply</button>', 213 'quick_edit' => '<button type="button" data-comment-id="' . $item['id'] . '" data-post-id="' . $item['product_id'] . '" data-action="question_edit" class="vim-q comment-inline button-link" aria-expanded="false" aria-label="Quick edit this review inline">Quick Edit</button>', 214 'spam' => '<a href="#" class="spam-question status_update_question" data-question-id="' . $item['id'] . '" data-action="Spam">Spam</a>', 215 'trash' => '<a href="#" class="trash-question status_update_question" data-question-id="' . $item['id'] . '" data-action="Trash">Trash</a>', 216 ); 217 218 return sprintf('%1$s %2$s', $item['question_content'], $this->row_actions($actions)); 219 } 220 } 221 222 193 // Define possible statuses and their labels 194 $statuses = [ 195 'approve' => 'Approved', 196 'unapprove' => 'Unapproved', 197 'spam' => 'Spam', 198 ]; 199 $current_status = isset($statuses[$item['status']]) ? $statuses[$item['status']] : ucfirst($item['status']); 200 $actions = ''; 201 foreach(['approve','unapprove','spam','trash'] as $act) { 202 // Only show the action if it's not the current status, or always show trash 203 if ($act !== $item['status'] || $act === 'trash') { 204 $actions .= '<a href="' . esc_url(add_query_arg(['action' => $act, 'id' => $item['id']])) . '">' . ucfirst($act) . '</a> '; 205 } 206 } 207 return sprintf('%1$s <div class="question-actions">%2$s</div>', $item['question_content'], $actions); 208 } 209 public function column_product($item) { 210 return $item['product']; 211 } 212 public function column_question_date($item) { 213 $parts = revnextwoo_get_relative_time_parts($item['question_date'], 'F j, Y g:i a'); 214 $label = !empty($parts['relative']) ? $parts['relative'] : $parts['absolute']; 215 $title = !empty($parts['absolute']) ? ' title="' . esc_attr($parts['absolute']) . '"' : ''; 216 return '<span class="revnextwoo-question-date"' . $title . '>' . esc_html($label) . '</span>'; 217 } 218 public function column_status($item) { 219 return $item['status']; 220 } 221 public function column_view_reply($item) { 222 global $wpdb; 223 if (!isset($item['id'])) return ''; 224 $qa_table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : ''; 225 $replies = array(); 226 if ($qa_table) { 227 $replies = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$qa_table} WHERE parent_id = %d AND type = 'question_reply' ORDER BY created_at ASC", $item['id'])); 228 } 229 if ($replies && count($replies) > 0) { 230 $reply_html = ''; 231 foreach ($replies as $reply) { 232 $reply_html .= '<div class="admin-reply-content"><strong>Admin:</strong> ' . esc_html($reply->content) . '</div>'; 233 } 234 return $reply_html; 235 } else { 236 return '<span class="no-reply-message"><h4>No Reply</h4></span>'; 237 } 238 } 239 240 // Highlight entire row if this is a question with no replies 241 public function single_row($item) { 242 $highlight_class = ''; 243 // If the type cell contains the no-reply-highlight span, add class to <tr> 244 if (isset($item['type']) && strpos($item['type'], 'no-reply-highlight') !== false) { 245 $highlight_class = 'no-reply-highlight'; 246 } 247 echo '<tr class="' . esc_attr($highlight_class) . '">'; 248 list($columns, $hidden) = $this->get_column_info(); 249 foreach ($columns as $column_name => $column_display_name) { 250 $class = "class=\"column-$column_name\""; 251 $style = in_array($column_name, $hidden) ? ' style="display:none;"' : ''; 252 if (method_exists($this, 'column_' . $column_name)) { 253 echo "<td $class$style>" . call_user_func(array( 254 $this, 255 'column_' . $column_name 256 ), $item) . "</td>"; 257 } else { 258 echo "<td $class$style>" . $this->column_default($item, $column_name) . "</td>"; 259 } 260 } 261 echo '</tr>'; 262 } 263 } 223 264 224 265 function revnextwoo_custom_table_plugin_page() 225 266 { 226 267 echo '<div class="wrap"><h2>Question list</h2>'; 268 echo '<div style="float:right;margin-top:-32px;"> 269 <span id="pending-bell" style="position:relative;cursor:pointer;font-size:24px;"> 270 <span class="dashicons dashicons-bell"></span> 271 <span id="pending-count" style="position:absolute;top:-10px;right:-10px;background:#e53935;color:#fff;border-radius:50%;font-size:13px;padding:2px 7px;min-width:18px;text-align:center;">0</span> 272 </span> 273 </div>'; 274 227 275 echo '<form method="post" action="">'; 228 276 wp_nonce_field('bulk-product_questions', '_wpnonce', true); -
review-next-for-woocommerce/trunk/template/admin/email-reminder.php
r3315734 r3457384 431 431 } 432 432 } 433 434 435 436 437 function revnextwoo_email_reminder_table_plugin_page()438 {439 $output = '<div class="wrap"><h2>No Review Order list</h2>';440 $output .= '<form method="post" action="">';441 $output .= wp_nonce_field('rnfw_email_reminder', 'rnfw_er_nonce');442 $output .= wp_nonce_field('bulk-email-reminders', '_wpnonce', true, false);443 444 $custom_table = new Revnextwoo_Email_Reminder_Table_Plugin();445 $custom_table->search_box('Search Orders', 'order_id');446 $custom_table->prepare_items();447 ob_start();448 $custom_table->display();449 $output .= ob_get_clean();450 $output .= '</form>';451 $output .= '</div>';452 453 echo wp_kses_post($output);454 }455 456 457 458 revnextwoo_email_reminder_table_plugin_page(); -
review-next-for-woocommerce/trunk/template/admin/top-order-customer.php
r3315734 r3457384 5 5 } 6 6 7 if (!class_exists('WP_List_Table')) {8 require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');9 }10 11 class REVNEXTWOO_Top_Order_Customer_Table_Plugin extends WP_List_Table12 {13 public function __construct()14 {15 parent::__construct(array(16 'singular' => 'top-order-customer',17 'plural' => 'top-order-customers',18 'ajax' => true,19 ));20 }21 22 public function prepare_items()23 {24 // Define the columns and data25 $columns = $this->get_columns();26 $hidden = array();27 $sortable = $this->get_sortable_columns();28 $data = $this->table_data();29 // Verify nonce for form submission30 if (isset($_POST['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'bulk-' . $this->_args['plural'])) {31 // Get the search query from the form submission and sanitize it32 $search_query = isset($_POST['s']) ? sanitize_text_field(wp_unslash($_POST['s'])) : '';33 } else {34 $search_query = '';35 }36 37 // If a search query is provided, filter the data38 if (!empty($search_query)) {39 $data = array_filter($this->table_data(), function ($item) use ($search_query) {40 return stripos($item['sl'], $search_query) !== false;41 });42 } else {43 $data = $this->table_data();44 }45 // Get the current page number and items per page from the URL46 $current_page = $this->get_pagenum();47 $items_per_page = 10; // You can adjust the number of items per page48 49 // Calculate the total number of items50 $total_items = count($this->table_data());51 52 // Create a paginated data array based on the current page53 $data = array_slice($this->table_data(), (($current_page - 1) * $items_per_page), $items_per_page);54 55 // Set up the pagination56 $this->set_pagination_args(array(57 'total_items' => $total_items,58 'per_page' => $items_per_page,59 ));60 $this->_column_headers = array($columns, $hidden, $sortable);61 $this->items = $data;62 }63 64 public function get_columns()65 {66 return array(67 'cb' => '<input type="checkbox" />',68 'sl' => 'SL NO',69 'user_name' => 'User-Name',70 'user_email' => 'User-Email',71 'total_order' => 'Total-Order',72 'total' => 'Total',73 'view' => 'View',74 );75 }76 77 78 protected function column_cb($item)79 {80 return sprintf(81 '<input type="checkbox" name="sl[]" value="%s" />',82 $item['sl']83 );84 }85 86 public function get_sortable_columns()87 {88 return array(89 'sl' => array('id', false),90 'user_name' => array('user', false),91 'total_order' => array('total_order', false),92 'total' => array('total', false),93 );94 }95 96 public function process_bulk_action() {}97 98 99 private function table_data()100 {101 // Check if WooCommerce is active and function exists102 if (!function_exists('wc_get_orders') || !class_exists('WooCommerce')) {103 return array(); // Return empty array if WooCommerce is not available104 }105 106 // Define the arguments for the order query107 $args = array(108 'numberposts' => -1,109 'post_type' => 'shop_order',110 'post_status' => array('wc-delivered'),111 );112 113 // Get the orders based on the query114 $orders = wc_get_orders($args);115 $data = array();116 117 foreach ($orders as $key => $order) {118 $user_id = $order->get_customer_id();119 $user = get_user_by('ID', $user_id);120 121 // Check if user exists and has required data122 if (!$user || !isset($user->user_login) || !isset($user->user_email)) {123 continue; // Skip this order if user data is invalid124 }125 126 $user_name = $user->user_login;127 $user_email = $user->user_email;128 129 $data[] = array(130 'sl' => $key + 1,131 'user_name' => '<span id="username">' . esc_html($user_name) . '</span>',132 'user_email' => '<span id="user-email">' . esc_html($user_email) . '</span>',133 'total' => $order->get_total(),134 'total_order' => '',135 'view' => 'dd',136 );137 }138 139 return $data;140 }141 142 143 public function column_default($item, $column_name)144 {145 switch ($column_name) {146 case 'view':147 return '<a href="' . wp_nonce_url('?page=order-details&order_id=' . $item['sl'], 'view_order_details') . '">View Details</a>';148 default:149 return $item[$column_name];150 }151 }152 153 154 public function column_order_status($item)155 {156 return $item['order_status'];157 }158 159 public function column_order_date($item)160 {161 return $item['order_date'];162 }163 }164 165 166 167 168 7 function revnextwoo_top_order_customer_table_plugin_page() 169 8 { 170 $output = '<div class="wrap"><h2> Most Ordered Customers</h2>';9 $output = '<div class="wrap"><h2>Qualified Cuatomers</h2>'; 171 10 172 11 // Check if WooCommerce is active 173 12 if (!function_exists('wc_get_orders') || !class_exists('WooCommerce')) { 174 $output .= '<div class="notice notice-warning"><p>';175 $output .= '<strong>WooCommerce Required:</strong> This feature requires WooCommerce to be installed and activated.';176 $output .= '</p></div>';13 // $output .= '<div class="notice notice-warning"><p>'; 14 // $output .= '<strong>WooCommerce Required:</strong> This feature requires WooCommerce to be installed and activated.'; 15 // $output .= '</p></div>'; 177 16 $output .= '</div>'; 178 17 echo wp_kses_post($output); … … 180 19 } 181 20 182 $output .= '<form method="post" action="">'; 183 $output .= wp_nonce_field('bulk-top-order-customers', '_wpnonce', true, false); 184 185 $custom_table = new REVNEXTWOO_Top_Order_Customer_Table_Plugin(); 186 $custom_table->search_box('Search Orders', 'sl'); 187 $custom_table->prepare_items(); 21 // --- Filtered Customer List --- 22 require_once dirname(__DIR__, 2) . '/admin/class-revnextwoo-cas-list-table.php'; 188 23 ob_start(); 189 $custom_table->display(); 24 $table = new RevNextWoo_CAS_List_Table(); 25 $table->prepare_items(); 26 $table->display(); 190 27 $output .= ob_get_clean(); 191 $output .= '</form>';192 $output .= '</div>';193 194 28 echo wp_kses_post($output); 195 29 } -
review-next-for-woocommerce/trunk/template/frontend/average-rating.php
r3317206 r3457384 13 13 $current_user_id = $current_user->ID; 14 14 15 // Make sure we have a valid WC_Product instance 16 if (!isset($product) || !is_a($product, 'WC_Product')) { 17 $maybe_product = wc_get_product(get_the_ID()); 18 if ($maybe_product && is_a($maybe_product, 'WC_Product')) { 19 $product = $maybe_product; 20 } else { 21 return; // Cannot proceed without a valid product 22 } 23 } 24 15 25 // Get product reviews 26 // Pagination disabled - showing all reviews 16 27 $args = array( 17 28 'post_id' => $product->get_id(), … … 47 58 } 48 59 49 // Get rating counts 50 $rating_counts = array( 51 1 => $product->get_rating_count(1), 52 2 => $product->get_rating_count(2), 53 3 => $product->get_rating_count(3), 54 4 => $product->get_rating_count(4), 55 5 => $product->get_rating_count(5), 56 ); 57 $total_ratings = array_sum($rating_counts); 60 // Build fresh rating counts from approved WooCommerce reviews (ensures immediate accuracy) 61 $rating_counts = array(1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0); 62 $rating_sum = 0; 63 $rating_total = 0; 64 $rating_review_ids = get_comments(array( 65 'post_id' => $product->get_id(), 66 'status' => 'approve', 67 'type' => 'review', 68 'meta_key' => 'rating', 69 'meta_compare' => 'EXISTS', 70 'fields' => 'ids', 71 'number' => 0, // all 72 )); 73 foreach ($rating_review_ids as $cid) { 74 $r = intval(get_comment_meta($cid, 'rating', true)); 75 if ($r >= 1 && $r <= 5) { 76 $rating_counts[$r] += 1; 77 $rating_sum += $r; 78 $rating_total += 1; 79 } 80 } 81 $total_ratings = $rating_total; 58 82 59 83 // Calculate the percentage for each star rating … … 69 93 $percentage_5_star = $rating_percentages[5]; 70 94 71 // Get average rating 72 $average_rating = $ product->get_average_rating();95 // Get average rating (computed live for accuracy) 96 $average_rating = $rating_total > 0 ? ($rating_sum / $rating_total) : 0; 73 97 $average_rating_formatted = number_format($average_rating, 1); 74 98 … … 97 121 98 122 <!-- Average Rating Summary --> 99 <div class="revnextwoo_rating_summary"> 123 <?php $rating_summary_bg = get_option('revnextwoo_review_rating_input_box_border_color', '#ffffff'); ?> 124 <div class="revnextwoo_rating_summary" style="background-color: <?php echo esc_attr($rating_summary_bg); ?>"> 100 125 <div class="revnextwoo_rating_overview"> 101 126 <div class="revnextwoo_average_rating"> … … 125 150 $count = isset($rating_counts[$i]) ? $rating_counts[$i] : 0; 126 151 ?> 152 <?php //echo $percentage ?> 127 153 <div class="revnextwoo_rating_bar"> 128 154 <span class="revnextwoo_star_label"><?php echo $i; ?> star</span> … … 140 166 <div class="revnextwoo_review_form_wrapper"> 141 167 <h3>Write a Review</h3> 142 <?php if (is_user_logged_in()) : ?> 168 <?php 169 $allowed_to_rate = get_option('revnextwoo_allowed_to_rate', 'registered_users_and_guests'); 170 $current_user_id = get_current_user_id(); 171 $user_can_see_form = false; 172 if ($allowed_to_rate === 'no_one') { 173 $user_can_see_form = false; 174 } elseif ($allowed_to_rate === 'registered_users_and_guests') { 175 $user_can_see_form = is_user_logged_in(); 176 } elseif ($allowed_to_rate === 'completed_orders') { 177 if ($current_user_id && isset($product)) { 178 $current_user = get_user_by('ID', $current_user_id); 179 $user_email = isset($current_user->user_email) ? $current_user->user_email : ''; 180 if (function_exists('wc_customer_bought_product') && wc_customer_bought_product($user_email, $current_user_id, $product->get_id())) { 181 $user_can_see_form = true; 182 } 183 } 184 } 185 ?> 186 <?php if ($user_can_see_form) : ?> 143 187 <?php if (!$user_has_reviewed) : 144 188 // Debug: Show if user is logged in but hasn't reviewed … … 147 191 } 148 192 ?> 149 <form class="revnextwoo_review_form" method="post" enctype="multipart/form-data"> 193 <?php 194 // Get max photos from options with a default of 6 195 $max_photos = get_option('revnextwoo_max_photos', 6); 196 ?> 197 <form id="revnextwoo-review-form" class="revnextwoo_review_form" method="post" enctype="multipart/form-data" data-max-photos="<?php echo esc_attr($max_photos); ?>"> 150 198 <?php wp_nonce_field('submit_review_' . $product->get_id(), 'review_nonce'); ?> 151 199 <input type="hidden" name="product_id" value="<?php echo esc_attr($product->get_id()); ?>"> … … 163 211 <div class="revnextwoo_form_group"> 164 212 <label for="review_title">Review Title</label> 165 <input type="text" id="review_title" name="review_title" placeholder="Summarize your review" required>213 <input type="text" id="review_title" name="review_title" maxlength="40" placeholder="Summarize your review"> 166 214 </div> 167 215 168 216 <div class="revnextwoo_form_group"> 169 217 <label for="review_text">Your Review</label> 170 <textarea id="review_text" name="review_text" rows="5" placeholder="Share your experience with this product"required></textarea>218 <textarea id="review_text" name="review_text" rows="5" required></textarea> 171 219 </div> 172 220 173 <div class="revnextwoo_form_group"> 174 <label>Add Photos (Optional)</label> 175 <div class="revnextwoo_media_upload"> 176 <div class="revnextwoo_media_preview"></div> 177 <label for="review_images" class="revnextwoo_upload_btn"> 178 <span class="dashicons dashicons-format-image"></span> 179 <span>Add Images</span> 180 <input type="file" id="review_images" name="review_images[]" multiple accept="image/*" style="display: none;"> 181 </label> 182 <small>Max 5 images. Supported formats: JPG, PNG, GIF. Max size: 2MB per image.</small> 183 </div> 184 </div> 185 221 <!-- image upload section start here --> 222 <?php if (get_option('revnextwoo_enable_photo_upload', 'yes') === 'yes') : ?> 223 <!-- image upload section --> 224 <div class="revnextwoo_form_group"> 225 <label>Add Photos (Optional)</label> 226 <div class="revnextwoo_media_upload"> 227 <div style="width:50%;height:50%;overflow:hidden;" class="revnextwoo_media_preview"></div> 228 <label for="review_images" class="revnextwoo_upload_btn"> 229 <span class="dashicons dashicons-format-image"></span> 230 <span>Add Images</span> 231 <input type="file" id="review_images" name="review_images[]" multiple accept="image/*" style="display: none;"> 232 </label> 233 <small>Max <?php echo esc_html(get_option('revnextwoo_max_photos', 3)); ?> images. Supported formats: JPG, PNG, GIF.</small> 234 </div> 235 </div> 236 <?php endif; ?> 237 <!-- Video Review Upload --> 238 <?php 239 $revnextwoo_video_enabled = get_option('revnextwoo_enable_video_upload', 'yes'); 240 $revnextwoo_max_video_size = 50; // Hardcoded limit for free version 241 if ($revnextwoo_video_enabled === 'yes') : ?> 242 <div class="revnextwoo_form_group"> 243 <label>Add Video (Optional)</label> 244 <div class="revnextwoo_video_upload"> 245 <div class="revnextwoo_video_preview"><!-- Video preview and cancel button will appear here --></div> 246 <label for="review_video" class="revnextwoo_upload_btn"> 247 <span class="dashicons dashicons-format-video"></span> 248 <span>Add Video</span> 249 <input type="file" id="review_video" name="review_video" accept="video/mp4,video/webm" style="display: none;" data-max-size-mb="<?php echo esc_attr($revnextwoo_max_video_size); ?>"> 250 </label> 251 <small>Max 1 video. Supported formats: MP4, WebM. Max size: <?php echo esc_html($revnextwoo_max_video_size); ?>MB.</small> 252 </div> 253 </div> 254 <?php endif; ?> 255 186 256 <?php wp_nonce_field('revnextwoo_review_images', 'revnextwoo_review_images_nonce'); ?> 187 257 … … 195 265 <?php else : ?> 196 266 <div class="revnextwoo_login_notice"> 197 <p>Please <a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>">login</a> to leave a review.</p> 267 <?php 268 if ($allowed_to_rate === 'no_one') { 269 echo '<p>The Review System is Currently OFF.</p>'; 270 } elseif ($allowed_to_rate === 'completed_orders') { 271 if (!is_user_logged_in()) { 272 echo '<p>Please <a href="' . esc_url(wp_login_url(get_permalink())) . '">login</a> to leave a review.</p>'; 273 } else { 274 echo '<p>Please Place an order to make a review.</p>'; 275 } 276 } else { 277 // Default: registered_users_and_guests 278 echo '<p>Please <a href="' . esc_url(wp_login_url(get_permalink())) . '">login</a> to leave a review.</p>'; 279 } 280 ?> 198 281 </div> 199 282 <?php endif; ?> … … 201 284 202 285 <!-- Reviews List --> 203 <div class="revnextwoo_reviews_list"> 286 <?php $review_section_bg = get_option('revnextwoo_product_review_section_bg', '#ffffff'); ?> 287 <div class="revnextwoo_reviews_list" style="background-color: <?php echo esc_attr($review_section_bg); ?>;"> 204 288 <div class="revnextwoo_reviews_header"> 205 289 <h3>Product Reviews</h3> … … 210 294 <option value="highest">Highest Rating</option> 211 295 <option value="lowest">Lowest Rating</option> 296 <option value="helpful">Most Voted</option> 297 <option value="media">With Images/Videos</option> 212 298 </select> 213 299 </div> … … 215 301 216 302 <?php if (!empty($reviews)) : ?> 217 <div class="revnextwoo_reviews_container"> 303 <?php $layout = get_option('revnextwoo_review_layout_style', 'default'); ?> 304 <div class="revnextwoo_reviews_container revnextwoo_layout_<?php echo esc_attr($layout); ?>" data-product-id="<?php echo esc_attr($product->get_id()); ?>"> 218 305 <?php foreach ($reviews as $review) : 219 $r ating = get_comment_meta($review->comment_ID, 'rating', true);306 $review_rating = intval(get_comment_meta($review->comment_ID, 'rating', true)); 220 307 $verified = wc_review_is_from_verified_owner($review->comment_ID); 221 $review_date = get_comment_date('F j, Y', $review->comment_ID); 308 $review_timestamp = strtotime($review->comment_date_gmt ?: $review->comment_date); 309 if (!$review_timestamp) { 310 // Fallback to WP helper if parsing fails 311 $review_timestamp = (int) get_comment_time('U', false, true, $review->comment_ID); 312 } 313 $review_date = human_time_diff($review_timestamp, current_time('timestamp')) . ' ago'; 222 314 $review_images = get_comment_meta($review->comment_ID, 'review_images', true); 223 315 ?> 224 <div class="revnextwoo_review_item"> 225 <div class="revnextwoo_review_header"> 316 <?php 317 $grid_colors = ['#ffb6b6','#6a7ee7','#8df7b1','#f9d56e','#f97e7e','#7ee8fa','#b6ffb6','#c6a1ff']; 318 $masonry_color = ''; 319 $style_attr = ''; 320 if($layout === 'default'){ 321 $style_attr = 'margin-bottom:20px;'; 322 $style_attr .= 'border-radius:10px;'; 323 $style_attr .= 'border:1px solid #ccc;'; 324 $style_attr .= 'overflow:hidden;'; 325 $item_bg = get_option('revnextwoo_review_item_bg', '#fff'); 326 $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';'; 327 } 328 if ($layout === 'grid') { 329 $masonry_color = $grid_colors[array_rand($grid_colors)]; 330 $style_attr = '--masonry-color: ' . $masonry_color . ';'; 331 } 332 if ($layout === 'card') { 333 $item_bg = get_option('revnextwoo_review_item_bg', '#fff'); 334 $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';'; 335 } 336 ?> 337 <div class="revnextwoo_review_item"<?php echo !empty($style_attr) ? ' style="' . $style_attr . '"' : ''; ?>> 338 <?php 339 // BADGE LOGIC: Get CAS and badge for this user 340 $user_id = $review->user_id; 341 $cas = 0; 342 $badge = ''; 343 // Fetch admin thresholds 344 $min_bronze = (int) get_option('revnextwoo_min_bronze_cas', 300); 345 // Calculate CAS for this user (same as backend logic) 346 $order_count = 0; $total_spend = 0; $review_count = 0; $rating_sum = 0; $rating_num = 0; 347 if ($user_id) { 348 if (function_exists('wc_get_orders')) { 349 $orders = wc_get_orders([ 350 'customer_id' => $user_id, 351 'status' => ['wc-completed', 'wc-processing', 'wc-delivered'], 352 'return' => 'ids', 353 'limit' => -1, 354 ]); 355 $order_count = count($orders); 356 foreach ($orders as $order_id) { 357 $order = wc_get_order($order_id); 358 if ($order) $total_spend += $order->get_total(); 359 } 360 } 361 $review_count = get_comments([ 362 'user_id' => $user_id, 363 'status' => 'approve', 364 'type' => 'review', 365 'count' => true, 366 ]); 367 $review_comments = get_comments([ 368 'user_id' => $user_id, 369 'status' => 'approve', 370 'type' => 'review', 371 'fields' => 'ids', 372 ]); 373 foreach ($review_comments as $cid) { 374 $user_rating_value = get_comment_meta($cid, 'rating', true); 375 if ($user_rating_value !== '') { 376 $rating_sum += floatval($user_rating_value); 377 $rating_num++; 378 } 379 } 380 $cas = ($order_count * 2) + ($review_count * 1.5) + ($total_spend / 100); 381 if ($cas >= $min_bronze) { 382 $badge = 'bronze'; 383 } 384 } 385 ?> 386 <?php if ($badge === 'bronze'): ?> 387 <span class="revnextwoo-badge revnextwoo-badge-bronze"> 388 <svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" stroke-width="3" stroke="#CD7F32" fill="none" width="36" height="36"><g><path d="M45.41,11.16l.86,2.61a3.75,3.75,0,0,0,1.5,2L50,17.21a3.85,3.85,0,0,1,1.43,4.62l-.84,2.09a3.83,3.83,0,0,0,0,2.88l.83,2a3.83,3.83,0,0,1-1.32,4.58L48.13,34.8a3.86,3.86,0,0,0-1.46,2.08L46,39.32a3.84,3.84,0,0,1-4,2.78l-2.1-.18a3.88,3.88,0,0,0-2.74.84l-2,1.63a3.83,3.83,0,0,1-4.85,0L28.54,43a3.85,3.85,0,0,0-2.45-.88H23a3.83,3.83,0,0,1-3.74-3l-.55-2.35a3.82,3.82,0,0,0-1.58-2.31L15.3,33.19a3.84,3.84,0,0,1-1.45-4.48l.86-2.36a3.86,3.86,0,0,0,0-2.66l-.77-2.06a3.86,3.86,0,0,1,1.51-4.58l2-1.31a3.87,3.87,0,0,0,1.61-2.19l.61-2.18a3.85,3.85,0,0,1,4-2.79l1.93.17a3.88,3.88,0,0,0,2.74-.84l1.83-1.48a3.84,3.84,0,0,1,4.87,0L36.7,7.81a3.82,3.82,0,0,0,2.75.88l2-.15A3.84,3.84,0,0,1,45.41,11.16Z"></path><path d="M44.59,41.57l7,12.21c.18.31,0,.64-.23.55l-8.61-3.5a.24.24,0,0,0-.31.19l-1.06,9c-.06.29-.42.25-.6-.06L32.65,45.25" stroke-linecap="round"></path><path d="M32.65,45.25,25.14,60c-.19.31-.53.35-.6.07l-1.27-9.2a.24.24,0,0,0-.31-.18L14.59,54.3c-.28.08-.43-.24-.24-.56l7-12.17" stroke-linecap="round"></path><path d="M32.74,16.89l2.7,5.49a.1.1,0,0,0,.08.05l6,.88c.08,0,.12.12.06.17l-4.38,4.27a.09.09,0,0,0,0,.09l1,6a.1.1,0,0,1-.14.11l-5.42-2.85a.07.07,0,0,0-.09,0L27.19,34a.11.11,0,0,1-.15-.11l1-6a.1.1,0,0,0,0-.09l-4.39-4.27a.11.11,0,0,1,.06-.17l6.05-.88a.09.09,0,0,0,.08-.05l2.71-5.49A.1.1,0,0,1,32.74,16.89Z" stroke-linecap="round"></path></g></svg> 389 </span> 390 <?php endif; ?> 391 <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?> 392 <div class="revnextwoo_review_header" style="background-color: <?php echo esc_attr($header_bg); ?>;"> 393 226 394 <div class="revnextwoo_reviewer_info"> 227 395 <div class="revnextwoo_reviewer_avatar"> 228 <?php echo get_avatar($review->comment_author_email, 50); ?> 396 <?php 397 $user_avatar_url = ''; 398 if (!empty($review->user_id)) { 399 $user_avatar_url = get_user_meta($review->user_id, 'revnextwoo_profile_avatar', true); 400 } 401 if (!$user_avatar_url) { 402 $settings = get_option('revnextwoo_social_settings'); 403 $user_avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : ''; 404 } 405 if ($user_avatar_url) { 406 echo '<img src="' . esc_url($user_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />'; 407 } else { 408 $avatar_key = !empty($review->user_id) ? intval($review->user_id) : (isset($review->comment_author_email) ? $review->comment_author_email : ''); 409 echo get_avatar($avatar_key, 50); 410 } 411 ?> 229 412 </div> 230 413 <div class="revnextwoo_reviewer_details"> 231 <h4 class="revnextwoo_reviewer_name"><?php echo esc_html($review->comment_author); ?></h4> 414 <?php if ($verified) : ?> 415 <span class="revnextwoo_verified_badge">Verified Purchase</span> 416 <?php endif; ?> 417 <h4 class="revnextwoo_reviewer_name"><b><?php echo esc_html($review->comment_author); ?></b></h4> 232 418 <div class="revnextwoo_review_meta"> 233 419 <div class="revnextwoo_review_rating"> 234 <?php for ($i = 1; $i <= 5; $i++) : ?> 235 <span class="dashicons dashicons-star-<?php echo $i <= $rating ? 'filled' : 'empty'; ?>"></span> 420 <?php 421 $current_icon_set = isset($icon_classes[$rating_images]) ? $icon_classes[$rating_images] : $icon_classes['star']; 422 for ($i = 1; $i <= 5; $i++) : 423 $iconType = ($i <= $review_rating) ? 'filled' : 'empty'; 424 $iconClass = isset($current_icon_set[$iconType]) ? $current_icon_set[$iconType] : ''; 425 ?> 426 <i class="<?php echo esc_attr($iconClass); ?>"></i> 236 427 <?php endfor; ?> 237 428 </div> 238 <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span>239 <?php if ($verified) : ?>240 <span class="revnextwoo_verified_badge">Verified Purchase</span>241 <?php endif; ?>242 429 </div> 430 <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span> 431 <?php 432 // Location display logic (same as comment-item-display.php) 433 $reviewer_city = get_comment_meta($review->comment_ID, 'reviewer_city', true); 434 $reviewer_country = get_comment_meta($review->comment_ID, 'reviewer_country', true); 435 $location_display = ''; 436 if (!empty($reviewer_city) || !empty($reviewer_country)) { 437 $location_parts = array(); 438 if (!empty($reviewer_city)) { 439 $location_parts[] = esc_html($reviewer_city); 440 } 441 if (!empty($reviewer_country)) { 442 $location_parts[] = esc_html($reviewer_country); 443 } 444 $location_display = '<span class="review-location">' . implode(', ', $location_parts) . '</span>'; 445 } 446 if (!empty($location_display)) { 447 echo $location_display; 448 } 449 ?> 243 450 </div> 244 451 </div> … … 253 460 } 254 461 ?> 255 <h4 class="revnextwoo_review_title"><?php echo esc_html($review_title); ?></h4> 256 <p class="revnextwoo_review_text"><?php echo esc_html($review->comment_content); ?></p> 462 <div class="revnextwoo_text_box"> 463 <h3 class="revnextwoo_review_title"><b><?php echo esc_html($review_title); ?></b></h3> 464 <p class="revnextwoo_review_text" data-full-text="<?php echo esc_attr(wp_strip_all_tags($review->comment_content)); ?>"> 465 <?php 466 $content = wp_strip_all_tags($review->comment_content); 467 $words = explode(' ', $content); 468 if (count($words) > 12) { 469 $short_content = implode(' ', array_slice($words, 0, 12)); 470 echo esc_html($short_content . '...'); 471 } else { 472 echo esc_html($content); 473 } 474 ?> 475 </p> 476 <?php if (count($words) > 12) : ?> 477 <span class="revnextwoo_read_more">Read More</span> 478 <?php endif; ?> 479 </div> 257 480 258 481 <?php if (!empty($review_images) && is_array($review_images)) : ?> 259 482 <div class="revnextwoo_review_images"> 260 <?php foreach ( array_slice($review_images, 0, 3)as $image_id) : ?>483 <?php foreach ($review_images as $image_id) : ?> 261 484 <div class="revnextwoo_review_image"> 262 < ?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?>485 <a href="<?php echo esc_url(wp_get_attachment_url($image_id)); ?>" data-lightbox="example-set" ><?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?></a> 263 486 </div> 264 487 <?php endforeach; ?> 265 <?php if (count($review_images) > 3) : ?>266 <div class="revnextwoo_review_image_more">267 +<?php echo count($review_images) - 3; ?> more268 </div>269 <?php endif; ?>270 488 </div> 271 489 <?php endif; ?> 272 </div> 273 <div class="revnextwoo_review_actions"> 274 <button class="revnextwoo_like_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>"> 490 <?php 491 $video_id = get_comment_meta($review->comment_ID, 'review_video', true); 492 if ($video_id) { 493 $video_url = wp_get_attachment_url($video_id); 494 if ($video_url) { 495 echo '<div class="revnextwoo_review_video">'; 496 echo '<video controls width="320" height="240" style="max-width:100%;margin-top:10px;">'; 497 echo '<source src="' . esc_url($video_url) . '" type="video/mp4">'; 498 echo __('Your browser does not support the video tag.', 'review-next-for-woocommerce'); 499 echo '</video>'; 500 echo '</div>'; 501 } 502 } 503 ?> 504 </div> 505 <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?> 506 <div class="revnextwoo_review_actions" style="background-color: <?php echo esc_attr($header_bg); ?>"> 507 <?php 508 $settings = get_option('revnextwoo_social_settings'); 509 $show_thumbs_up = isset($settings['enable_thumbs_up']) && $settings['enable_thumbs_up'] === 'yes'; 510 $show_thumbs_down = isset($settings['enable_thumbs_down']) && $settings['enable_thumbs_down'] === 'yes'; 511 ?> 512 <?php if ($show_thumbs_up): ?> 513 <button class="revnextwoo_like_btn revnextwoo_vote_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>" data-vote-type="up"> 275 514 <span class="dashicons dashicons-thumbs-up"></span> 276 515 <span class="revnextwoo_like_count"><?php echo get_comment_meta($review->comment_ID, 'likes', true) ?: 0; ?></span> 277 516 </button> 278 <button class="revnextwoo_report_btn">Report</button> 517 <?php endif; ?> 518 519 520 <div class="revnextwoo_social_sharing"> 521 <?php 522 $show_facebook = isset($settings['enable_facebook_share']) && $settings['enable_facebook_share'] === 'yes'; 523 $show_whatsapp = isset($settings['enable_whatsapp_share']) && $settings['enable_whatsapp_share'] === 'yes'; 524 $show_twitter = isset($settings['enable_twitter_share']) && $settings['enable_twitter_share'] === 'yes'; 525 ?> 526 <?php if ($show_facebook): ?> 527 <a href="#" class="revnextwoo_share_btn revnextwoo_facebook_share" title="Share on Facebook"> 528 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="#1877F2"><path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z"/></svg> 529 </a> 530 <?php endif; ?> 531 532 533 </div> 534 535 279 536 </div> 537 <span class="revnextwoo_vote_message" style="display:none;"></span> 280 538 </div> 281 539 <?php endforeach; ?> … … 284 542 <p class="revnextwoo_no_reviews">No reviews yet. Be the first to review!</p> 285 543 <?php endif; ?> 544 545 <?php 546 // Pagination controls 547 // Pagination controls removed 548 ?> 286 549 </div> 287 550 288 551 <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.8.2/css/lightbox.css'> 552 <style> 553 .revnextwoo-pagination { 554 display: flex; 555 align-items: center; 556 justify-content: center; 557 gap: 22px; 558 margin: 32px 0 10px 0; 559 padding: 0; 560 } 561 .revnextwoo-pagination li { 562 list-style: none; 563 margin: 0; 564 padding: 0; 565 } 566 .revnextwoo-page-num, .revnextwoo-page-prev, .revnextwoo-page-next { 567 display: flex; 568 align-items: center; 569 justify-content: center; 570 width: 44px; 571 height: 44px; 572 border-radius: 50%; 573 background: #fff; 574 border: 2px solid #e1e1e1; 575 color: #222; 576 font-size: 1.2rem; 577 font-weight: 500; 578 cursor: pointer; 579 transition: background 0.18s, color 0.18s, border 0.18s; 580 user-select: none; 581 } 582 .revnextwoo-page-num.active { 583 background: #111; 584 color: #fff; 585 border: 2px solid #111; 586 font-weight: 700; 587 } 588 .revnextwoo-page-num:hover:not(.active), .revnextwoo-page-prev:hover:not(.disabled), .revnextwoo-page-next:hover:not(.disabled) { 589 background: #f2f2f2; 590 color: #111; 591 border-color: #bdbdbd; 592 } 593 .revnextwoo-page-prev.disabled, .revnextwoo-page-next.disabled, .revnextwoo-page-num.disabled { 594 opacity: 0.5; 595 pointer-events: none; 596 background: #fafafa; 597 color: #bdbdbd; 598 border-color: #e1e1e1; 599 } 600 .revnextwoo-page-prev span, .revnextwoo-page-next span, .revnextwoo-page-num span { 601 display: block; 602 line-height: 1; 603 font-size: 1.35rem; 604 } 605 606 .revnextwoo-badge { 607 position: absolute; 608 top: 12px; 609 right: 12px; 610 z-index: 10; 611 display: flex; 612 align-items: center; 613 justify-content: center; 614 width: 40px; 615 height: 40px; 616 border-radius: 50%; 617 background: #fff; 618 box-shadow: 0 2px 8px rgba(0,0,0,0.08); 619 padding: 2px; 620 } 621 .revnextwoo-badge-gold svg { 622 stroke: #ffd900; 623 } 624 .revnextwoo-badge-bronze svg { 625 stroke: #CD7F32; 626 } 627 .revnextwoo-badge-silver svg { 628 stroke: #C0C0C0; 629 } 630 .revnextwoo_review_item { 631 position: relative; 632 } 633 634 .revnextwoo_actions_menu { 635 position: relative; 636 display: inline-block; 637 vertical-align: middle; 638 margin-left: 10px; 639 } 640 641 .revnextwoo_actions_toggle { 642 background: none; 643 border: none; 644 cursor: pointer; 645 padding: 5px; 646 border-radius: 4px; 647 display: flex; 648 align-items: center; 649 justify-content: center; 650 color: #666; 651 transition: all 0.2s ease; 652 } 653 654 .revnextwoo_actions_toggle:hover { 655 background: rgba(0, 0, 0, 0.05); 656 color: #333; 657 } 658 659 .revnextwoo_actions_dropdown { 660 position: absolute; 661 right: 0; 662 top: 100%; 663 margin-top: 5px; 664 background: #fff; 665 border: 1px solid #ddd; 666 border-radius: 4px; 667 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); 668 z-index: 1000; 669 min-width: 160px; 670 overflow: hidden; 671 } 672 673 .revnextwoo_action_item { 674 display: block; 675 width: 100%; 676 padding: 8px 16px; 677 text-align: left; 678 background: none; 679 border: none; 680 color: #333; 681 cursor: pointer; 682 transition: all 0.2s ease; 683 } 684 685 .revnextwoo_action_item:hover { 686 background: #f5f5f5; 687 color: #000; 688 } 689 690 .revnextwoo_actions_dropdown.show { 691 display: block !important; 692 animation: fadeIn 0.2s ease; 693 } 694 695 @keyframes fadeIn { 696 from { opacity: 0; transform: translateY(-5px); } 697 to { opacity: 1; transform: translateY(0); } 698 } 699 </style> 289 700 290 701 <script> 702 var revnextwoo_img_compression = '<?php echo esc_js(get_option('revnextwoo_img_compression', 'yes')); ?>'; 703 var revnextwoo_img_compression_scale = '<?php echo esc_js(get_option('revnextwoo_img_compression_scale', 0.15)); ?>'; 704 var revnextwoo_img_watermark_enabled = '<?php echo esc_js(get_option('revnextwoo_img_watermark', 'no')); ?>'; 705 var revnextwoo_img_watermark_text = '<?php echo esc_js(get_option('revnextwoo_img_watermark_text', '')); ?>'; 291 706 jQuery(document).ready(function($) { 707 // Toggle actions dropdown 708 $(document).on('click', '.revnextwoo_actions_toggle', function(e) { 709 e.stopPropagation(); 710 var $dropdown = $(this).siblings('.revnextwoo_actions_dropdown'); 711 $('.revnextwoo_actions_dropdown').not($dropdown).removeClass('show').hide(); 712 $dropdown.toggleClass('show').toggle(); 713 }); 714 715 // Close dropdown when clicking outside 716 $(document).on('click', function(e) { 717 if (!$(e.target).closest('.revnextwoo_actions_menu').length) { 718 $('.revnextwoo_actions_dropdown').removeClass('show').hide(); 719 } 720 }); 721 722 // Handle action items click 723 $(document).on('click', '.revnextwoo_action_item', function() { 724 var action = $(this).data('action'); 725 var $review = $(this).closest('.revnextwoo_review_item'); 726 727 if (action === 'report') { 728 if (confirm('Are you sure you want to report this review?')) { 729 // Add your report logic here 730 alert('Thank you for reporting this review. Our team will review it shortly.'); 731 } 732 } else if (action === 'edit') { 733 // Add your edit logic here 734 alert('Edit review functionality will be implemented here.'); 735 } 736 737 // Close the dropdown 738 $(this).closest('.revnextwoo_actions_dropdown').removeClass('show').hide(); 739 }); 740 741 // Handle image preview and upload 292 742 // Handle image preview and upload 293 743 $('body').on('change', '#review_images', function(e) { … … 296 746 preview.empty(); 297 747 298 // Limit to 5 images 299 if (files.length > 5) { 300 alert('You can upload a maximum of 5 images.'); 748 // Get max images from form data attribute or default to 6 749 const maxImages = $('#revnextwoo-review-form').data('max-photos') || 6; 750 751 // Check against dynamic limit 752 if (files.length > maxImages) { 753 alert('You can upload a maximum of ' + maxImages + ' images.'); 301 754 this.value = ''; 302 755 return false; 303 756 } 304 757 305 // Check file sizes and types306 758 for (let i = 0; i < files.length; i++) { 307 759 const file = files[i]; 308 309 // Check file size (2MB max)310 if (file.size > 2 * 1024 * 1024) {311 alert('One or more files exceed the 2MB size limit.');312 this.value = '';313 preview.empty();314 return false;315 }316 760 317 761 // Check file type … … 322 766 return false; 323 767 } 324 325 // Create preview 768 326 769 const reader = new FileReader(); 327 770 reader.onload = function(e) { … … 439 882 440 883 441 // Handle like button click 442 $('.revnextwoo_like_btn').on('click', function() { 884 // Handle Read More functionality 885 $('body').on('click', '.revnextwoo_read_more', function() { 886 const $this = $(this); 887 const $text = $this.siblings('.revnextwoo_review_text'); 888 const fullText = $text.data('full-text'); 889 890 if ($this.text() === 'Read More') { 891 $text.text(fullText); 892 $this.text('Read Less'); 893 } else { 894 const words = fullText.split(' '); 895 const shortText = words.slice(0, 12).join(' ') + '...'; 896 $text.text(shortText); 897 $this.text('Read More'); 898 } 899 }); 900 901 // Handle like/dislike voting (both buttons) 902 $('.revnextwoo_vote_btn').on('click', function(e) { 903 e.preventDefault(); 443 904 var button = $(this); 444 905 var reviewId = button.data('review-id'); 445 906 var voteType = button.data('vote-type'); 907 var reviewItem = button.closest('.revnextwoo_review_item'); 908 var messageSpan = reviewItem.find('.revnextwoo_vote_message'); 909 var nonce = '<?php echo wp_create_nonce('revnextwoo_vote_nonce'); ?>'; 910 // Prevent double click 911 if (button.hasClass('voted')) return; 446 912 $.ajax({ 447 913 url: '<?php echo admin_url('admin-ajax.php'); ?>', 448 914 type: 'POST', 915 dataType: 'json', 449 916 data: { 450 action: 'revnextwoo_ like_review',917 action: 'revnextwoo_vote_review', 451 918 review_id: reviewId, 452 nonce: '<?php echo wp_create_nonce('like_review_nonce'); ?>' 919 vote_type: voteType, 920 nonce: nonce 453 921 }, 454 922 success: function(response) { 455 923 if (response.success) { 456 var count = button.find('.revnextwoo_like_count'); 457 count.text(parseInt(count.text()) + 1); 458 button.addClass('liked'); 924 if (voteType === 'up') { 925 var countSpan = button.find('.revnextwoo_like_count'); 926 countSpan.text(response.data.likes); 927 } else if (voteType === 'down') { 928 var countSpan = button.find('.revnextwoo_dislike_count'); 929 countSpan.text(response.data.dislikes); 930 } 931 button.addClass('voted'); 932 messageSpan.hide(); 459 933 } else { 460 alert('You have already liked this review.'); 934 // Show vote message 935 messageSpan.text(response.data.message || 'You have already voted once, Thanks').show(); 461 936 } 937 }, 938 error: function() { 939 messageSpan.text('An error occurred. Please try again.').show(); 462 940 } 463 941 }); … … 465 943 }); 466 944 </script> 945 <!-- jquery image lightbox cdn --> 946 <script src='https://code.jquery.com/jquery-2.2.4.min.js'></script> 947 <script src='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.8.2/js/lightbox.min.js'></script><script src="..assets/js/vendor/script.js"></script> -
review-next-for-woocommerce/trunk/template/frontend/user-question.php
r3315734 r3457384 3 3 if (! defined('ABSPATH')) exit; 4 4 5 6 // --- Validation logic for questions and replies --- 7 function revnextwoo_validate_question($question) { 8 $emailRegex = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/'; 9 $phoneRegex = '/\b\d{10,11}\b/'; 10 $linkRegex = '/https?:\/\/[^ 11 \s\/$.#?]+[^ 12 \s]*/'; 13 if (preg_match($emailRegex, $question) || preg_match($phoneRegex, $question) || preg_match($linkRegex, $question)) { 14 return false; 15 } 16 return true; 17 } 18 19 // --- Handle user question form POST --- 20 if ( 21 isset( 22 $_POST['question'], 23 $_POST['comment_post_ID'], 24 $_POST['question_nonce']) && 25 wp_verify_nonce($_POST['question_nonce'], 'submit_question_nonce') && 26 is_user_logged_in() 27 ) { 28 $question_content = wp_strip_all_tags($_POST['question']); 29 if (!revnextwoo_validate_question($question_content)) { 30 wp_die('Your question should not contain contact information.'); 31 } 32 $question_data = array( 33 'product_id' => intval($_POST['comment_post_ID']), 34 'user_id' => get_current_user_id(), 35 'author_name' => wp_get_current_user()->display_name, 36 'author_email' => wp_get_current_user()->user_email, 37 'content' => $question_content, 38 'status' => 'approve', 39 ); 40 if (class_exists('Revnextwoo_QA_DB')) { 41 Revnextwoo_QA_DB::insert_question($question_data); 42 wp_safe_redirect(add_query_arg(array('question_added' => 1))); 43 exit; 44 } 45 } 46 47 // --- Handle admin/user reply form POST --- 48 if ( 49 isset( 50 $_POST['question_reply'], 51 $_POST['comment_post_ID'], 52 $_POST['comment_parent'], 53 $_POST['question_reply_nonce']) && 54 wp_verify_nonce($_POST['question_reply_nonce'], 'submit_question_reply_nonce') && 55 is_user_logged_in() 56 ) { 57 $reply_content = wp_strip_all_tags($_POST['question_reply']); 58 if (!revnextwoo_validate_question($reply_content)) { 59 wp_die('Your question reply should not contain contact information.'); 60 } 61 $reply_data = array( 62 'product_id' => intval($_POST['comment_post_ID']), 63 'parent_id' => intval($_POST['comment_parent']), 64 'user_id' => get_current_user_id(), 65 'author_name' => wp_get_current_user()->display_name, 66 'author_email' => wp_get_current_user()->user_email, 67 'content' => $reply_content, 68 'status' => 'approve', 69 ); 70 if (class_exists('Revnextwoo_QA_DB')) { 71 Revnextwoo_QA_DB::insert_reply($reply_data); 72 wp_safe_redirect(add_query_arg(array('reply_added' => 1))); 73 exit; 74 } 75 } 76 77 78 5 79 global $product; 6 $product_id = $product->get_id(); 7 $comments_per_page = 5; 8 $page = get_query_var('paged') ? get_query_var('paged') : 1; // Get the current page or default to page 1 9 $questions = get_comments( 10 array( 11 'post_id' => $product->get_id(), 80 $product_id = 0; 81 if (is_object($product) && method_exists($product, 'get_id')) { 82 $product_id = $product->get_id(); 83 } 84 if (!$product_id && isset($_POST['product_id'])) { 85 $product_id = intval($_POST['product_id']); 86 } 87 if (!$product_id) { 88 $product_id = get_the_ID(); 89 } 90 $comments_per_page = 4; 91 $page = isset($_POST['paged']) ? intval($_POST['paged']) : (isset($_GET['paged']) ? intval($_GET['paged']) : 1); // AJAX or normal 92 if ($page < 1) { $page = 1; } 93 // Fetch from custom QA table 94 $questions = array(); 95 $question_count = 0; 96 $question_reply_count = 0; 97 if (class_exists('Revnextwoo_QA_DB')) { 98 $questions = Revnextwoo_QA_DB::get_questions($product_id, array( 12 99 'status' => 'approve', 13 // Display approved comments14 100 'number' => $comments_per_page, 15 // Limit the number of comments to 1516 'order' => 'DESC',17 // Order by most recent comments18 'type' => 'question',19 101 'offset' => ($page - 1) * $comments_per_page, 20 ) 21 ); 22 $args = array( 23 'post_id' => $product->get_id(), 24 'type' => 'question', 25 'count' => true // return only the count 26 ); 27 // Get the number of reviews for the product 28 $question_count = get_comments($args); 29 $args = array( 30 'post_id' => $product->get_id(), 31 'type' => 'question_reply', 32 'count' => true // return only the count 33 ); 34 // Get the number of reviews for the product 35 $question_reply_count = get_comments($args); 36 102 'order' => 'DESC', 103 )); 104 $question_count = Revnextwoo_QA_DB::count_questions($product_id); 105 // Total replies for this product 106 global $wpdb; 107 $qa_table = Revnextwoo_QA_DB::table_name(); 108 $question_reply_count = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$qa_table} WHERE product_id=%d AND type='question_reply'", $product_id)); 109 } 37 110 38 111 // Customize your tab title here (e.g., "Custom Tab (X)") 39 echo '<div class=" revnextwoo_question_section" id="revnextwoo_question">';40 echo '<h 2 class="revnextwoo_title">Questions About This Product (' . esc_html($question_count) . ')</h2>';112 echo '<div class="user_question_section" id="revnextwoo_question">'; 113 echo '<h3 class="title";>Questions About This Product (' . esc_html($question_count) . ')</h3>'; 41 114 if (is_user_logged_in()) { 42 115 // Output the form HTML here with your specific requirements … … 46 119 echo '<input type="hidden" name="comment_post_ID" value="' . esc_attr($product_id) . '" id="comment_post_ID">'; 47 120 echo '<input type="hidden" name="comment_parent" id="comment_parent" value="0">'; 48 echo '<textarea class="question_field" name="question" value="" id="question" placeholder="Ask aQuestion" required></textarea>';121 echo '<textarea class="question_field" name="question" value="" id="question" maxlength="300" placeholder="Ask Your Question" required></textarea>'; 49 122 echo '<p id="error-message" class="">Your question should not contain contact information such as email, phone or external web links. Visit "My Orders" if you have questions about your previous order. </p>'; 50 echo '< input class="submit_btn" type="submit" value="ASK QUESTION">';123 echo '<div><input class="submit_btn" type="submit" value="ASK QUESTION"></div>'; 51 124 echo '</div>'; 52 125 echo '</form>'; … … 60 133 $register_link = '<a href="' . esc_url($registration_url) . '">Register</a>'; 61 134 62 $login_message = 'Please ' . $login_link . ' or ' . $register_link . ' to ask a question.'; 135 136 $login_message = '<div class="revnextwoo_login_notice">Please ' . $login_link . ' or ' . $register_link . ' to ask a question.</div>'; 63 137 64 138 echo '<p class="login_redirect">' . wp_kses_post($login_message) . '</p>'; 65 139 } 66 echo '<div class="revnextwoo_question_show">'; 140 // question and replay list show start here---------------------------------------- 141 echo '<div class="user_question_show">'; 67 142 echo '<h4 class="short_title">Other questions answered by Admin (' . esc_html($question_reply_count) . ')</h4>'; 68 143 echo '<div class="card">'; 69 144 70 // Get the question icon image71 $question_icon_path = plugin_dir_path(dirname(dirname(__FILE__))) . 'public/images/problem.png';72 $question_icon_id = revnextwoo_get_attachment_id_by_path($question_icon_path, 'problem.png');73 145 foreach ($questions as $question) { 74 $authorUsername = get_comment_author($question->comment_ID); 75 $questionDate = gmdate('F j, Y', strtotime($question->comment_date)); // Format the comment date 146 // Rows from custom table 147 $authorUsername = isset($question->author_name) ? $question->author_name : ''; 148 $questionDate = revnextwoo_get_relative_time_parts($question->created_at); 76 149 $currentUserId = get_current_user_id(); 77 echo '<div class="item">'; 78 echo '<div class="short_content">'; 79 if ($question_icon_id) { 80 echo wp_get_attachment_image($question_icon_id, 'thumbnail', false, array('class' => 'question_icon', 'alt' => 'question-icon')); 150 $avatar_url = ''; 151 if (!empty($question->user_id)) { 152 $avatar_url = get_user_meta($question->user_id, 'revnextwoo_profile_avatar', true); 153 } 154 if (!$avatar_url) { 155 $settings = get_option('revnextwoo_social_settings'); 156 $avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : ''; 157 } 158 if ($avatar_url) { 159 $authorAvatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />'; 81 160 } else { 82 // Fallback if attachment ID not found, use WooCommerce placeholder 83 echo wp_kses_post(wc_placeholder_img('thumbnail', array('class' => 'question_icon', 'alt' => 'Question Icon Placeholder'))); 84 } 85 echo '<p>' . esc_html($question->comment_content) . '</p>'; 86 echo '</div>'; 87 echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question->comment_ID) . '" >'; 161 $avatar_key = !empty($question->user_id) ? intval($question->user_id) : (isset($question->author_email) ? $question->author_email : ''); 162 $authorAvatar = get_avatar($avatar_key, 50); 163 } 164 165 166 // the replay div start haere -------------------------------------------- 167 $question_has_reply = !empty($question_replies); 168 $highlight_class = $question_has_reply ? '' : ' no-reply-highlight'; 169 echo '<div class="item' . esc_attr($highlight_class) . '">'; 170 echo "<div class='short_content'>"; 171 echo '<div class="revnext_que_info">'; 172 echo '<div class="revnextwoo_question_avatar">'.$authorAvatar.'</div>'; 173 echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>'; 174 echo '</div>'; 175 echo '<p>' . esc_html($question->content) . '</p>'; 176 echo '</div>'; 177 178 //question replay button in right side 179 echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question->id) . '" >'; 88 180 if (current_user_can('administrator')) { 89 echo '<h5 class="question-reply-trigger" target="' . esc_attr($question->comment_ID) . '"><i class="fas fa-reply"></i></span> Reply</h5>'; 90 } 91 echo '</div>'; 181 echo '<h5 class="question-reply-trigger" target="' . esc_attr($question->id) . '"><i class="fas fa-reply"></i></span> Reply</h5>'; 182 } 183 echo '</div>'; 184 185 //this include the question date below 92 186 echo '<div class="content">'; 93 echo '<p class="create_date">' . esc_html($questionDate) . '</p>'; 94 echo '</div>'; 95 echo '</div>'; 96 echo '<form class="reply_question_fm" id="reply_question_form' . esc_attr($question->comment_ID) . '" method="post" style="display:none">'; 187 $question_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute']; 188 $question_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : ''; 189 echo '<p class="create_date"' . $question_date_title . '>' . esc_html($question_date_label).'</p>'; 190 echo '</div>'; 191 echo '</div>'; 192 193 //reply form visible start here ============ 194 echo '<form class="reply_question_fm" id="reply_question_form' . esc_attr($question->id) . '" method="post" style="display:none">'; 97 195 wp_nonce_field('submit_question_reply_nonce', 'question_reply_nonce'); 98 196 echo '<div class="input_group" id="question_group">'; 99 197 echo '<input type="hidden" name="comment_post_ID" value="' . esc_attr($product_id) . '" id="comment_post_ID">'; 100 echo '<input type="hidden" name="comment_parent" id="comment_parent" value="' . esc_attr($question-> comment_ID) . '">';101 echo '<textarea class="question_field" name="question_reply" value="" id="reply_question_field" placeholder=" Ask aQuestion" required></textarea>';102 echo '<p id="reply-error-message" class="">Your question should not contain contact information such as email, phone or external web links. Visit "My Orders" if you have questions about your previous order.</p>';103 echo '<input class="submit_btn" type="submit" value=" ASK QUESTION">';198 echo '<input type="hidden" name="comment_parent" id="comment_parent" value="' . esc_attr($question->id) . '">'; 199 echo '<textarea class="question_field" name="question_reply" value="" id="reply_question_field" placeholder="Reply to this Question" required></textarea>'; 200 echo '<p id="reply-error-message" class="">Your reply should not contain contact information such as email, phone or external web links.</p>'; 201 echo '<input class="submit_btn" type="submit" value="Reply">'; 104 202 echo '</div>'; 105 203 echo '</form>'; 106 204 107 205 // Get and output the replies to this comment 108 $ args = array(109 'parent' => $question->comment_ID,110 'status' => 'approve',111 'order' => 'ASC',112 'type' => 'question_reply',113 );114 $question_replies = get_comments($args);206 $question_replies = array(); 207 if (class_exists('Revnextwoo_QA_DB')) { 208 $question_replies = Revnextwoo_QA_DB::get_replies($question->id, array( 209 'status' => 'approve', 210 'order' => 'ASC', 211 )); 212 } 115 213 // Get the answer icon image 116 214 $answer_icon_path = plugin_dir_path(dirname(dirname(__FILE__))) . 'public/images/answer.png'; 117 215 $answer_icon_id = revnextwoo_get_attachment_id_by_path($answer_icon_path, 'answer.png'); 118 216 foreach ($question_replies as $question_reply) { 119 $questionDate = gmdate('F j, Y', strtotime($question_reply->comment_date)); 120 $authorUsername = get_comment_author($question_reply->comment_ID); 121 $questionDate = gmdate('F j, Y', strtotime($question_reply->comment_date)); // Format the comment date 217 $questionDate = revnextwoo_get_relative_time_parts($question_reply->created_at); 218 $authorUsername = isset($question_reply->author_name) ? $question_reply->author_name : ''; 122 219 $currentUserId = get_current_user_id(); 123 echo '<div class="item" style="padding-left:25px">'; 124 echo '<div class="short_content">'; 125 if ($answer_icon_id) { 126 echo wp_get_attachment_image($answer_icon_id, 'thumbnail', false, array('class' => 'question_icon', 'alt' => 'answer-icon')); 220 $avatar_url = ''; 221 if (!empty($question_reply->user_id)) { 222 $avatar_url = get_user_meta($question_reply->user_id, 'revnextwoo_profile_avatar', true); 223 } 224 if (!$avatar_url) { 225 $settings = get_option('revnextwoo_social_settings'); 226 $avatar_url = isset($settings['custom_avatar_admin']) ? $settings['custom_avatar_admin'] : ''; 227 } 228 if ($avatar_url) { 229 $admin_replay_avatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="Admin Avatar" />'; 127 230 } else { 128 // Fallback if attachment ID not found, use WooCommerce placeholder 129 echo wp_kses_post(wc_placeholder_img('thumbnail', array('class' => 'question_icon', 'alt' => 'Answer Icon Placeholder'))); 130 } 131 echo '<p>' . esc_html($question_reply->comment_content) . '</p>'; 231 $avatar_key = !empty($question_reply->user_id) ? intval($question_reply->user_id) : (isset($question_reply->author_email) ? $question_reply->author_email : ''); 232 $admin_replay_avatar = get_avatar($avatar_key, 50); 233 } 234 echo '<div class="item" style="background:#f6faff; border-left:4px solid #4a90e2; margin-left:48px; margin-top:8px; padding:16px 16px 12px 28px; width:auto; float:none;">'; 235 echo '<div class="short_content">'; 236 echo '<div class="revnext_que_info">'; 237 echo '<div class="revnextwoo_question_avatar">'.$admin_replay_avatar.'</div>'; 238 echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>'; 239 echo '</div>'; 240 echo '<p>' . esc_html($question_reply->content) . '</p>'; 241 echo '</div>'; 242 echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question_reply->id) . '" >'; 243 echo '</div>'; 244 echo '<div class="content">'; 245 $reply_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute']; 246 $reply_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : ''; 247 echo '<p class="create_date"' . $reply_date_title . '>' . esc_html($reply_date_label) . '</p>'; 248 echo '</div>'; 132 249 echo '</div>'; 133 echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question_reply->comment_ID) . '" >'; 134 echo '</div>'; 135 echo '<div class="content">'; 136 echo '<p class="create_date">' . esc_html($questionDate) . '</p>'; 137 echo '</div>'; 138 echo '</div>'; 139 } 140 } 141 // Pagination links 142 // $total_comments = get_comments_number($product->get_id()); 143 // if ($total_comments > $comments_per_page) { 144 // echo '<div class="pagination">'; 145 // echo paginate_comments_links(array( 146 // 'total' => ceil($total_comments / $comments_per_page), 147 // 'current' => $page, 148 // )); 149 // echo '</div>'; 150 // } 151 250 } 251 } 152 252 echo '</div>'; 153 253 echo '</div>'; 154 echo '</div>'; 254 //pagination start here 255 if (defined('DOING_AJAX') && DOING_AJAX && isset($_POST['action']) && $_POST['action'] === 'revnextwoo_load_questions') { 256 // AJAX response logic (mimic inc/revnextwoo-questions-pagination.php) 257 ob_start(); 258 foreach ($questions as $question) { 259 $authorUsername = isset($question->author_name) ? $question->author_name : ''; 260 $questionDate = revnextwoo_get_relative_time_parts($question->created_at); 261 $avatar_url = ''; 262 if (!empty($question->user_id)) { 263 $avatar_url = get_user_meta($question->user_id, 'revnextwoo_profile_avatar', true); 264 } 265 if (!$avatar_url) { 266 $settings = get_option('revnextwoo_social_settings'); 267 $avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : ''; 268 } 269 if ($avatar_url) { 270 $authorAvatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />'; 271 } else { 272 $avatar_key = !empty($question->user_id) ? intval($question->user_id) : (isset($question->author_email) ? $question->author_email : ''); 273 $authorAvatar = get_avatar($avatar_key, 50); 274 } 275 echo '<div class="item">'; 276 echo "<div class='short_content'>"; 277 echo '<div class="revnext_que_info">'; 278 echo '<div class="revnextwoo_question_avatar">'.$authorAvatar.'</div>'; 279 echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>'; 280 echo '</div>'; 281 echo '<p>' . esc_html($question->content) . '</p>'; 282 echo '</div>'; 283 $question_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute']; 284 $question_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : ''; 285 echo '<div class="content"><p class="create_date"' . $question_date_title . '>' . esc_html($question_date_label).'</p></div>'; 286 echo '</div>'; 287 // Replies 288 $question_replies = array(); 289 if (class_exists('Revnextwoo_QA_DB')) { 290 $question_replies = Revnextwoo_QA_DB::get_replies($question->id, array( 291 'status' => 'approve', 292 'order' => 'ASC', 293 )); 294 } 295 if (!empty($question_replies)) { 296 echo '<div class="revnextwoo-question-replies" style="margin-left:48px;">'; 297 foreach ($question_replies as $question_reply) { 298 $replyDate = revnextwoo_get_relative_time_parts($question_reply->created_at); 299 $replyUsername = isset($question_reply->author_name) ? $question_reply->author_name : ''; 300 $reply_avatar_url = ''; 301 if (!empty($question_reply->user_id)) { 302 $reply_avatar_url = get_user_meta($question_reply->user_id, 'revnextwoo_profile_avatar', true); 303 } 304 if (!$reply_avatar_url) { 305 $settings = get_option('revnextwoo_social_settings'); 306 $reply_avatar_url = isset($settings['custom_avatar_admin']) ? $settings['custom_avatar_admin'] : ''; 307 } 308 if ($reply_avatar_url) { 309 $replyAvatar = '<img src="' . esc_url($reply_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="Admin Avatar" />'; 310 } else { 311 $reply_avatar_key = !empty($question_reply->user_id) ? intval($question_reply->user_id) : (isset($question_reply->author_email) ? $question_reply->author_email : ''); 312 $replyAvatar = get_avatar($reply_avatar_key, 50); 313 } 314 echo '<div class="item" style="background:#f6faff; border-left:4px solid #4a90e2; margin-top:8px; padding:16px 16px 12px 28px; width:auto; float:none;">'; 315 echo '<div class="short_content">'; 316 echo '<div class="revnext_que_info">'; 317 echo '<div class="revnextwoo_question_avatar">'.$replyAvatar.'</div>'; 318 echo '<div class="revnextwoo_question_username"><h4>'.esc_html($replyUsername).'</h4></div>'; 319 echo '</div>'; 320 echo '<p>' . esc_html($question_reply->content) . '</p>'; 321 echo '</div>'; 322 $reply_date_label = !empty($replyDate['relative']) ? $replyDate['relative'] : $replyDate['absolute']; 323 $reply_date_title = !empty($replyDate['absolute']) ? ' title="' . esc_attr($replyDate['absolute']) . '"' : ''; 324 echo '<div class="content"><p class="create_date"' . $reply_date_title . '>' . esc_html($reply_date_label) . '</p></div>'; 325 echo '</div>'; 326 } 327 echo '</div>'; 328 } 329 } 330 $card_html = '<div class="card">' . ob_get_clean() . '</div>'; 331 $total_pages = ceil($question_count / $comments_per_page); 332 $next_page = $page + 1; // always point to the subsequent page 333 echo '<div class="revnextwoo-questions-response">' 334 . $card_html 335 . '<div class="revnextwoo-load-more-meta" data-next-page="' . esc_attr($next_page) . '" data-total-pages="' . esc_attr($total_pages) . '"></div>' 336 . '</div>'; 337 wp_die(); 338 } else { 339 // Normal HTML render: show Load More button 340 $total_pages = ceil($question_count / $comments_per_page); 341 if ($page < $total_pages) { 342 $next_page = $page + 1; 343 echo '<div class="revnextwoo-load-more" style="display:flex;justify-content:center;margin:24px 0 0 0;">'; 344 echo '<button id="revnextwoo_load_more_questions" data-next-page="' . esc_attr($next_page) . '" data-total-pages="' . esc_attr($total_pages) . '" style="background:#17a2b8;color:#fff;border:none;padding:10px 14px;border-radius:4px;cursor:pointer;">Load more Q&A</button>'; 345 echo '</div>'; 346 } 347 echo '</div>'; 348 } 349
Note: See TracChangeset
for help on using the changeset viewer.