File: /home/royaltuning/www/public/wp-content/plugins/vp-woo-review-reminder/index.php
<?php
/*
Plugin Name: VP Review Reminder E-mails for WooCommerce
Plugin URI: http://visztpeter.me
Description: Send automatic e-mails to your customers to collect reviews
Author: Viszt Péter
Version: 1.0.3
Text Domain: vp-woo-review-reminder
Domain Path: /languages/
Update URI: vp-woo-review-reminder
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class VP_Woo_Review_Reminder {
protected static $_instance = null;
public static $version;
public static $plugin_path;
//Get main instance
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
//Construct
public function __construct() {
//Check for updates
self::$version = '1.0.3';
self::$plugin_path = trailingslashit(dirname(__FILE__));
require_once( plugin_dir_path( __FILE__ ) . 'class-vp.php' );
$update = new VP_Update_Checker('vp-woo-review-reminder', 'admin.php?page=wc-settings&tab=email§ion=vp_woo_review_reminder_email');
//Add settings field
add_filter( 'woocommerce_get_settings_products', array( $this, 'add_settings' ), 10, 2 );
//Load plugin
add_action( 'plugins_loaded', array( $this, 'init' ) );
add_filter( 'woocommerce_email_classes', array( $this, 'register_email' ), 90, 1 );
//Create action to send review reminder manually from order actions dropdown
add_filter( 'woocommerce_order_actions', array($this, 'send_email_manually'));
add_action( 'woocommerce_order_action_send_review_reminder', array($this, 'send_reminder_manually'));
//Process submitted review(from the e-mail embedded form and on-site)
add_action( 'template_redirect', array($this, 'process_email_review') );
add_action( 'template_redirect', array($this, 'process_product_page_review') );
//Schedule e-mail
add_action( 'woocommerce_order_status_completed', array($this, 'schedule_email'), 10, 1 );
//Function to run for scheduled async jobs
add_action('vp_woo_review_reminder_email', array($this, 'send_reminder_email'), 10, 2);
//Frontend assets
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_assets' ));
//Show image upload form
add_shortcode( 'vp_review_image', array($this, 'image_uploader_shortcode') );
//Handle image upload via ajax
add_action( 'wp_ajax_vp_woo_review_reminder_upload_image', array($this, 'file_upload_callback'));
add_action( 'wp_ajax_nopriv_vp_woo_review_reminder_upload_image', array($this, 'file_upload_callback'));
//Redirect normal review to thank you page
add_action( 'comment_post', array( $this, 'add_review_token' ), 1 );
add_filter( 'comment_post_redirect', array($this, 'redirect_after_normal_review'), 10, 2 );
//Show image in admin
add_filter( 'woocommerce_product_reviews_table_column_comment_content', array($this, 'show_image_in_reviews_admin'), 10, 2 );
//Display image on product page review
add_action( 'woocommerce_review_after_comment_text', array( $this, 'show_image_in_product_page'));
//Custom shortcode to display reviews
add_shortcode( 'vp_reviews_grid', array($this, 'reviews_grid_shortcode') );
//Bulk actions
add_filter( 'bulk_actions-edit-shop_order', array( $this, 'add_bulk_options'), 20, 1);
add_filter( 'handle_bulk_actions-edit-shop_order', array( $this, 'handle_bulk_actions'), 10, 3 );
add_filter( 'bulk_actions-woocommerce_page_wc-orders', array( $this, 'add_bulk_options'), 20, 1 );
add_filter( 'handle_bulk_actions-woocommerce_page_wc-orders', array( $this, 'handle_bulk_actions'), 10, 3 );
add_action( 'admin_notices', array( $this, 'bulk_actions_results') );
}
//Frontend CSS & JS if we are on the thank you page
public function frontend_assets() {
global $post;
if ( is_product() || ( ! empty( $post->post_content ) && strstr( $post->post_content, '[vp_review' ) ) ) {
$css_deps = array();
$js_deps = array('jquery');
if ( current_theme_supports( 'wc-product-gallery-lightbox' ) ) {
$css_deps = array('photoswipe-default-skin');
$js_deps = array('jquery', 'photoswipe', 'photoswipe-ui-default');
add_action( 'wp_footer', 'woocommerce_photoswipe' );
}
wp_enqueue_style( 'vp_woo_review_reminder_frontend_css', plugins_url( '/assets/css/frontend.css',__FILE__ ), $css_deps, VP_Woo_Review_Reminder::$version );
wp_enqueue_script( 'vp_woo_review_reminder_frontend_js', plugins_url( '/assets/js/frontend.min.js',__FILE__ ), $js_deps, VP_Woo_Review_Reminder::$version );
$script_data = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'supports_photoswipe' => current_theme_supports( 'wc-product-gallery-lightbox' )
);
wp_localize_script( 'vp_woo_review_reminder_frontend_js', 'vp_woo_review_reminder_params', $script_data );
}
}
public function add_settings($settings, $current_section) {
$setting_field = array(
'id' => 'vp_woo_review_reminder_redirect',
'title' => __( 'Thank you page', 'vp-woo-review-reminder' ),
'desc' => __('Customer will be redirected to this page after the review has been submitted. Use the [vp_review_image] shortcode to show an additional image upload interface on this page, so the customer can upload an image after they left a review.', 'vp-woo-review-reminder'),
'type' => 'single_select_page_with_search',
'default' => '',
'class' => 'wc-page-search',
'css' => 'min-width:300px;',
'args' => array(
'exclude' =>
array(
wc_get_page_id( 'cart' ),
wc_get_page_id( 'myaccount' ),
wc_get_page_id( 'checkout' ),
),
),
'autoload' => false,
'desc_tip' => true
);
$index = array_search('woocommerce_review_rating_required', array_column($settings, 'id'));
if ($index !== false) {
array_splice($settings, $index + 1, 0, array($setting_field));
}
return $settings;
}
public function register_email( $emails ) {
require_once 'class-email.php';
$emails['VP_Woo_Review_Reminder_Email'] = new VP_Woo_Review_Reminder_Email();
return $emails;
}
public function init() {
//Load translations
load_plugin_textdomain( 'vp-woo-review-reminder', false, basename( dirname( __FILE__ ) ) . '/languages/' );
}
public function send_email_manually($actions) {
$actions['send_review_reminder'] = __( 'Ask for review', 'vp-woo-review-reminder' );
return $actions;
}
public function send_reminder_manually($order) {
$order_items = $order->get_items();
foreach ( $order_items as $item_id => $item ) {
$product = $item->get_product();
if($product) {
WC()->mailer()->emails['VP_Woo_Review_Reminder_Email']->trigger( $order->get_id(), $order, $product );
}
}
}
public function process_email_review() {
if ( isset( $_GET['vp_review_order'] ) ) {
// Get the submitted data
$rating_indicator = isset( $_GET['vp_review_rating'] ) ? intval( $_GET['vp_review_rating'] ) : 5;
$order_key = isset( $_GET['vp_review_order'] ) ? sanitize_text_field( $_GET['vp_review_order'] ) : '';
$review_message = isset( $_GET['vp_review_message'] ) ? wp_kses_post( $_GET['vp_review_message'] ) : '';
$product_id = get_queried_object_id();
$order_id = wc_get_order_id_by_order_key($order_key);
//If order or product not found, just bail
if(!$order_id || !$product_id) return;
//Get order and product
$order = wc_get_order($order_id);
$product = wc_get_product($product_id);
//If order or product not found, just bail
if(!$order || !$product) return;
//Just to verify, make sure product is in the order
if(!$this->is_product_in_order($product_id, $order)) return;
//Check if user already had a review
if($this->is_user_reviewed_product($order, $product_id)) {
wc_add_notice( __('You’ve already submitted a review for this product', 'vp-woo-review-reminder'), 'notice' );
return;
}
//Create review
$token = wp_generate_password(12, false);
$review = array(
'comment_post_ID' => $product_id,
'comment_author' => $order->get_billing_first_name(),
'comment_author_email' => $order->get_billing_email(),
'comment_content' => $review_message,
'comment_type' => 'review',
'comment_approved' => 1,
'comment_meta' => array(
'rating' => $rating_indicator,
'verified' => true,
'_vp_woo_review_token' => $token
)
);
//Set customer id if it was a register customer
if($order->get_customer_id()) {
$review['user_id'] = $order->get_customer_id();
}
//Save review
$review_id = wp_insert_comment($review);
//And redirect user to the success page if set, if not, just to the product page
$redirect = get_option('vp_woo_review_reminder_redirect');
if($redirect && get_post($redirect)) {
$url = add_query_arg( array(
'vp_review_token' => $token
), get_permalink($redirect) );
wp_redirect( $url );
exit;
} else {
wc_add_notice( __('Thank you for your feedback!', 'vp-woo-review-reminder'), 'success' );
wp_redirect( $product->get_permalink() );
exit;
}
}
}
public function process_product_page_review() {
if ( isset( $_GET['vp_review_email_link'] ) && is_product()) {
$order_key = isset( $_GET['vp_review_email_link'] ) ? sanitize_text_field( $_GET['vp_review_email_link'] ) : '';
$order_id = wc_get_order_id_by_order_key($order_key);
$product_id = get_queried_object_id();
//If order or product not found, just bail
if(!$order_id) return;
//Get order and product
$order = wc_get_order($order_id);
$product = wc_get_product($product_id);
//If order or product not found, just bail
if(!$order || !$product) return;
//Just to verify, make sure product is in the order
if(!$this->is_product_in_order($product_id, $order)) return;
//Check if user already had a review
if($this->is_user_reviewed_product($order, $product_id)) {
wc_add_notice( __('You’ve already submitted a review for this product', 'vp-woo-review-reminder'), 'notice' );
return;
}
//Get e-mail template as a website
$email = WC()->mailer()->emails['VP_Woo_Review_Reminder_Email'];
$email->website = true;
$email->trigger( $order->get_id(), $order, $product );
$email_template = $email->get_content_html();
$message = apply_filters( 'woocommerce_mail_content', $email->style_inline( $email_template ) );
echo $message;
exit;
}
}
public function is_product_in_order($product_id, $order) {
//Get a list of product id's in the order
$products_in_order = array();
foreach($order->get_items() as $order_item) {
if($order_item->get_product_id()) {
$products_in_order[] = $order_item->get_product_id();
}
}
return (in_array($product_id, $products_in_order));
}
public function is_user_reviewed_product($order, $product_id) {
$args = array(
'post_type' => 'product',
'author_email' => $order->get_billing_email(),
'post_id' => $product_id,
'status' => 'approve',
);
$existing_reviews = get_comments($args);
if (!empty($existing_reviews)) {
return true;
}
return false;
}
public function schedule_email($order_id) {
$order = wc_get_order($order_id);
$order_items = $order->get_items();
$email = WC()->mailer()->emails['VP_Woo_Review_Reminder_Email'];
foreach ( $order_items as $item_id => $item ) {
$product = $item->get_product();
if($product && $email->is_enabled()) {
//Get sending delay(days)
$delay = $email->get_option('delay', 5);
//Create scheduled action
WC()->queue()->schedule_single(
time() + DAY_IN_SECONDS*$delay,
'vp_woo_review_reminder_email',
array(
'order_id' => $order_id,
'product_id' => $product->get_id()
),
'vp-woo-review-reminder'
);
}
}
}
public function send_reminder_email($order_id, $product_id) {
//Get order and product
$order = wc_get_order($order_id);
$product = wc_get_product($product_id);
//If order or product not found, just bail
if(!$order || !$product) return;
//Check if already sent a review, in this case we can stop
if($this->is_user_reviewed_product($order, $product_id)) {
return;
}
//Check if email is still enabled
$email = WC()->mailer()->emails['VP_Woo_Review_Reminder_Email'];
if(!$email->is_enabled()) {
return;
}
//Send email
$email->trigger( $order->get_id(), $order, $product );
}
public function image_uploader_shortcode() {
ob_start();
$review_token = isset( $_GET['vp_review_token'] ) ? sanitize_text_field( $_GET['vp_review_token'] ) : false;
if(!$review_token) return '';
//Try to get review and order
$review = $this->get_review_by_token( $review_token );
//Check if they exists
if(!$review) {
return '';
}
?>
<form class="vp-woo-review-reminder-image-uploader">
<div class="vp-woo-review-reminder-image-uploader-form">
<label for="vp-woo-review-image">
<span class="vp-woo-review-reminder-image-uploader-icon">📷</span>
<strong class="vp-woo-review-reminder-image-uploader-text"><?php esc_html_e('Choose a photo or drag it here', 'vp-woo-review-reminder'); ?></strong>
</label>
<input type="file" id="vp-woo-review-image" accept="image/*">
</div>
<div class="vp-woo-review-reminder-image-uploader-results">
<div class="vp-woo-review-reminder-image-uploader-results-image">
</div>
<div class="vp-woo-review-reminder-image-uploader-results-text">
<p><?php esc_html_e('Thank you for your review! Once approved, your image will appear on the product page.', 'vp-woo-review-reminder'); ?></p>
<p><a href="#" class="vp-woo-review-reminder-image-uploader-reset"><?php esc_html_e('Select a different image.', 'vp-woo-review-reminder'); ?></a>
</div>
</div>
<input type="hidden" name="review_token" value="<?php echo esc_attr($review_token); ?>">
<input type="hidden" name="action" value="vp_woo_review_reminder_upload_image">
<input type="hidden" name="security" value="<?php echo esc_attr(wp_create_nonce( 'vp_woo_review_reminder_image_upload' )); ?>">
</form>
<?php
return ob_get_clean();
}
public function file_upload_callback() {
check_ajax_referer('vp_woo_review_reminder_image_upload', 'security');
$arr_img_ext = array('image/png', 'image/jpeg', 'image/jpg');
//Check if an image was submitted
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error(array(
'message' => __('Please select an image to upload.', 'vp-woo-review-reminder'),
'code' => 'missing_image'
));
}
//Get comment data(post is the product we are leaving a review for)
$review_token = isset( $_POST['review_token'] ) ? sanitize_text_field( $_POST['review_token'] ) : false;
//If parameters not set
if(!$review_token) {
wp_send_json_error(array(
'message' => __('Missing parameters.', 'vp-woo-review-reminder'),
'code' => 'missing_parameters'
));
}
//Try to get review and order
$review = $this->get_review_by_token( $review_token );
//Check if they exists
if(!$review) {
wp_send_json_error(array(
'message' => __('Review not found.', 'vp-woo-review-reminder'),
'code' => 'review_not_found'
));
}
//Parse submitted image
if (in_array($_FILES['image']['type'], $arr_img_ext)) {
//Create a custom file name
$original_file_name = $_FILES["image"]["name"];
$file_extension = pathinfo($original_file_name, PATHINFO_EXTENSION);
$custom_file_name = 'review-'.$review->comment_ID.'-'.$review->comment_post_ID.'.'.$file_extension;
//Upload file
$upload = wp_upload_bits( $custom_file_name, null, file_get_contents( $_FILES["image"]["tmp_name"] ) );
if ( ! $upload['error'] ) {
//Set it up as an attahcment
$filename = $upload['file'];
$wp_filetype = wp_check_filetype( $filename, null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
'post_content' => '',
'post_status' => 'inherit'
);
//And Save it to the media library too, so its easier to manage and we have nice thumbnails generated
$attachment_id = wp_insert_attachment( $attachment, $filename );
if ( ! is_wp_error( $attachment_id ) ) {
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attachment_data = wp_generate_attachment_metadata( $attachment_id, $filename );
wp_update_attachment_metadata( $attachment_id, $attachment_data );
}
//And pair the attachment id with our review
update_comment_meta($review->comment_ID, '_vp_woo_review_image', $attachment_id);
//In this case, set the review to pending approval just in case
wp_set_comment_status($review->comment_ID, 'hold');
//And return the uploaded image
$uploaded_image = wp_get_attachment_image_src($attachment_id, 'large');
wp_send_json_success(array(
'image' => $uploaded_image[0]
));
} else {
wp_send_json_error(array(
'message' => __('Unable to upload image.', 'vp-woo-review-reminder'),
'code' => 'upload_error',
'error' => $upload['error']
));
}
} else {
//Wrong format
wp_send_json_error(array(
'message' => __('Wrong file format. Please try to upload a different image.', 'vp-woo-review-reminder'),
'code' => 'wrong_format',
));
}
}
public function add_review_token($comment_id) {
if ( isset( $_POST['comment_post_ID'] ) && 'product' === get_post_type( absint( $_POST['comment_post_ID'] ) ) ) {
$token = wp_generate_password(12, false);
add_comment_meta( $comment_id, '_vp_woo_review_token', $token, true );
}
}
public function redirect_after_normal_review($location, $comment) {
$redirect = get_option('vp_woo_review_reminder_redirect');
if ($redirect && 'product' === get_post_type($comment->comment_post_ID)) {
$token = get_comment_meta($comment->comment_ID, '_vp_woo_review_token', true);
if($token) {
$location = add_query_arg( array(
'vp_review_token' => $token
), get_permalink($redirect) );
}
}
return $location;
}
public function get_review_by_token($token) {
$args = array(
'meta_key' => '_vp_woo_review_token',
'meta_value' => $token,
'post_type' => 'product',
'post_status' => 'any',
'number' => 1,
);
$comments = get_comments($args);
if (!empty($comments)) {
return $comments[0];
} else {
return false;
}
}
public function show_image_in_reviews_admin($output, $review) {
$image_id = $this->get_review_image_id($review);
if($image_id) {
$uploaded_image = wp_get_attachment_image_src($image_id, 'thumbnail');
$link = '#';
if(current_user_can( 'edit_comment', $review->comment_ID )) {
$link = get_edit_post_link($image_id);
}
$output .= '<a href="'.esc_url($link).'" target="_blank"><img style="border-radius:4px;margin-top:10px;" width="64" height="64" src="'.esc_url($uploaded_image[0]).'"></a>';
}
return $output;
}
public function get_review_image_id($review) {
$image_id = get_comment_meta( $review->comment_ID, '_vp_woo_review_image', true );
if($image_id && wp_get_attachment_metadata($image_id)) {
return $image_id;
} else {
return false;
}
}
public function show_image_in_product_page($review) {
$image_id = $this->get_review_image_id($review);
if($image_id) {
$full = wp_get_attachment_image_src($image_id, 'full');
echo '<a class="vp-woo-review-image" href="'.esc_url($full[0]).'" data-width="'.$full[1].'" data-height="'.$full[2].'">'.wp_get_attachment_image( $image_id ).'</a>';
}
}
public function show_image_in_blocks($response) {
if(!is_admin()) {
print_r($response);
die();
}
return $response;
}
public function reviews_grid_shortcode($atts) {
$atts = shortcode_atts(array(
'limit' => 10,
'product_id' => '',
'only_images' => false,
), $atts);
$single_product = false;
$args = array(
'number' => $atts['limit'],
'status' => 'approve',
'post_type' => 'product'
);
//If we only need reviews with images
if($atts['only_images']) {
$args['meta_key'] = '_vp_woo_review_image';
$args['meta_compare'] = 'EXISTS';
}
//For specific products
if($atts['product_id']) {
$product_ids = explode(',', $atts['product_id']);
$args['post__in'] = $product_ids;
$single_product = true;
}
//Get results
$reviews = get_comments($args);
ob_start();
wc_get_template('reviews-grid.php', array(
'reviews' => $reviews,
'single_product' => $single_product
), false, VP_Woo_Review_Reminder::$plugin_path . '/templates/');
return ob_get_clean();
}
public function add_bulk_options( $actions ) {
$actions['vp_woo_review_reminder_send'] = __('Send Review Reminders', 'vp-woo-review-reminder');
return $actions;
}
public function handle_bulk_actions( $redirect_to, $action, $post_ids ) {
if( $action == 'vp_woo_review_reminder_send' ) {
$processed = array();
// Loop through each selected order and call the send_reminder_manually() function
foreach ( $post_ids as $order_id ) {
$order = wc_get_order($order_id);
$this->send_reminder_manually($order);
$processed[] = $order_id;
}
$redirect_to = add_query_arg( array('vp_woo_review_reminder_send' => count($processed)), $redirect_to );
return $redirect_to;
} else {
return $redirect_to;
}
}
public function bulk_actions_results() {
if(isset($_REQUEST['vp_woo_review_reminder_send'])) {
$review_count = intval( $_REQUEST['vp_woo_review_reminder_send'] );
?>
<div class="notice notice-info vp-woo-pont-notice vp-woo-pont-bulk-actions vp-woo-pont-print">
<p>
<span><?php echo sprintf( esc_html__( 'Review reminders sent out for %1$s order(s) successfully.', 'vp-woo-review-reminder' ), $review_count); ?></span>
</p>
</div>
<?php
}
}
}
//WC Detection
if ( ! function_exists( 'is_woocommerce_active' ) ) {
function is_woocommerce_active() {
$active_plugins = (array) get_option( 'active_plugins', array() );
if ( is_multisite() ) {
$active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
}
return in_array( 'woocommerce/woocommerce.php', $active_plugins ) || array_key_exists( 'woocommerce/woocommerce.php', $active_plugins ) ;
}
}
//WooCommerce inactive notice.
function vp_woo_review_reminder_notice() {
if ( current_user_can( 'activate_plugins' ) ) {
echo '<div id="message" class="error"><p>';
printf( __( '%1$sThe VP Review Reminder E-mails for WooCommerce extension is inactive%2$s. You need to install and enable %3$sWooCommerce%4$s first. %5$sPlease install and turn on WooCommerce »%6$s', 'vp-woo-user-guide' ), '<strong>', '</strong>', '<a href="http://wordpress.org/extend/plugins/woocommerce/">', '</a>', '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
echo '</p></div>';
}
}
//Initialize
if ( is_woocommerce_active() ) {
function VP_Woo_Review_Reminder() {
return VP_Woo_Review_Reminder::instance();
}
//For backward compatibility
$vp_woo_review_reminder = VP_Woo_Review_Reminder();
} else {
add_action( 'admin_notices', 'vp_woo_review_reminder_notice' );
}