Overview
  • Package
  • Function
  • Tree

Packages

  • EDD
    • License
    • Reviews
      • Licensing
      • Shortcodes
      • Widgets

Classes

  • EDD_Reviews

Functions

  • edd_reviews
  • edd_reviews_callback
   1 <?php
   2 /**
   3  * Plugin Name: Easy Digital Downloads - Reviews
   4  * Plugin URI: http://easydigitaldownloads.com/extension/reviews/
   5  * Description: A fully featured reviewing system for Easy Digital Downloads.
   6  * Author: Sunny Ratilal
   7  * Version: 1.1
   8  * Requires at least: 3.6
   9  * Tested up to: 3.6
  10  *
  11  * Text Domain: edd-reviews
  12  * Domain Path: languages
  13  *
  14  * Copyright 2013 Sunny Ratilal
  15  *
  16  * @package     EDD_Reviews
  17  * @category    Core
  18  * @author      Sunny Ratilal
  19  * @version     1.1
  20  */
  21 
  22 // Exit if accessed directly
  23 if ( ! defined( 'ABSPATH' ) ) exit;
  24 
  25 if ( ! class_exists( 'EDD_Reviews' ) ) :
  26 
  27 /**
  28  * EDD_Reviews Class
  29  *
  30  * @package EDD_Reviews
  31  * @since   1.0
  32  * @version 1.1
  33  * @author  Sunny Ratilal
  34  */
  35 final class EDD_Reviews {
  36     /**
  37      * EDD Reviews uses many variables, several of which can be filtered to
  38      * customize the way it operates. Most of these variables are stored in a
  39      * private array that gets updated with the help of PHP magic methods.
  40      *
  41      * @var array
  42      * @see EDD_Reviews::setup_globals()
  43      * @since 1.0
  44      */
  45     private $data;
  46 
  47     /**
  48      * Holds the instance
  49      *
  50      * Ensures that only one instance of EDD Reviews exists in memory at any one
  51      * time and it also prevents needing to define globals all over the place.
  52      *
  53      * TL;DR This is a static property property that holds the singleton instance.
  54      *
  55      * @var object
  56      * @static
  57      * @since 1.0
  58      */
  59     private static $instance;
  60 
  61     /**
  62      * Boolean whether or not to use the singleton, comes in handy
  63      * when doing testing
  64      *
  65      * @var bool
  66      * @static
  67      * @since 1.0
  68      */
  69     public static $testing = false;
  70 
  71     /**
  72      * Holds the version number
  73      *
  74      * @var string
  75      * @since 1.0
  76      */
  77     public $version = '1.1';
  78 
  79     /**
  80      * Get the instance and store the class inside it. This plugin utilises
  81      * the PHP singleton design pattern.
  82      *
  83      * @since 1.0
  84      * @static
  85      * @staticvar array $instance
  86      * @access public
  87      * @see edd_reviews();
  88      * @uses EDD_Reviews::setup_globals() Setup the globals needed
  89      * @uses EDD_Reviews::load_classes() Loads all the classes
  90      * @uses EDD_Reviews::hooks() Setup hooks and actions
  91      * @return object self::$instance Instance
  92      */
  93     public static function get_instance() {
  94         if ( ! isset( self::$instance ) && ! ( self::$instance instanceof EDD_Reviews ) || self::$testing ) {
  95             self::$instance = new EDD_Reviews;
  96             self::$instance->setup_globals();
  97             self::$instance->load_classes();
  98             self::$instance->hooks();
  99             self::$instance->updater();
 100         }
 101 
 102         return self::$instance;
 103     }
 104 
 105     /**
 106      * Constructor Function
 107      *
 108      * @since 1.0
 109      * @access protected
 110      * @see EDD_Reviews::init()
 111      * @see EDD_Reviews::activation()
 112      */
 113     public function __construct() {
 114         self::$instance = $this;
 115 
 116         add_action( 'init', array( $this, 'init' ) );
 117         register_activation_hook( __FILE__, array( $this, 'activation' ) );
 118     }
 119 
 120     /**
 121      * Sets up the constants/globals used
 122      *
 123      * @since 1.0
 124      * @static
 125      * @access public
 126      */
 127     private function setup_globals() {
 128         // File Path and URL Information
 129         $this->file          = __FILE__;
 130         $this->basename      = apply_filters( 'edd_reviews_plugin_basenname', plugin_basename( $this->file ) );
 131         $this->plugin_url    = plugin_dir_url( __FILE__ );
 132         $this->plugin_path   = plugin_dir_path( __FILE__ );
 133         $this->lang_dir      = apply_filters( 'edd_reviews_lang_dir',         trailingslashit( $this->plugin_path . 'languages' ) );
 134 
 135         // Assets
 136         $this->assets_dir    = apply_filters( 'edd_reviews_assets_dir',       trailingslashit( $this->plugin_path . 'assets'    ) );
 137         $this->assets_url    = apply_filters( 'edd_reviews_assets_url',       trailingslashit( $this->plugin_url  . 'assets'    ) );
 138 
 139         // Classes
 140         $this->classes_dir   = apply_filters( 'edd_reviews_classes_dir',      trailingslashit( $this->plugin_path . 'classes'   ) );
 141         $this->classes_url   = apply_filters( 'edd_reviews_classes_url',      trailingslashit( $this->plugin_url  . 'classes'   ) );
 142 
 143         // Templating
 144         $this->templates_dir = apply_filters( 'edd_reviews_templates_dir',    trailingslashit( $this->plugin_path . 'templates' ) );
 145         $this->templates_url = apply_filters( 'edd_reviews_templates_url',    trailingslashit( $this->plugin_url  . 'templates' ) );
 146     }
 147 
 148     /**
 149      * Throw error on object clone
 150      *
 151      * The whole idea of the singleton design pattern is that there is a single
 152      * object therefore, we don't want the object to be cloned.
 153      *
 154      * @since 1.0
 155      * @access protected
 156      * @return void
 157      */
 158     public function __clone() {
 159         // Cloning instances of the class is forbidden
 160         _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'edd-reviews' ), '1.0' );
 161     }
 162 
 163     /**
 164      * Disable unserializing of the class
 165      *
 166      * @since 1.0
 167      * @access protected
 168      * @return void
 169      */
 170     public function __wakeup() {
 171         // Unserializing instances of the class is forbidden
 172         _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'edd-reviews' ), '1.0' );
 173     }
 174 
 175     /**
 176      * Magic method for checking if custom variables have been set
 177      *
 178      * @since 1.0
 179      * @access protected
 180      * @return void
 181      */
 182     public function __isset( $key ) {
 183         return isset( $this->data[ $key ] );
 184     }
 185 
 186     /**
 187      * Magic method for getting variables
 188      *
 189      * @since 1.0
 190      * @access protected
 191      * @return void
 192      */
 193     public function __get( $key ) {
 194         return isset( $this->data[ $key ] ) ? $this->data[ $key ] : null;
 195     }
 196 
 197     /**
 198      * Magic method for setting variables
 199      *
 200      * @since 1.0
 201      * @access protected
 202      * @return void
 203      */
 204     public function __set( $key, $value ) {
 205         $this->data[ $key ] = $value;
 206     }
 207 
 208     /**
 209      * Magic method for unsetting variables
 210      *
 211      * @since 1.0
 212      * @access protected
 213      * @return void
 214      */
 215     public function __unset( $key ) {
 216         if ( isset( $this->data[ $key ] ) )
 217             unset( $this->data[ $key ] );
 218     }
 219 
 220     /**
 221      * Magic method to prevent notices and errors from invalid method calls
 222      *
 223      * @since 1.0
 224      * @access public
 225      *
 226      * @param string $name
 227      * @param array $args
 228      *
 229      * @return void
 230      */
 231     public function __call( $name = '', $args = array() ) {
 232         unset( $name, $args );
 233         return null;
 234     }
 235 
 236     /**
 237      * Reset the instance of the class
 238      *
 239      * @since 1.0
 240      * @access public
 241      * @static
 242      */
 243     public static function reset() {
 244         self::$instance = null;
 245     }
 246 
 247     /**
 248      * Function fired on init
 249      *
 250      * This function is called on WordPress 'init'. It's triggered from the
 251      * constructor function.
 252      *
 253      * @since 1.0
 254      * @access public
 255      *
 256      * @uses EDD_Reviews::load_plugin_textdomain()
 257      * @uses EDD_Reviews::add_shortcodes()
 258      *
 259      * @return void
 260      */
 261     public function init() {
 262         do_action( 'edd_reviews_before_init' );
 263 
 264         $this->load_plugin_textdomain();
 265 
 266         $this->add_shortcodes();
 267 
 268         do_action( 'edd_reviews_after_init' );
 269     }
 270 
 271     /**
 272      * Loads Classes
 273      *
 274      * @since 1.0
 275      * @access private
 276      * @return void
 277      */
 278     private function load_classes() {
 279         if ( ! class_exists( 'EDD_License' ) ) require $this->classes_dir . 'class-edd-license-handler.php';
 280         require $this->classes_dir . 'shortcodes/class-edd-reviews-shortcode-review.php';
 281         require $this->classes_dir . 'widgets/class-reviews-widget.php';
 282         require $this->classes_dir . 'widgets/class-featured-review-widget.php';
 283     }
 284 
 285     /**
 286      * Load Plugin Text Domain
 287      *
 288      * Looks for the plugin translation files in certain directories and loads
 289      * them to allow the plugin to be localised
 290      *
 291      * @since 1.0
 292      * @access public
 293      * @return bool True on success, false on failure
 294      */
 295     public function load_plugin_textdomain() {
 296         // Traditional WordPress plugin locale filter
 297         $locale = apply_filters( 'plugin_locale',  get_locale(), 'edd-reviews' );
 298         $mofile = sprintf( '%1$s-%2$s.mo', 'edd-reviews', $locale );
 299 
 300         // Setup paths to current locale file
 301         $mofile_local  = $this->lang_dir . $mofile;
 302 
 303         if ( file_exists( $mofile_local ) ) {
 304             // Look in the /wp-content/plugins/edd-reviews/languages/ folder
 305             load_textdomain( 'edd-reviews', $mofile_local );
 306         } else {
 307             // Load the default language files
 308             load_plugin_textdomain( 'edd-reviews', false, $this->lang_dir );
 309         }
 310 
 311         return false;
 312     }
 313 
 314     /**
 315      * Activation function fires when the plugin is activated.
 316      *
 317      * This function is fired when the activation hook is called by WordPress,
 318      * it flushes the rewrite rules and disables the plugin if EDD isn't active
 319      * and throws an error.
 320      *
 321      * @since 1.0
 322      * @access public
 323      *
 324      * @return void
 325      */
 326     public function activation() {
 327         flush_rewrite_rules();
 328 
 329         if ( ! class_exists( 'Easy_Digital_Downloads' ) ) {
 330             if ( is_plugin_active( $this->basename ) ) {
 331                 deactivate_plugins( $this->basename );
 332                 unset( $_GET[ 'activate' ] );
 333                 add_action( 'admin_notices', array( $this, 'admin_notices' ) );
 334             }
 335         }
 336     }
 337 
 338     /**
 339      * Adds all the shortcodes
 340      *
 341      * @since 1.0
 342      * @access public
 343      * @return void
 344      */
 345     public function add_shortcodes() {
 346         add_shortcode( 'review', array( 'EDD_Reviews_Shortcode_Review', 'render' ) );
 347     }
 348 
 349     /**
 350      * Adds all the hooks/filters
 351      *
 352      * The plugin relies heavily on the use of hooks and filters and modifies
 353      * default WordPress behaviour by the use of actions and filters which are
 354      * provided by WordPress.
 355      *
 356      * Actions are provided to hook on this function, before the hooks and filters
 357      * are added and after they are added. The class object is passed via the action.
 358      *
 359      * @since 1.0
 360      * @access public
 361      * @return void
 362      */
 363     public function hooks() {
 364         do_action_ref_array( 'edd_reviews_before_setup_actions', array( &$this ) );
 365 
 366         /** Actions */
 367         add_action( 'comment_post',                            array( $this, 'save_review_meta'   ) );
 368         add_action( 'add_meta_boxes',                          array( $this, 'disable_trackbacks' ) );
 369         add_action( 'add_meta_boxes',                          array( $this, 'change_meta_boxes'  ) );
 370         add_action( 'wp_enqueue_scripts',                      array( $this, 'load_styles'        ) );
 371         add_action( 'wp_enqueue_scripts',                      array( $this, 'load_scripts'       ) );
 372         add_action( 'admin_enqueue_scripts',                   array( $this, 'admin_scripts'      ) );
 373         add_action( 'the_content',                             array( $this, 'microdata'          ) );
 374         add_action( 'admin_init',                              array( $this, 'activate_license'   ) );
 375         add_action( 'wp_before_admin_bar_render',              array( $this, 'admin_bar_menu'     ) );
 376         add_action( 'edd_reviews_review_display',              array( $this, 'render_review'      ), 10, 3 );
 377         add_action( 'widgets_init',                            array( $this, 'register_widgets'   ) );
 378         add_action( 'init',                                    array( $this, 'process_vote'       ) );
 379         add_action( 'wp_ajax_edd_reviews_process_vote',        array( $this, 'process_ajax_vote'  ) );
 380         add_action( 'wp_ajax_nopriv_edd_reviews_process_vote', array( $this, 'process_ajax_vote'  ) );
 381         add_action( 'wp_dashboard_setup',                      array( $this, 'dashboard_widgets'  ) );
 382         add_action( 'add_meta_boxes',                          array( $this, 'add_meta_boxes'     ) );
 383         add_action( 'edit_comment',                            array( $this, 'update_review_meta' ) );
 384         add_action( 'init',                                    array( $this, 'tinymce_button'     ) );
 385         add_action( 'init',                                    array( $this, 'process_mce_dialog' ) );
 386 
 387         /** Filters */
 388         add_filter( 'preprocess_comment',                      array( $this, 'check_author'       ) );
 389         add_filter( 'edd_download_supports',                   array( $this, 'enable_comments'    ) );
 390         add_filter( 'preprocess_comment',                      array( $this, 'check_review_title' ) );
 391         add_filter( 'preprocess_comment',                      array( $this, 'check_rating'       ) );
 392         add_filter( 'comment_form_default_fields',             array( $this, 'remove_url'         ) );
 393         add_filter( 'edd_settings_styles',                     array( $this, 'styles_settings'    ) );
 394         add_filter( 'edd_settings_misc',                       array( $this, 'misc_settings'      ) );
 395         add_filter( 'manage_edit-comments_columns',            array( $this, 'custom_columns'     ) );
 396         add_filter( 'manage_comments_custom_column',           array( $this, 'custom_column_data' ), 10, 2 );
 397         add_filter( 'comments_open',                           array( $this, 'open_all_comments'  ), 10, 2 );
 398         add_filter( 'comments_template',                       array( $this, 'comments_template'  ) );
 399         add_filter( 'edd_api_valid_query_modes',               array( $this, 'register_api_mode'  ) );
 400         add_filter( 'edd_api_output_data',                     array( $this, 'api_output'         ), 10, 3 );
 401         add_filter( 'query_vars',                              array( $this, 'query_vars'         ) );
 402         add_filter( 'plugin_row_meta',                         array( $this, 'plugin_links'       ), 10, 2 );
 403 
 404         do_action_ref_array( 'edd_reviews_after_setup_actions', array( &$this ) );
 405     }
 406 
 407     /**
 408      * Register Widgets
 409      *
 410      * @since 1.0
 411      * @access public
 412      * @return void
 413      */
 414     public function register_widgets() {
 415         register_widget( 'EDD_Reviews_Widget_Reviews' );
 416         register_widget( 'EDD_Reviews_Widget_Featured_Review' );
 417     }
 418 
 419     /**
 420      * Comments Template
 421      *
 422      * Override the default comments template for the download post type
 423      *
 424      * @since 1.0
 425      * @access public
 426      * @return string Path to the review template
 427      */
 428     public function comments_template( $template ) {
 429         if ( 'download' !== get_post_type() )
 430             return $template;
 431 
 432         // Looks in the stylesheet directory (i.e. if a child theme is used)
 433         if ( file_exists( trailingslashit( get_stylesheet_directory() ) . 'edd_templates/reviews.php' ) ) {
 434             return trailingslashit( get_stylesheet_directory() ) . 'edd_templates/reviews.php';
 435         // Looks in the theme directory (i.e. the parent theme directory)
 436         } elseif ( file_exists( trailingslashit( get_template_directory() ) . 'edd_templates/reviews.php' ) ) {
 437             return trailingslashit( get_template_directory() ) . 'edd_templates/reviews.php';
 438         // Otherwise returns the default template provided by the plugin
 439         } else {
 440             return apply_filters( 'edd_reviews_comments_template_path', $this->plugin_path . '/templates/reviews.php' );
 441         }
 442     }
 443 
 444     /**
 445      * Reviews form
 446      *
 447      * This function is called by the reviews template and overrides the default
 448      * comments form by replacing the fields in order for reviews to be placed.
 449      *
 450      * @since 1.0
 451      * @access public
 452      * @return void
 453      */
 454     public function reviews_form() {
 455         echo '<div id="reviews_form" class="edd_reviews_form">';
 456 
 457         $commenter = wp_get_current_commenter();
 458 
 459         $form = array(
 460             'title_reply' => apply_filters( 'edd_reviews_leave_a_review_text',  __( 'Leave a Review', 'edd-reviews' ) ),
 461             'comment_notes_before' => '',
 462             'comment_notes_after' => '',
 463             'fields' => array(
 464                 'author' => '<p class="comment-form-author">' . '<label for="author">' . __( 'Name', 'edd-reviews' ) . '<span class="required">*</span></label>' .
 465                             '<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" aria-required="true" /></p>',
 466                 'email'  => '<p class="comment-form-email"><label for="email">' . __( 'Email', 'edd-reviews' ) . '<span class="required">*</span></label>' .
 467                             '<input id="email" name="email" type="text" value="' . esc_attr(  $commenter['comment_author_email'] ) . '" size="30" aria-required="true" /></p>',
 468             ),
 469             'label_submit' => __( 'Submit Review', 'edd-reviews' ),
 470             'logged_in_as' => '',
 471             'comment_field' => ''
 472         );
 473 
 474         $form['comment_field'] = apply_filters( 'edd_reviews_review_form_template', '
 475             <p class="comment_form_review_title">
 476                 <label for="edd_review_title">' . __( 'Review Title', 'edd-reviews' ) . '<span class="required">*</span></label>
 477                 <input type="text" name="edd_review_title" id="edd_review_title" value="" size="30" aria-required="true" />
 478             </p>
 479 
 480             <p class="comment_form_rating">
 481                 <label for="edd_rating">' . __( 'Rating', 'edd-reviews' ) . '<span class="required">*</span></label>
 482                 <span class="edd_reviews_rating_box">
 483                     <span class="edd_star_rating"></span>
 484                     <span class="edd_ratings">
 485                         <a class="edd_rating" href="" data-rating="5"><span></span></a>
 486                         <span class="edd_show_if_no_js"><input type="radio" name="edd_rating" id="edd_rating" value="5"/>5&nbsp;</span>
 487 
 488                         <a class="edd_rating" href="" data-rating="4"><span></span></a>
 489                         <span class="edd_show_if_no_js"><input type="radio" name="edd_rating" id="edd_rating" value="4"/>4&nbsp;</span>
 490 
 491                         <a class="edd_rating" href="" data-rating="3"><span></span></a>
 492                         <span class="edd_show_if_no_js"><input type="radio" name="edd_rating" id="edd_rating" value="3"/>3&nbsp;</span>
 493 
 494                         <a class="edd_rating" href="" data-rating="2"><span></span></a>
 495                         <span class="edd_show_if_no_js"><input type="radio" name="edd_rating" id="edd_rating" value="2"/>2&nbsp;</span>
 496 
 497                         <a class="edd_rating" href="" data-rating="1"><span></span></a>
 498                         <span class="edd_show_if_no_js"><input type="radio" name="edd_rating" id="edd_rating" value="1"/>1&nbsp;</span>
 499                     </span>
 500                 </span>
 501             </p>
 502 
 503             <p class="comment-form-comment">
 504                 <label for="comment">' . __( 'Review', 'edd-reviews' ) . '<span class="required">*</span></label>
 505                 <textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea>
 506             </p>
 507 
 508             <input type="hidden" id="edd_rating" name="edd_rating" />
 509             <input type="hidden" name="edd_review" value="true" />
 510         ' );
 511 
 512         do_action( 'edd_reviews_review_form_before' );
 513 
 514         comment_form( apply_filters( 'edd_reviews_review_form_args', $form ) );
 515 
 516         do_action( 'edd_reviews_review_form_after' );
 517 
 518         echo '</div>';
 519     }
 520 
 521     /**
 522      * Checks if multiple reviews have been disabled and then verifies
 523      * if the author has already posted a review for this download (product).
 524      * This function queries the database for any reviews by taking the
 525      * comment_post_ID and comment_author_email and if anything is returned, execution
 526      * of the comment addition will fail with wp_die().
 527      *
 528      * @since 1.2
 529      * @access public
 530      * @param  array $commentdata All the comment data sent via $_POST
 531      * @global array $edd_options Used to access the EDD Options
 532      * @return object|bool Returns an instance of wp_die() or the comment data
 533      */
 534     public function check_author( $commentdata ) {
 535         global $edd_options;
 536 
 537         if ( isset( $edd_options['edd_reviews_disable_multiple_reviews'] ) ) {
 538             $args = array(
 539                 'author_email' => $commentdata['comment_author_email'],
 540                 'post_id'      => $commentdata['comment_post_ID']
 541             );
 542 
 543             $comments = get_comments( $args );
 544 
 545             if ( $comments ) {
 546                 wp_die(
 547                     __( 'You are only allowed to post one review for this product. Multiple reviews have been disabled.', 'edd-reviews' ),
 548                     __( 'Multiple Reviews Not Allowed', 'edd-reviews' ),
 549                     array( 'back_link' => true )
 550                 );
 551             } else {
 552                 return $commentdata;
 553             }
 554         } else {
 555             return $commentdata;
 556         }
 557     }
 558 
 559     /**
 560      * Checks if a review title has been entered otherwise dies with an error
 561      *
 562      * @since 1.0
 563      * @access public
 564      * @param  array $commentdata All the comment data sent via $_POST
 565      * @return array $commentdata All the comment data sent via $_POST
 566      */
 567     public function check_review_title( $commentdata ) {
 568         if ( isset( $_POST['edd_review'] ) && ! isset( $_POST['edd_review_title'] ) ) {
 569             wp_die( sprintf( __( '%sERROR:%s You did not add a review title.', 'edd-reviews' ), '<strong>', '</strong>' ), __( 'Error', 'edd-reviews' ), array( 'back_link' => true ) );
 570         }
 571 
 572         return $commentdata;
 573     }
 574 
 575     /**
 576      * Checks if a rating has been made otherwise dies with an error
 577      *
 578      * @since 1.0
 579      * @access public
 580      * @param  array $commentdata All the comment data sent via $_POST
 581      * @return array $commentdata All the comment data sent via $_POST
 582      */
 583     public function check_rating( $commentdata ) {
 584         if ( isset( $_POST['edd_review'] ) && ! isset( $_POST['edd_rating'] ) && is_int( $_POST['edd_rating'] )  && ( ! $_POST['edd_rating'] > 5 || $_POST['edd_rating'] < 0 ) ) {
 585             wp_die( sprintf( __( '%sERROR:%s You did not add a rating or the rating you supplied was not validated.', 'edd-reviews' ), '<strong>', '</strong>' ), __( 'Error', 'edd-reviews' ), array( 'back_link' => true ) );
 586         }
 587 
 588         return $commentdata;
 589     }
 590 
 591     /**
 592      * Remove URL field from Comments (Review) Form only on the Download page
 593      *
 594      * @since 1.0
 595      * @access public
 596      *
 597      * @param  array $fields All the comment fields
 598      * @return array $fields Updated list of comment fields without the URL
 599      */
 600     public function remove_url( $fields ) {
 601         if ( is_singular( 'download' ) && comments_open() ) {
 602             if ( isset( $fields['url'] ) ) {
 603                 unset( $fields['url'] );
 604             }
 605         }
 606 
 607         return $fields;
 608     }
 609 
 610     /**
 611      * Save the Review Meta Data
 612      *
 613      * @since 1.0
 614      * @access public
 615      * @param int $comment_id Comment ID
 616      * @return void
 617      */
 618     public function save_review_meta( $comment_id ) {
 619         /** Check if a rating has been submitted */
 620         if ( isset( $_POST['edd_review'] ) && isset( $_POST['edd_rating'] ) && ! empty( $_POST['edd_review_title'] ) ) {
 621             $rating = wp_filter_nohtml_kses( $_POST['edd_rating'] );
 622             add_comment_meta( $comment_id, 'edd_rating', $rating );
 623         }
 624 
 625         /** Check if a review title has been submitted */
 626         if ( isset( $_POST['edd_review'] ) && isset( $_POST['edd_review_title'] ) && ! empty( $_POST['edd_review_title'] ) ) {
 627             $review_title = sanitize_text_field( wp_filter_nohtml_kses( esc_html( $_POST['edd_review_title'] ) ) );
 628             add_comment_meta( $comment_id, 'edd_review_title', $review_title );
 629         }
 630     }
 631 
 632     /**
 633      * Microdata
 634      *
 635      * @since 1.0
 636      * @access public
 637      * @global object $post Used to access the post data
 638      *
 639      * @uses EDD_Reviews::average_rating()
 640      *
 641      * @param  string $content Content of the post
 642      * @return string $content Content of the post with the microdata
 643      */
 644     public function microdata( $content ) {
 645         global $post;
 646 
 647         // Bail if we're not on a download page
 648         if ( ! is_singular( 'download' ) )
 649             return $content;
 650 
 651         do_action( 'edd_reviews_microdata_before' );
 652         ?>
 653         <div style="display:none" class="edd-review-microdata" itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
 654             <span itemprop="ratingValue"><?php echo $this->average_rating(); ?></span>
 655             <span itemprop="reviewCount"><?php echo wp_count_comments( $post->ID )->total_comments; ?></span>
 656         </div>
 657         <?php
 658         do_action( 'edd_reviews_microdata_after' );
 659         return $content;
 660     }
 661 
 662     /**
 663      * Get Average Rating
 664      *
 665      * @since 1.0
 666      * @access public
 667      * @global object Used to access the post data
 668      *
 669      * @param  bool $echo Whether to echo the result or return it
 670      * @return string $average Returns the average rating
 671      */
 672     public function average_rating( $echo = true ) {
 673         global $post;
 674 
 675         $reviews = get_comments( apply_filters( 'edd_reviews_average_rating_query_args', array(
 676             'post_id' => $post->ID
 677         ) ) );
 678 
 679         $total_ratings = 0;
 680 
 681         foreach ( $reviews as $review ) {
 682             $rating = get_comment_meta( $review->comment_ID, 'edd_rating', true );
 683             $total_ratings += $rating;
 684         }
 685 
 686         $total = wp_count_comments( $post->ID )->total_comments;
 687 
 688         if ( 0 == $total )
 689             $total = 1;
 690 
 691         $average = round( $total_ratings / $total, 1 );
 692 
 693         if ( $echo ) {
 694             echo $average;
 695         } else {
 696             return $average;
 697         }
 698     }
 699 
 700     /**
 701      * Enable Reviews on all Downloads
 702      *
 703      * @since 1.0
 704      * @access public
 705      *
 706      * @param array $support What the Downloads post type supports (e.g. title)
 707      * @return array Merged array with comments support enabled for downloads
 708      */
 709     public function enable_comments( $supports ) {
 710         return array_merge( $supports, array( 'comments' ) );
 711     }
 712 
 713     /**
 714      * Disable Trackbacks
 715      *
 716      * This function removes the Trackbacks meta box from the Add/Edit Download
 717      * screen as it's not the sole purpose of this plugin and if any were to be
 718      * made, they wouldn't render correctly as the plugin doesn't provide
 719      * support for trackbacks.
 720      *
 721      * @since 1.0
 722      * @access public
 723      * @return void
 724      */
 725     public function disable_trackbacks() {
 726         remove_meta_box( 'trackbacksdiv', 'download', 'normal' );
 727     }
 728 
 729     /**
 730      * Edit Meta Boxes
 731      *
 732      * The Comments meta box on the Add/Edit Download screen is renamed here and
 733      * the callback function for the comments meta box is also changed.
 734      *
 735      * @since 1.0
 736      * @access public
 737      * @global array Used to edit the admin meta boxes
 738      * @return void
 739      */
 740     public function change_meta_boxes() {
 741         global $wp_meta_boxes;
 742 
 743         /**
 744          * The comment status box (whether comments are open or not) does not
 745          * need to be displayed because a filter is used to force open all
 746          * comments for the downloads post type
 747          */
 748         unset( $wp_meta_boxes['download']['normal']['core']['commentstatusdiv'] );
 749 
 750         /** Titles */
 751         $wp_meta_boxes['download']['normal']['core']['commentsdiv']['title'] = __( 'Reviews', 'edd-reviews' );
 752 
 753         /** Callbacks */
 754         $wp_meta_boxes['download']['normal']['core']['commentsdiv']['callback'] = array( $this, 'post_comment_meta_box' );
 755     }
 756 
 757     /**
 758      * Override the default comment status meta box
 759      *
 760      * @since 1.0
 761      * @access public
 762      *
 763      * @param object $post Post Object
 764      * @return void
 765      */
 766     public function post_comment_status_meta_box( $post ) {
 767         ?>
 768         <input name="advanced_view" type="hidden" value="1" />
 769         <p class="meta-options">
 770             <label for="comment_status" class="selectit">
 771                 <input name="comment_status" type="checkbox" id="comment_status" value="open" <?php checked( $post->comment_status, 'open' ); ?> /> <?php _e( 'Allow reviews.', 'edd-reviews' ) ?>
 772             </label>
 773         </p>
 774         <?php
 775     }
 776 
 777     /**
 778      * Override the default comments meta box on Add/Edit screen
 779      *
 780      * @since 1.0
 781      * @access public
 782      * @global object Used to query the database using the WordPress Database API
 783      *
 784      * @param object $post Current Post
 785      * @return void
 786      */
 787     public function post_comment_meta_box( $post ) {
 788         global $wpdb;
 789 
 790         wp_nonce_field( 'get-comments', 'add_comment_nonce', false );
 791 
 792         $total = get_comments( array( 'post_id' => $post->ID, 'number' => 1, 'count' => true ) );
 793         $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table' );
 794         $wp_list_table->display( true );
 795 
 796         if ( 1 > $total ) {
 797             echo '<p id="no-comments">' . apply_filters( 'edd_reviews_admin_no_reviews_text', __( 'No reviews yet.', 'edd-reviews' ) ) . '</p>';
 798         } else {
 799             $hidden = get_hidden_meta_boxes( get_current_screen() );
 800             if ( ! in_array( 'commentsdiv', $hidden ) ) {
 801                 ?>
 802                 <script type="text/javascript">jQuery(document).ready(function(){commentsBox.get(<?php echo $total; ?>, 10);});</script>
 803                 <?php
 804             }
 805 
 806             ?>
 807             <p class="hide-if-no-js" id="show-comments"><a href="#commentstatusdiv" onclick="commentsBox.get(<?php echo $total; ?>);return false;"><?php _e('Show comments'); ?></a> <span class="spinner"></span></p>
 808             <?php
 809         }
 810 
 811         wp_comment_trashnotice();
 812     }
 813 
 814     /**
 815      * Load Styles
 816      *
 817      * @since 1.0
 818      * @access public
 819      * @global array $edd_options Used to access the EDD Options
 820      * @return void
 821      */
 822     public function load_styles() {
 823         global $edd_options;
 824 
 825         wp_register_style( 'edd-reviews-admin', $this->assets_url . 'css/edd-reviews-admin.css', array( ), $this->version );
 826         wp_enqueue_style( 'edd-reviews-admin' );
 827 
 828         if ( isset( $edd_options['edd_reviews_disable_css'] ) )
 829             return;
 830 
 831         wp_register_style( 'edd-reviews', $this->assets_url . 'css/edd-reviews.css', array( ), $this->version );
 832         wp_enqueue_style( 'edd-reviews' );
 833     }
 834 
 835     /**
 836      * Load Scripts
 837      *
 838      * @since 1.0
 839      * @access public
 840      * @return void
 841      */
 842     public function load_scripts() {
 843         wp_register_script( 'edd-reviews-js', $this->assets_url . 'js/edd-reviews.js', array( 'jquery' ), $this->version );
 844 
 845         if ( is_singular( 'download' ) ) {
 846             wp_enqueue_script( 'edd-reviews-js' );
 847         }
 848 
 849         $edd_reviews_params = array(
 850             'ajax_url'         => admin_url( 'admin-ajax.php' ),
 851             'edd_voting_nonce' => wp_create_nonce( 'edd_reviews_voting_nonce' ),
 852             'thank_you_msg'    => apply_filters( 'edd_reviews_thank_you_for_voting_message', __( 'Thank you for your feedback.', 'edd-reviews' ) )
 853         );
 854 
 855         wp_localize_script( 'edd-reviews-js', 'edd_reviews_params', apply_filters( 'edd_reviews_js_params', $edd_reviews_params ) );
 856     }
 857 
 858     /**
 859      * Load Admin Scripts/Styles
 860      *
 861      * @since 1.0
 862      * @access public
 863      * @global object Used to access the current 'screen' that the user is
 864      *  browsing
 865      * @global object Used to access the post data
 866      * @return void
 867      */
 868     public function admin_scripts() {
 869         global $current_screen, $post;
 870 
 871         wp_register_style( 'edd-reviews-admin', $this->assets_url . 'css/edd-reviews-admin.css', array( ), $this->version );
 872 
 873         wp_enqueue_style( 'edd-reviews-admin' );
 874         wp_enqueue_style( 'wp-color-picker' );
 875     }
 876 
 877     /**
 878      * Shows Review Meta on Comments List Table
 879      *
 880      * @since 1.0
 881      * @access public
 882      *
 883      * @param array $columns All the columns on the list table
 884      * @return array $columns New columns with Review Title and Rating added
 885      */
 886     function custom_columns( $columns ) {
 887         $columns['title']  = __( 'Review Title', 'edd-reviews' );
 888         $columns['rating'] = __( 'Rating', 'edd-reviews' );
 889 
 890         return $columns;
 891     }
 892 
 893     /**
 894      * Display Custom Column Data
 895      *
 896      * @since 1.0
 897      * @access public
 898      *
 899      * @param string $column Current column
 900      * @param int $comment_ID Comment ID
 901      * @return void
 902      */
 903     public function custom_column_data( $column, $comment_ID ) {
 904         if ( 'title' == $column ) {
 905             if ( get_comment_meta( $comment_ID, 'edd_review_title', true ) )
 906                 echo get_comment_meta( $comment_ID, 'edd_review_title', true );
 907             else
 908                 echo '-';
 909         }
 910 
 911         if ( 'rating' == $column ) {
 912             if ( get_comment_meta( $comment_ID, 'edd_rating', true ) )
 913                 echo get_comment_meta( $comment_ID, 'edd_rating', true ) . ' / 5';
 914             else
 915                 echo '-';
 916         }
 917     }
 918 
 919     /**
 920      * Open Comments for all Downloads
 921      *
 922      * @since 1.0
 923      * @access public
 924      *
 925      * @param bool $open Whether the comments are open or not
 926      * @param string $post_id Post ID
 927      * @return bool $open Whether the comments are open or not
 928      */
 929     public function open_all_comments( $open, $post_id ) {
 930         $post_type = get_post_type( $post_id );
 931 
 932         if ( 'download' == $post_type )
 933             $open = true;
 934 
 935         return $open;
 936     }
 937 
 938     /**
 939      * Register Misc Settings
 940      *
 941      * @since 1.0
 942      * @access public
 943      *
 944      * @param array $settings Existing registered settings
 945      * @param array New settings
 946      * @return array Merged array with new settings added
 947      */
 948     public function misc_settings( $settings ) {
 949         $new = array(
 950             array(
 951                 'id'   => 'edd_review_settings',
 952                 'name' => '<strong>' . __( 'Reviews', 'edd-reviews' ) . '</strong>',
 953                 'desc' => '',
 954                 'type' => 'header'
 955             ),
 956             array(
 957                 'id'   => 'edd_reviews_enable_breakdown',
 958                 'name' => __( 'Enable review breakdown', 'edd-reviews' ),
 959                 'desc' => __( 'This will show how many people have rated the download for each star rating.', 'edd-reviews' ),
 960                 'type' => 'checkbox',
 961                 'size' => 'regular'
 962             ),
 963             array(
 964                 'id'   => 'edd_reviews_disable_multiple_reviews',
 965                 'name' => __( 'Disable multiple reviews by same author', 'edd-reviews' ),
 966                 'desc' => __( 'This will disallow authors to post multiple reviews on the same download', 'edd-reviews' ),
 967                 'type' => 'checkbox',
 968                 'size' => 'regular'
 969             ),
 970         );
 971 
 972         return array_merge( $settings, $new );
 973     }
 974 
 975     /**
 976      * Register Misc Settings
 977      *
 978      * @since 1.0
 979      * @access public
 980      *
 981      * @param array $settings Existing registered settings
 982      * @param array New settings
 983      * @return array Merged array with new settings added
 984      */
 985     public function styles_settings( $settings ) {
 986         $new = array(
 987             array(
 988                 'id'   => 'edd_reviews_styling_options',
 989                 'name' => '<strong>' . __( 'Reviews', 'edd-reviews' ) . '</strong>',
 990                 'desc' => '',
 991                 'type' => 'header'
 992             ),
 993             array(
 994                 'id'   => 'edd_reviews_disable_css',
 995                 'name' => __( 'Disable EDD Reviews CSS', 'edd-reviews' ),
 996                 'desc' => __( 'Check this to disable styling for the reviews provided by the EDD Reviews plugin', 'edd-reviews' ),
 997                 'type' => 'checkbox',
 998                 'size' => 'regular'
 999             )
1000         );
1001 
1002         return array_merge( $settings, $new );
1003     }
1004 
1005     /**
1006      * Adds "View Reviews" Link to Admin Bar
1007      *
1008      * @since 1.0
1009      * @access public
1010      * @global object $wp_admin_bar Used to add nodes to the WordPress Admin Bar
1011      * @global object $post Used to access the post data
1012      * @return void
1013      */
1014     public function admin_bar_menu() {
1015         global $wp_admin_bar, $post;
1016 
1017         if ( is_admin() && current_user_can( 'moderate_comments' ) ) {
1018             $current_screen = get_current_screen();
1019 
1020             if ( 'post' == $current_screen->base && 'add' != $current_screen->action && ( $post_type_object = get_post_type_object( $post->post_type ) ) && 'download' == $post->post_type && current_user_can( $post_type_object->cap->read_post, $post->ID ) && ( $post_type_object->public ) && ( $post_type_object->show_in_admin_bar ) && current_user_can( 'moderate_comments' ) ) {
1021                 if ( wp_count_comments( $post->ID )->total_comments > 0 ) {
1022                     $wp_admin_bar->add_node( array(
1023                         'id' => 'edd-view-reviews',
1024                         'title' => __( 'View Reviews', 'edd-reviews' ) . '<span class="edd-review-count-wrap"><span class="edd-review-count">' . wp_count_comments( $post->ID )->total_comments . '</span></span>',
1025                         'href' => admin_url( 'edit-comments.php?p=' . $post->ID )
1026                     ) );
1027                 }
1028             }
1029         } elseif ( is_singular( 'download' ) && current_user_can( 'moderate_comments' ) ) {
1030             if ( wp_count_comments( $post->ID )->total_comments > 0 ) {
1031                 $wp_admin_bar->add_node( array(
1032                     'id' => 'edd-view-reviews',
1033                     'title' => __( 'View Reviews', 'edd-reviews' ) . '<span class="edd-review-count-wrap"><span class="edd-review-count">' . wp_count_comments( $post->ID )->total_comments . '</span></span>',
1034                     'href' => admin_url( 'edit-comments.php?p=' . $post->ID )
1035                 ) );
1036             }
1037         }
1038     }
1039 
1040     /**
1041      * Handles the displaying of any notices in the admin area
1042      *
1043      * @since 1.0
1044      * @access public
1045      * @return void
1046      */
1047     public function admin_notices() {
1048         echo '<div class="error"><p>' . sprintf( __( 'You must install %sEasy Digital Downloads%s for the Reviews Add-On to work.', 'edd-reviews' ), '<a href="http://easydigitaldownloads.com" title="Easy Digital Downloads">', '</a>' ) . '</p></div>';
1049     }
1050 
1051     /**
1052      * Count the number of reviews from the database
1053      *
1054      * @since 1.0
1055      * @access public
1056      * @global object $wpdb Used to query the database using the WordPress
1057      *   Database API
1058      * @global object $post Used to access the post data
1059      * @return string $count Number of reviews
1060      */
1061     public function count_reviews() {
1062         global $wpdb, $post;
1063 
1064         $count = $wpdb->get_var(
1065             $wpdb->prepare(
1066                 "
1067                 SELECT COUNT(meta_value)
1068                 FROM {$wpdb->commentmeta}
1069                 LEFT JOIN {$wpdb->comments} ON {$wpdb->commentmeta}.comment_id = {$wpdb->comments}.comment_ID
1070                 WHERE meta_key = 'edd_rating'
1071                 AND comment_post_ID = %d
1072                 AND comment_approved = '1'
1073                 AND meta_value > 0
1074                 ",
1075                 $post->ID
1076             )
1077         );
1078 
1079         return $count;
1080     }
1081 
1082     /**
1083      * Count the number of ratings from the database
1084      *
1085      * @since 1.0
1086      * @access public
1087      * @global object $wpdb Used to query the database using the WordPress
1088      *   Database API
1089      * @global object $post Used to access the post data
1090      * @return string $count Number of reviews
1091      */
1092     public function count_ratings() {
1093         global $wpdb, $post;
1094 
1095         $count = $wpdb->get_var(
1096             $wpdb->prepare(
1097                 "
1098                 SELECT SUM(meta_value)
1099                 FROM {$wpdb->commentmeta}
1100                 LEFT JOIN {$wpdb->comments} ON {$wpdb->commentmeta}.comment_id = {$wpdb->comments}.comment_ID
1101                 WHERE meta_key = 'edd_rating'
1102                 AND comment_post_ID = %d
1103                 AND comment_approved = '1'
1104                 ",
1105                 $post->ID
1106             )
1107         );
1108 
1109         return $count;
1110     }
1111 
1112     /**
1113      * Gets the number of the reviews by a rating
1114      *
1115      * @since 1.0
1116      * @access public
1117      * @global object $wpdb Used to query the database using the WordPress
1118      *   Database API
1119      * @param int $rating Rating (1 - 5)
1120      * @return int $number Number of reviews
1121      */
1122     public function get_review_count_by_rating( $rating ) {
1123         global $wpdb, $post;
1124 
1125         $rating = (int) $rating;
1126 
1127         if ( $rating < 1 && $rating > 5 )
1128             return;
1129 
1130         $count = $wpdb->get_var(
1131             $wpdb->prepare(
1132                 "
1133                 SELECT COUNT(meta_value)
1134                 FROM {$wpdb->commentmeta}
1135                 LEFT JOIN {$wpdb->comments} ON {$wpdb->commentmeta}.comment_id = {$wpdb->comments}.comment_ID
1136                 WHERE meta_key = 'edd_rating'
1137                 AND meta_value = {$rating}
1138                 AND comment_approved = '1'
1139                 AND meta_value > 0
1140                 AND {$wpdb->comments}.comment_post_ID = %s
1141                 ",
1142                 $post->ID
1143             )
1144         );
1145 
1146         return $count;
1147     }
1148 
1149     /**
1150      * Build Reviews (comments) title
1151      *
1152      * @since 1.0
1153      * @access public
1154      * @global object $post Used to access the post data
1155      *
1156      * @uses EDD_Reviews::count_reviews()
1157      *
1158      * @param int $average Average ratings for reviews
1159      * @return void
1160      */
1161     public function reviews_title( $average = null ) {
1162         global $post;
1163 
1164         if ( $average ) :
1165 
1166         do_action( 'edd_reviews_title_before' );
1167         ?>
1168         <div itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
1169             <span itemprop="ratingValue" style="display:none"><?php echo $average; ?></span>
1170             <h2 class="comments-title" id="comments-title"><?php echo sprintf( apply_filters( 'edd_reviews_reviews_title', __( '%s Reviews for %s', 'edd-reviews' ) ), '<span itemprop="reviewCount" class="edd-review-count">' . $this->count_reviews() . '</span>', get_the_title( $post->ID ) ); ?></h2>
1171         </div>
1172         <?php
1173         else :
1174         ?>
1175         <h2 class="comments-title" id="comments-title"><?php apply_filters( 'edd_reviews_review_title_default',  _e( 'Reviews', 'edd-reviews' ) ); ?></h2>
1176         <?php
1177         do_action( 'edd_reviews_title_after' );
1178         endif;
1179     }
1180 
1181     /**
1182      * Checks if the reviewer has purchased the download
1183      *
1184      * @since 1.0
1185      * @access public
1186      * @global object $post Used to access the post data
1187      * @return bool Whether reviews has purchased download or not
1188      */
1189     public function reviewer_has_purchased_download() {
1190         global $post;
1191 
1192         $user_email = wp_get_current_commenter();
1193         $user_email = $user_email['comment_author_email'];
1194 
1195         if ( edd_has_user_purchased( $user_email, $post->ID ) )
1196             return true;
1197 
1198         return false;
1199     }
1200 
1201     /**
1202      * Add Classes to the Reviews
1203      *
1204      * @since 1.0
1205      * @access public
1206      * @param array $classes Comment classes
1207      * @return array $classes Comment (reviews) classes with 'review' added
1208      */
1209     public function review_classes( $classes ) {
1210         $classes[] = 'review';
1211 
1212         return $classes;
1213     }
1214 
1215     /**
1216      * Template for displaying reviews
1217      *
1218      * This function just executes the hooks to display the review.
1219      *
1220      * @since 1.0
1221      * @access public
1222      *
1223      * @param object $comment
1224      * @param array  $args
1225      * @param string $depth
1226      * @return void
1227      */
1228     public function review( $comment, $args, $depth ) {
1229         $GLOBALS['comment'] = $comment;
1230         extract( $args, EXTR_SKIP );
1231 
1232         do_action( 'edd_reviews_review_before' );
1233         do_action( 'edd_reviews_review_display', $comment, $args, $depth );
1234         do_action( 'edd_reviews_review_after' );
1235     }
1236 
1237     /**
1238      * Renders each review
1239      *
1240      * NOTE: Closing tags have been left out deliberately because WordPress will
1241      * supply the closing tag automatically. See here:
1242      * http://codex.wordpress.org/Function_Reference/wp_list_comments
1243      *
1244      * @since 1.0
1245      * @access public
1246      *
1247      * @uses EDD_Reviews::voting_info()
1248      *
1249      * @param object $comment
1250      * @param array $args
1251      * @param string $depth
1252      * @return void
1253      */
1254     public function render_review( $comment, $args, $depth ) {
1255         ob_start();
1256         $rating = get_comment_meta( get_comment_ID(), 'edd_rating', true );
1257         ?>
1258 
1259         <?php if ( $depth == 1 ) : ?>
1260         <li itemprop="review" itemscope itemtype="http://schema.org/Review" <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
1261             <article id="comment-<?php comment_ID(); ?>" class="comment comment_container edd_reviews_container">
1262                 <?php if ( get_option( 'show_avatars' ) ) echo get_avatar( $GLOBALS['comment'], 60 ); ?>
1263 
1264                 <div class="edd-review-meta">
1265                     <span itemprop="name" class="review-title-text"><?php echo get_comment_meta( get_comment_ID(), 'edd_review_title', true ); ?> <?php if ( current_user_can( 'edit_others_posts' ) ) : ?><span class="edit-link"><a class="comment-edit-link" href="<?php echo admin_url( 'comment.php?action=editcomment&c=' . get_comment_ID() ); ?>"><?php _e( 'Edit Review', 'edd-reviews' ); ?></a></span><?php endif; ?> </span>
1266 
1267                     <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="star-rating">
1268                         <div class="edd_reviews_rating_box" role="img" aria-label="<?php echo $rating . ' ' . __( 'stars', 'edd-reviews' ); ?>">
1269                             <div class="edd_star_rating" style="width: <?php echo ( 19 * $rating ); ?>px"></div>
1270                         </div>
1271                         <div style="display:none" itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
1272                             <meta itemprop="worstRating" content="1" />
1273                             <span itemprop="ratingValue"><?php echo $rating; ?></span>
1274                             <span itemprop="bestRating">5</span>
1275                         </div>
1276                     </div>
1277 
1278                     <?php if ( 0 == $GLOBALS['comment']->comment_approved ) : ?>
1279                         <p class="moderation"><em><?php apply_filters( 'edd_reviews_moderation_message', _e( 'Your review is awaiting moderation.', 'edd-reviews' ) ); ?></em></p>
1280                     <?php else : ?>
1281                         <p class="edd-reviews-author-meta"><?php _e( 'by', 'edd-reviews' ); ?> <span itemprop="author"><?php comment_author(); ?></span>, <time itemprop="datePublished" time datetime="<?php echo get_comment_date( 'c' ); ?>"><?php echo get_comment_date( get_option( 'date_format' ) ); ?></time>:</p>
1282                     <?php endif; ?>
1283                 </div>
1284                 <div class="clear"></div>
1285 
1286                 <div class="comment-text review-text">
1287                     <div itemprop="description" class="description edd-review-content comment-content">
1288                         <?php comment_text(); ?>
1289                     </div>
1290                 </div>
1291 
1292                 <?php if ( ( isset( $_GET['edd_reviews_vote'] ) && $_GET['edd_reviews_vote'] == 'success' && isset( $_GET['edd_c'] ) && is_numeric( $_GET['edd_c'] ) && $_GET['edd_c'] == get_comment_ID() ) || EDD()->session->get( 'wordpress_edd_reviews_voted_' . get_comment_ID() ) ) : ?>
1293                 <div class="edd-review-vote edd-yellowfade">
1294                     <p style="margin:0;padding:0;"><?php echo apply_filters( 'edd_reviews_thank_you_for_voting_message', __( 'Thank you for your feedback.', 'edd-reviews' ) ); ?></p>
1295                     <?php $this->voting_info(); ?>
1296                 </div>
1297                 <?php else: ?>
1298                 <div class="edd-review-vote">
1299                     <?php do_action( 'edd_reviews_voting_box_before' ); ?>
1300                     <?php $this->voting_info(); ?>
1301                     <p><?php echo apply_filters( 'edd_reviews_voting_intro_text', _e( 'Help other customers find the most helpful reviews', 'edd-reviews' ) ); ?></p>
1302                     <p>
1303                         <?php echo apply_filters( 'edd_reviews_review_helpful_text', __( 'Did you find this review helpful?', 'edd-reviews' ) ); ?>
1304                         <span class="edd-reviews-voting-buttons">
1305                             <a class="vote-yes" data-edd-reviews-comment-id="<?php echo get_comment_ID(); ?>" data-edd-reviews-vote="yes" rel="nofollow" href="<?php echo add_query_arg( array( 'edd_review_vote' => 'yes', 'edd_c' => get_comment_ID() ) ); ?>"><?php _e( 'Yes', 'edd-reviews' ); ?></a>
1306                             <a class="vote-no" data-edd-reviews-comment-id="<?php echo get_comment_ID(); ?>" data-edd-reviews-vote="no" rel="nofollow" href="<?php echo add_query_arg( array( 'edd_review_vote' => 'no', 'edd_c' => get_comment_ID() ) ); ?>"><?php _e( 'No', 'edd-reviews' ); ?></a>
1307                         </span>
1308                     </p>
1309                     <?php do_action( 'edd_reviews_voting_box_after' ); ?>
1310                 </div>
1311                 <?php endif; ?>
1312             </article>
1313             <div class="clear"></div>
1314         <?php elseif ( $depth > 1 ) : ?>
1315         <li itemprop="review" itemscope itemtype="http://schema.org/Review" <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
1316             <article id="comment-<?php comment_ID(); ?>" class="comment comment_container edd_reviews_container">
1317                 <?php if ( get_option( 'show_avatars' ) ) echo get_avatar( $GLOBALS['comment'], 44 ); ?>
1318 
1319                 <?php if ( 0 == $GLOBALS['comment']->comment_approved ) : ?>
1320                 <p class="moderation"><em><?php apply_filters( 'edd_reviews_moderation_message', _e( 'Your review is awaiting moderation.', 'edd-reviews' ) ); ?></em></p>
1321                 <?php else : ?>
1322                 <p class="edd-reviews-comment-author"><strong><?php comment_author(); ?></strong></p>
1323                 <p class="edd-reviews-date-published"><time itemprop="datePublished" time datetime="<?php echo get_comment_date( 'c' ); ?>"><?php echo get_comment_date( get_option( 'date_format' ) ); ?></time></p>
1324                 <?php endif; ?>
1325                 <div class="clear"></div>
1326 
1327                 <div class="comment-text review-text">
1328                     <div itemprop="description" class="description edd-review-content comment-content">
1329                         <?php comment_text(); ?>
1330                     </div>
1331                 </div>
1332             </article>
1333             <div class="clear"></div>
1334         <?php
1335         endif;
1336         echo ob_get_clean();
1337     }
1338 
1339     /**
1340      * Display Voting Info
1341      *
1342      * Example output: 2 of 8 people found this review helpful
1343      *
1344      * @since 1.0
1345      * @access public
1346      * @global object $GLOBALS['comment'] Current comment
1347      * @return void
1348      */
1349     public function voting_info() {
1350         $comment = $GLOBALS['comment'];
1351 
1352         $votes = array(
1353             'yes' => get_comment_meta( $comment->comment_ID, 'edd_review_vote_yes', true ),
1354             'no'  => get_comment_meta( $comment->comment_ID, 'edd_review_vote_no',  true ),
1355         );
1356 
1357         if ( ! empty( $votes['yes'] ) && $votes['yes'] > 1 ) {
1358             $total = $votes['yes'] + $votes['no'];
1359 
1360             echo '<p class="edd-review-voting-feedback">' . sprintf( __( '%s of %s people found this review helpful.', 'edd-reviews' ), $votes['yes'], $total ) . '</p>';
1361         }
1362     }
1363 
1364     /**
1365      * Conditional whether or not to display review breakdown
1366      *
1367      * @since 1.0
1368      * @access public
1369      * @global array $edd_options Used to access the EDD Options
1370      *
1371      * @uses EDD_Reviews::review_breakdown()
1372      *
1373      * @return void
1374      */
1375     public function maybe_show_review_breakdown() {
1376         global $edd_options;
1377 
1378         if ( isset( $edd_options['edd_reviews_enable_breakdown'] ) && 1 == $edd_options['edd_reviews_enable_breakdown'] ) {
1379             $this->review_breakdown();
1380         }
1381     }
1382 
1383     /**
1384      * Reviews Breakdown
1385      *
1386      * Shows a breakdown of all the reviews and the number of people that given
1387      * each rating for each download
1388      *
1389      * Example: 8 people gave a 5 star rating; 10 people have a 2 star rating
1390      *
1391      * @since 1.0
1392      * @access public
1393      *
1394      * @uses EDD_Reviews::display_total_reviews_count()
1395      * @uses EDD_Reviews::display_review_counts()
1396      *
1397      * @return void
1398      */
1399     public function review_breakdown() {
1400         echo '<div class="edd_reviews_breakdown">';
1401         $this->display_total_reviews_count();
1402         $this->display_review_counts();
1403         echo '</div>';
1404     }
1405 
1406     /**
1407      * Displays the total reviews count
1408      *
1409      * @since 1.0
1410      * @access public
1411      *
1412      * @uses EDD_Reviews::count_reviews()
1413      *
1414      * @return void
1415      */
1416     public function display_total_reviews_count() {
1417         echo '<div class="edd-reviews-total-count">' . $this->count_reviews() . ' ' . _n( 'review', 'reviews', $this->count_reviews(), 'edd-reviews' ) . '</div>';
1418     }
1419 
1420     /**
1421      * Displays reviews count for each rating by looping through 1 - 5
1422      *
1423      * @since 1.0
1424      * @access public
1425      *
1426      * @uses EDD_Reviews::get_review_count_by_rating()
1427      * @uses EDD_Reviews::count_reviews()
1428      *
1429      * @return void
1430      */
1431     public function display_review_counts() {
1432         $output = '';
1433 
1434         for ( $i = 5; $i >= 1; $i-- ) {
1435             $number = $this->get_review_count_by_rating( $i );
1436 
1437             $all = $this->count_reviews();
1438 
1439             ( $all == 0 ) ? $all = 1 : $all;
1440 
1441             $output .= '<div class="edd-counter-container edd-counter-container-'. $i .'">';
1442             $output .= '<div class="edd-counter-label">'. $i . ' ' . _n( 'star', 'stars', $i, 'edd-reviews' ) . '</div>';
1443             $output .= '<div class="edd-counter-back"><span class="edd-counter-front" style="width: '. number_format( ( $number / $all ) * 100, 1 ) .'px"></span></div>';
1444             $output .= '<div class="edd-review-count">'. $number .'</div>';
1445             $output .= '</div>';
1446         }
1447 
1448         echo $output;
1449     }
1450 
1451     /**
1452      * Process Vote from Review
1453      *
1454      * This function is called if a JavaScript isn't enabled
1455      *
1456      * @since 1.0
1457      * @access public
1458      * @return void
1459      */
1460     public function process_vote() {
1461         if ( isset( $_GET['edd_review_vote'] ) && isset( $_GET['edd_c'] ) && is_numeric( $_GET['edd_c'] ) ) {
1462             $this->add_comment_vote_meta( $_GET['edd_c'], $_GET['edd_review_vote'] );
1463 
1464             // Remove the query arguments to prevent multiple votes
1465             $url = remove_query_arg( array( 'edd_c', 'edd_review_vote' ) );
1466             wp_redirect( $url . '?edd_reviews_vote=success&edd_c=' . $_GET['edd_c'] .'#comment-' . intval( $_GET['edd_c'] ) );
1467             die();
1468         }
1469     }
1470 
1471     /**
1472      * Add Comment Vote
1473      *
1474      * @since 1.0
1475      * @access public
1476      *
1477      * @param int $comment_id Comment ID
1478      * @param string $vote Whether the vote was yes or no
1479      * @return void
1480      */
1481     public function add_comment_vote_meta( $comment_id, $vote ) {
1482         if ( 'yes' == $vote ) {
1483             $value = get_comment_meta( $comment_id, 'edd_review_vote_yes', true );
1484 
1485             if ( ! empty( $value ) && ! $value > 0 ) {
1486                 $number = 1;
1487                 add_comment_meta( $comment_id, 'edd_review_vote_yes', $number );
1488             } else {
1489                 $number = $value + 1;
1490                 update_comment_meta( $comment_id, 'edd_review_vote_yes', $number );
1491             }
1492 
1493         } elseif ( 'no' == $vote ) {
1494             $value = get_comment_meta( $comment_id, 'edd_review_vote_no', true );
1495 
1496             if ( ! empty( $value ) && ! $value > 0 ) {
1497                 $number = 1;
1498                 add_comment_meta( $comment_id, 'edd_review_vote_yes', $number );
1499             } else {
1500                 $number = $value + 1;
1501                 update_comment_meta( $comment_id, 'edd_review_vote_no', $number );
1502             }
1503 
1504             add_comment_meta( $comment_id, 'edd_review_vote_no', $number );
1505         }
1506     }
1507 
1508     /**
1509      * Checks whether an AJAX request has been sent
1510      *
1511      * @since 1.0
1512      * @access public
1513      * @return bool Whether or not AJAX $_GET header has been passed
1514      */
1515     public function is_ajax_request() {
1516         return (bool) ( isset( $_POST['edd_reviews_ajax'] ) && ! empty( $_REQUEST['action'] ) );
1517     }
1518 
1519     /**
1520      * Process Voting for the Reviews via AJAX
1521      *
1522      * Processes the voting button appended to the bottom of each review by adding
1523      * or updating the comment meta via AJAX.
1524      *
1525      * @since 1.0
1526      * @access public
1527      *
1528      * @uses EDD_Reviews::is_ajax_request()
1529      *
1530      * @return mixed returns if AJAX check fails
1531      */
1532     public function process_ajax_vote() {
1533         // Bail if an AJAX request isn't sent
1534         if ( ! $this->is_ajax_request() )
1535             return;
1536 
1537         check_ajax_referer( 'edd_reviews_voting_nonce', 'security', true );
1538 
1539         if ( ! isset( $_POST['review_vote'] ) )
1540             return;
1541 
1542         if ( isset( $_POST['review_vote'] ) && isset( $_POST['comment_id'] ) && is_numeric( $_POST['comment_id'] ) ) {
1543             $this->add_comment_vote_meta( $_POST['comment_id'], $_POST['review_vote'] );
1544 
1545             EDD()->session->set( 'wordpress_edd_reviews_voted_' . $_POST['comment_id'], 'yes' );
1546 
1547             echo 'success';
1548         } else {
1549             echo 'fail';
1550         }
1551 
1552         die();
1553     }
1554 
1555     /**
1556      * Register Dashboard Widgets
1557      *
1558      * @since 1.0
1559      * @access public
1560      * @return void
1561      */
1562     public function dashboard_widgets() {
1563         if ( is_blog_admin() && current_user_can( 'moderate_comments' ) ) {
1564             $recent_reviews_title = apply_filters( 'edd_reviews_recent_reviews_dashboard_widget_title', __( 'Easy Digital Downloads Recent Reviews', 'edd-reviews' ) );
1565             wp_add_dashboard_widget(
1566                 'edd_reviews_dashboard_recent_reviews',
1567                 $recent_reviews_title,
1568                 array( 'EDD_Reviews', 'render_dashboard_widget' )
1569             );
1570         }
1571     }
1572 
1573     /**
1574      * Render the Dashboard Widget
1575      *
1576      * @since 1.0
1577      * @access public
1578      * @global object $wpdb Used to query the database using the WordPress Database API
1579      * @return void
1580      */
1581     public function render_dashboard_widget() {
1582         global $wpdb;
1583 
1584         $reviews = $wpdb->get_results(
1585             "
1586             SELECT *, SUBSTRING( comment_content, 1, 100 ) AS excerpt
1587             FROM {$wpdb->comments}
1588             LEFT JOIN $wpdb->posts ON {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID
1589             WHERE comment_approved = '1'
1590             AND post_password = ''
1591             AND post_type = 'download'
1592             AND comment_type = ''
1593             ORDER BY comment_date_gmt DESC
1594             LIMIT 5
1595             "
1596         );
1597 
1598         if ( $reviews ) {
1599             echo '<div id="edd-reviews-list">';
1600 
1601             foreach ( $reviews as $review ) {
1602                 $output = '<div id="review-'. $review->ID .'">';
1603 
1604                 $output .= ( get_option( 'show_avatars' ) ) ? get_avatar( $review->comment_author_email, 50 ) : '';
1605 
1606                 $output .= '<div class="edd-dashboard-review-wrap">';
1607 
1608                 $rating = get_comment_meta( $review->comment_ID, 'edd_rating', true );
1609 
1610                 $output .= '<h4 class="meta"><a href="' . get_permalink( $review->ID ) . '#comment-' . absint( $review->comment_ID ) .'">' . esc_html__( get_comment_meta( $review->comment_ID, 'edd_review_title', true ) ) . '</a></h4>';
1611                 $output .= '<div class="edd_reviews_rating_box"><div class="edd_star_rating" style="width: ' . 19 * $rating  . 'px"></div></div>';
1612                 $output .= '<p>' . __( 'By', 'edd-reviews' ) . ' ' . esc_html( $review->comment_author ) . ', ' . get_comment_date( get_option( 'date_format()' ), $review->comment_ID ) . '</p>';
1613                 $output .= '<blockquote>' . wp_kses_data( $review->excerpt ) . ' ...</blockquote></div>';
1614                 $output .= '</div>';
1615 
1616                 echo $output;
1617             }
1618 
1619             echo '</div>';
1620         } else {
1621             echo '<p>' . __( 'There are no reviews yet.', 'edd-reviews' ) . '</p>';
1622         }
1623     }
1624 
1625     /**
1626      * Add the Meta Boxes
1627      *
1628      * @since 1.0
1629      * @access public
1630      * @return void
1631      */
1632     public function add_meta_boxes() {
1633         add_meta_box( 'edd_reviews_review_meta_box', __( 'Review Information', 'edd-reviews' ), array( $this, 'review_meta_box' ), 'comment', 'normal', 'high' );
1634     }
1635 
1636     /**
1637      * Render the Review Meta Box
1638      *
1639      * Outputs the Review Information meta box on the Edit Comment screen. This
1640      * meta box displays the review title and the star rating.  It also allows
1641      * for it to be edited.
1642      *
1643      * @since 1.0
1644      * @access public
1645      * @param object $comment Comment information
1646      * @return void
1647      */
1648     public function review_meta_box( $comment ) {
1649         ?>
1650         <table class="form-table editcomment">
1651             <tbody>
1652                 <tr valign="top">
1653                     <td class="first"><?php _e( 'Review Title:', 'edd-reviews' ); ?></td>
1654                     <td><input type="text" class="widefat" id="edd_reviews_review_title" name="edd_reviews_review_title" value="<?php echo get_comment_meta( $comment->comment_ID, 'edd_review_title', true ); ?>" /></td>
1655                 </tr>
1656                 <tr valign="top">
1657                     <td class="first"><?php _e( 'Rating:', 'edd-reviews' ); ?></td>
1658                     <td><input type="text" class="widefat" id="edd_reviews_rating" name="edd_reviews_rating" value="<?php echo get_comment_meta( $comment->comment_ID, 'edd_rating', true ); ?>" /></td>
1659                 </tr>
1660             </tbody>
1661         </table>
1662         <?php
1663     }
1664 
1665     /**
1666      * Save the Meta Data from the Meta Box on the Edit Comment Screen
1667      *
1668      * @since 1.0
1669      * @access public
1670      * @param int $comment_id Comment ID
1671      * @return void
1672      */
1673     public function update_review_meta( $comment_id ) {
1674         $review_title = sanitize_text_field( $_POST['edd_reviews_review_title'] );
1675         $rating       = intval( $_POST['edd_reviews_rating'] );
1676 
1677         if ( empty ( $review_title ) ) {
1678             wp_die( sprintf( __( '%sError%s: Please add a review title.', 'edd-reviews' ), '<strong>', '</strong>' ), __( 'Error', 'edd-reviews' ), array( 'back_link' => true ) );
1679         }
1680 
1681         if ( ! ( $rating > 0 && $rating <= 5 ) ) {
1682             wp_die( sprintf( __( '%sError%s: Please add a valid rating between 1 and 5.', 'edd-reviews' ), '<strong>', '</strong>' ), __( 'Error', 'edd-reviews' ), array( 'back_link' => true ) );
1683         }
1684 
1685         update_comment_meta( $comment_id, 'edd_review_title', $review_title );
1686         update_comment_meta( $comment_id, 'edd_rating',       $rating       );
1687     }
1688 
1689     /**
1690      * Register API Query Mode
1691      *
1692      * @since 1.0
1693      * @access public
1694      * @param array $modes Whitelisted query modes
1695      * @return array $modes Updated list of query modes
1696      */
1697     public function register_api_mode( $modes ) {
1698         $modes[] = 'reviews';
1699         return $modes;
1700     }
1701 
1702     /**
1703      * Add 'review_id' Query Var into WordPress Whitelisted Query Vars
1704      *
1705      * @since 1.0
1706      * @access public
1707      * @param array $vars Array of WordPress allowed query vars
1708      * @return array $vars Updated array of WordPress query vars to allow
1709      *  Reviews to integrate with the EDD API
1710      */
1711     public function query_vars( $vars ) {
1712         $vars[] = 'review_id';
1713         return $vars;
1714     }
1715 
1716     /**
1717      * Processes the Data Outputted when an API Call for Reviews is Triggered
1718      *
1719      * @since 1.0
1720      * @access public
1721      * @global object $wpdb Used to query the database using the WordPress
1722      *   Database API
1723      * @global object $wp_query Used to access the query vars
1724      *
1725      * @param array $data Array to hold the output
1726      * @param array $query_mode Query mode (i.e. reviews)
1727      * @param object $api_object EDD_API Object
1728      *
1729      * @return array $data All the data for when the API call for reviews is fired
1730      */
1731     public function api_output( $data, $query_mode, $api_object ) {
1732         global $wpdb, $wp_query;
1733 
1734         // Bail if the query mode isn't reviews
1735         if ( 'reviews' !== $query_mode )
1736             return $data;
1737 
1738         // Get the review_id query var
1739         $review_id = isset( $wp_query->query_vars['review_id'] ) ? $wp_query->query_vars['review_id'] : null;
1740 
1741         if ( $review_id ) {
1742             // Get the review from the database
1743             $review = $wpdb->get_results(
1744                 $wpdb->prepare(
1745                     "
1746                     SELECT *
1747                     FROM {$wpdb->comments}
1748                     INNER JOIN {$wpdb->posts} ON {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID
1749                     WHERE comment_ID = '%d'
1750                     LIMIT 1
1751                     ",
1752                     $review_id
1753                 )
1754             );
1755 
1756             if ( $review ) :
1757                 $data['reviews']['id']             = $review[0]->comment_ID;
1758                 $data['reviews']['title']          = get_comment_meta( $review[0]->comment_ID, 'edd_review_title', true );
1759                 $data['reviews']['download_id']    = $review[0]->comment_post_ID;
1760                 $data['reviews']['download_title'] = $review[0]->post_title;
1761                 $data['reviews']['rating']         = get_comment_meta( $review[0]->comment_ID, 'edd_rating', true );
1762                 $data['reviews']['author']         = $review[0]->comment_author;
1763                 $data['reviews']['email']          = $review[0]->comment_author_email;
1764                 $data['reviews']['IP']             = $review[0]->comment_author_IP;
1765                 $data['reviews']['date']           = $review[0]->comment_date;
1766                 $data['reviews']['date_gmt']       = $review[0]->comment_date_gmt;
1767                 $data['reviews']['content']        = $review[0]->comment_content;
1768                 $data['reviews']['status']         = $review[0]->comment_approved;
1769                 $data['reviews']['user_id']        = $review[0]->user_id;
1770             else :
1771                 $error['error'] = sprintf( __( 'Review %s not found!', 'edd-reviews' ), $review_id );
1772                 return $error;
1773             endif;
1774         } else {
1775             // Get total reviews count from the database
1776             $total_reviews = $wpdb->get_var(
1777                 $wpdb->prepare(
1778                     "
1779                     SELECT COUNT(meta_value)
1780                     FROM {$wpdb->commentmeta}
1781                     LEFT JOIN {$wpdb->comments} ON {$wpdb->commentmeta}.comment_id = {$wpdb->comments}.comment_ID
1782                     WHERE meta_key = 'edd_rating'
1783                     AND comment_approved = '1'
1784                     AND meta_value > 0
1785                     "
1786                 )
1787             );
1788 
1789             /** Total Reviews */
1790             $data['reviews']['total'] = $total_reviews;
1791 
1792             /** Most Recent Review */
1793             $most_recent_review = get_comments( array( 'post_type' => 'download', 'number' => 1 ) );
1794 
1795             $data['reviews']['most_recent']['id']             = $most_recent_review[0]->comment_ID;
1796             $data['reviews']['most_recent']['title']          = get_comment_meta( $most_recent_review[0]->comment_ID, 'edd_review_title', true );
1797             $data['reviews']['most_recent']['download_id']    = $most_recent_review[0]->comment_post_ID;
1798             $data['reviews']['most_recent']['download_title'] = get_the_title( $most_recent_review[0]->comment_post_ID );
1799             $data['reviews']['most_recent']['rating']         = get_comment_meta( $most_recent_review[0]->comment_ID, 'edd_rating', true );
1800             $data['reviews']['most_recent']['author']         = $most_recent_review[0]->comment_author;
1801             $data['reviews']['most_recent']['email']          = $most_recent_review[0]->comment_author_email;
1802             $data['reviews']['most_recent']['IP']             = $most_recent_review[0]->comment_author_IP;
1803             $data['reviews']['most_recent']['date']           = $most_recent_review[0]->comment_date;
1804             $data['reviews']['most_recent']['date_gmt']       = $most_recent_review[0]->comment_date_gmt;
1805             $data['reviews']['most_recent']['content']        = $most_recent_review[0]->comment_content;
1806             $data['reviews']['most_recent']['status']         = $most_recent_review[0]->comment_approved;
1807             $data['reviews']['most_recent']['user_id']        = $most_recent_review[0]->user_id;
1808         }
1809 
1810         // Allow extensions to add to the data outpt
1811         $data = apply_filters( 'edd_reviews_api_output_data', $data );
1812 
1813         return $data;
1814     }
1815 
1816     /**
1817      * Is the User Allowed to See TinyMCE?
1818      *
1819      * @since 1.0
1820      * @access public
1821      * @return bool Whether the user can see TinyMCE or not
1822      */
1823     public function user_can_see_tinymce() {
1824         return ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) );
1825     }
1826 
1827     /**
1828      * Add TinyMCE Button
1829      *
1830      * Adds a button to the TinyMCE editor to easily embed reviews into posts
1831      * and pages
1832      *
1833      * @since 1.0
1834      * @access public
1835      *
1836      * @uses EDD_Reviews::user_can_see_tinymce()
1837      *
1838      * @return void
1839      */
1840     public function tinymce_button() {
1841         if ( $this->user_can_see_tinymce() && 'true' == get_user_option( 'rich_editing' ) ) {
1842             add_filter( 'mce_external_plugins', array( $this, 'add_plugin'      ) );
1843             add_filter( 'mce_buttons',          array( $this, 'register_button' ) );
1844         }
1845     }
1846 
1847     /**
1848      * Register TinyMCE Plugin
1849      *
1850      * @since 1.0
1851      * @access public
1852      * @param array $plugin_array Array of TinyMCE Plugins
1853      * @return array $plugin_array Array of TinyMCE Plugins
1854      */
1855     public function add_plugin( $plugin_array ) {
1856         $plugin_array['edd_reviews'] = $this->assets_url . 'js/edd-reviews-admin.js';
1857         return $plugin_array;
1858     }
1859 
1860     /**
1861      * Register TinyMCE Button
1862      *
1863      * @since 1.0
1864      * @access public
1865      * @param array $buttons Array of TinyMCE Button
1866      * @return array $buttons Array of TinyMCE Button
1867      */
1868     public function register_button( $buttons ) {
1869         array_push( $buttons, '|', 'edd_reviews' );
1870         return $buttons;
1871     }
1872 
1873     /**
1874      * Process the TinyMCE Modal Dialog
1875      *
1876      * @since 1.0
1877      * @access public
1878      * @global object $wpdb Used to query the database using the WordPress
1879      *   Database API
1880      * @return void
1881      */
1882     public function process_mce_dialog() {
1883         if ( is_user_logged_in() && isset( $_GET['edd_reviews_mce_dialog'] ) && 'true' == $_GET['edd_reviews_mce_dialog'] ) {
1884             global $wpdb;
1885 
1886             $reviews = $wpdb->get_results( $wpdb->prepare( "SELECT meta_value, comment_id FROM {$wpdb->commentmeta} WHERE meta_key = %s", 'edd_review_title' ) );
1887             ?>
1888             <!DOCTYPE html>
1889             <html <?php language_attributes(); ?>>
1890             <head>
1891                 <meta charset="utf-8" />
1892                 <title><?php _e( 'Embed Review', 'edd-reviews' ); ?></title>
1893                 <script type="text/javascript" src="<?php echo includes_url(); ?>/js/tinymce/tiny_mce_popup.js"></script>
1894                 <script type="text/javascript">
1895                 var edd_reviews_dialog = {
1896                     local_ed: 'ed',
1897 
1898                     init: function (ed) {
1899                         edd_reviews_dialog.local_ed = ed;
1900                         tinyMCEPopup.resizeToInnerSize();
1901                     },
1902 
1903                     insert: function insertButton(ed) {
1904                         tinyMCEPopup.execCommand('mceRemoveNode', false, null);
1905 
1906                         var elem     = document.getElementById('edd_reviews_shortcode_dialog'),
1907                             selected = elem.options[elem.selectedIndex].value;
1908                             output   = '';
1909 
1910                         output = '[review id="' + selected + '"]';
1911                         tinyMCEPopup.execCommand('mceReplaceContent', false, output);
1912                         tinyMCEPopup.close();
1913                     }
1914                 }
1915 
1916                 tinyMCEPopup.onInit.add(edd_reviews_dialog.init, edd_reviews_dialog);
1917                 </script>
1918             </head>
1919             <body>
1920             <?php
1921             if ( $reviews ) {
1922                 echo '<h2>' . __( 'Select a Review to Embed' ) . '</h2>';
1923                 echo '<p><select id="edd_reviews_shortcode_dialog" name="edd_reviews_shortcode_dialog">';
1924 
1925                 foreach ( $reviews as $review ) :
1926                     echo '<option value="' . $review->comment_id . '">' . esc_html( $review->meta_value ) . '</option>';
1927                 endforeach;
1928 
1929                 echo '</select></p>';
1930 
1931                 echo '<p><a href="javascript:edd_reviews_dialog.insert(edd_reviews_dialog.local_ed)" id="insert" style="display: block; line-height: 24px;">' . __( 'Embed Review', 'edd-reviews' ) . '</a></p>';
1932             } else {
1933                 echo '<h2>' . __( 'No Reviews Have Been Created Yet', 'edd-reviews' ) . '</h2>';
1934             }
1935             ?>
1936             </body>
1937             </html>
1938             <?php
1939             die();
1940         }
1941     }
1942 
1943     /**
1944      * Plugin Action Links
1945      *
1946      * This function adds a link to the plugin action links bar on the Plugins
1947      * Administrati on page to for the API documentation and to Easy Digital Downloads
1948      * Support Forum.
1949      *
1950      * @since 1.0
1951      * @access public
1952      * @param array $links Plugin Action Links
1953      * @return array $links Plugin Action Links
1954      */
1955     public function plugin_links( $links, $file ) {
1956         static $this_plugin;
1957 
1958         if ( ! $this_plugin ) {
1959             $this_plugin = $this->basename;
1960         }
1961 
1962         if ( $file == $this_plugin ) {
1963             $api_link = '<a href="' . $this->plugin_url . 'api-docs/index.html' . '">' . __( 'API Documentation', 'edd-reviews' ) . '</a>';
1964             array_unshift( $links , $api_link );
1965         }
1966 
1967         return $links;
1968     }
1969 
1970     /**
1971      * Loads the Updater
1972      *
1973      * Instantiates the Software Licensing Plugin Updater and passes the plugin
1974      * data to the class.
1975      *
1976      * @since 1.0
1977      * @access public
1978      * @return void
1979      */
1980     public function updater() {
1981         $license = new EDD_License( $this->file, 'Product Reviews', $this->version, 'Sunny Ratilal', 'edd_reviews_licensing_license_key' );
1982     }
1983 }
1984 
1985 /**
1986  * The callback for the reviews called by wp_list_comments().
1987  *
1988  * This must be placed outside of the class because wp_list_comments()
1989  * doesn't allow a function to be passed as a callback within a class,
1990  * it must be a separate function.
1991  *
1992  * @since 1.0
1993  *
1994  * @param object $comment
1995  * @param array  $args
1996  * @param string $depth
1997  * @return void
1998  */
1999 function edd_reviews_callback( $comment, $args, $depth ) {
2000     edd_reviews()->review( $comment, $args, $depth );
2001 }
2002 /**
2003  * Loads a single instance of EDD Reviews
2004  *
2005  * This follows the PHP singleton design pattern.
2006  *
2007  * Use this function like you would a global variable, except without needing
2008  * to declare the global.
2009  *
2010  * @example <?php $edd_reviews = edd_reviews(); ?>
2011  *
2012  * @since 1.0
2013  *
2014  * @see EDD_Reviews::get_instance()
2015  *
2016  * @return object Returns an instance of the EDD_Reviews class
2017  */
2018 function edd_reviews() {
2019     return EDD_Reviews::get_instance();
2020 }
2021 
2022 /**
2023  * Loads plugin after all the others have loaded and have registered their
2024  * hooks and filters
2025  */
2026 add_action( 'plugins_loaded', 'edd_reviews', apply_filters( 'edd_reviews_action_priority', 10 ) );
2027 
2028 endif;
2029 
API documentation generated by ApiGen 2.8.0