From 97d35613266e87f9b9274cf47b568ad79fe85959 Mon Sep 17 00:00:00 2001 From: BenSibley Date: Thu, 10 Aug 2017 15:11:28 -0400 Subject: [PATCH] added review request --- dnh/assets/js/main.js | 15 + dnh/handler.php | 609 ++++++++++++++++++++++++++++++ dnh/includes/helper-functions.php | 83 ++++ functions.php | 8 + inc/review.php | 412 ++++++++++++++++++++ 5 files changed, 1127 insertions(+) create mode 100755 dnh/assets/js/main.js create mode 100755 dnh/handler.php create mode 100755 dnh/includes/helper-functions.php create mode 100755 inc/review.php diff --git a/dnh/assets/js/main.js b/dnh/assets/js/main.js new file mode 100755 index 0000000..0c3db8c --- /dev/null +++ b/dnh/assets/js/main.js @@ -0,0 +1,15 @@ +jQuery(document).ready(function($) { + $( '.notice.is-dismissible' ).on('click', '.notice-dismiss', function ( event ) { + event.preventDefault(); + var $this = $(this); + if( 'undefined' == $this.parent().attr('id') ){ + return; + } + $.post( ajaxurl, { + action: 'dnh_dismiss_notice', + url: ajaxurl, + id: $this.parent().attr('id') + }); + + }); +}); \ No newline at end of file diff --git a/dnh/handler.php b/dnh/handler.php new file mode 100755 index 0000000..4b51af7 --- /dev/null +++ b/dnh/handler.php @@ -0,0 +1,609 @@ + + * + * @package Dismissible Notices Handler + * @author Julien Liabeuf + * @version 1.0 + * @license GPL-2.0+ + * @link https://julienliabeuf.com + * @copyright 2016 Julien Liabeuf + */ + +// If this file is called directly, abort. +if ( ! defined( 'WPINC' ) ) { + die; +} + +if ( ! class_exists( 'Dismissible_Notices_Handler' ) ) { + + final class Dismissible_Notices_Handler { + + /** + * @var Dismissible_Notices_Handler Holds the unique instance of the handler + * @since 1.0 + */ + private static $instance; + + /** + * Library version + * + * @since 1.0 + * @var string + */ + public $version = '1.0'; + + /** + * Required version of PHP. + * + * @since 1.0 + * @var string + */ + public $php_version_required = '5.5'; + + /** + * Minimum version of WordPress required to use the library + * + * @since 1.0 + * @var string + */ + public $wordpress_version_required = '4.2'; + + /** + * @var array Holds all our registered notices + * @since 1.0 + */ + private $notices; + + /** + * Instantiate and return the unique Dismissible_Notices_Handler object + * + * @since 1.0 + * @return object Dismissible_Notices_Handler Unique instance of the handler + */ + public static function instance() { + + if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Dismissible_Notices_Handler ) ) { + self::$instance = new Dismissible_Notices_Handler; + self::$instance->init(); + } + + return self::$instance; + + } + + /** + * Initialize the library + * + * @since 1.0 + * @return void + */ + private function init() { + + // Make sure WordPress is compatible + if ( ! self::$instance->is_wp_compatible() ) { + return; + } + + // Make sure PHP is compatible + if ( ! self::$instance->is_php_compatible() ) { + return; + } + + self::$instance->includes(); + + add_action( 'admin_notices', array( self::$instance, 'display' ) ); + add_action( 'admin_print_scripts', array( self::$instance, 'load_script' ) ); + add_action( 'wp_ajax_dnh_dismiss_notice', array( self::$instance, 'dismiss_notice_ajax' ) ); + + } + + /** + * Check if the current WordPress version fits the requirements + * + * @since 1.0 + * @return boolean + */ + private function is_wp_compatible() { + + if ( version_compare( get_bloginfo( 'version' ), self::$instance->wordpress_version_required, '<' ) ) { + return false; + } + + return true; + + } + + /** + * Check if the version of PHP is compatible with this library + * + * @since 1.0 + * @return boolean + */ + private function is_php_compatible() { + + if ( version_compare( phpversion(), self::$instance->php_version_required, '<' ) ) { + return false; + } + + return true; + + } + + /** + * Include all our files + * + * @since 1.0 + * @return void + */ + private function includes() { + require( trailingslashit( get_template_directory() ) . 'dnh/includes/helper-functions.php' ); + } + + /** + * Load the script + * + * @since 1.0 + * @return void + */ + public function load_script() { + wp_register_script( 'dnh', trailingslashit( get_template_directory_uri() ) . 'dnh/assets/js/main.js', array( 'jquery' ), self::$instance->version, true ); + wp_enqueue_script( 'dnh' ); + } + + /** + * Display all the registered notices + * + * @since 1.0 + * @return void + */ + public function display() { + + if ( is_null( self::$instance->notices ) || empty( self::$instance->notices ) ) { + return; + } + + foreach ( self::$instance->notices as $id => $notice ) { + + $id = self::$instance->get_id( $id ); + + // Check if the notice was dismissed + if ( self::$instance->is_dismissed( $id ) ) { + continue; + } + + // Check if the current user has required capability + if ( ! empty( $notice['cap'] ) && ! current_user_can( $notice['cap'] ) ) { + continue; + } + + $class = array( + 'notice', + $notice['type'], + 'is-dismissible', + $notice['class'], + ); + + printf( '

%2$s

', trim( implode( ' ', $class ) ), $notice['content'], "dnh-$id" ); + + } + + } + + /** + * Spits an error message at the top of the admin screen + * + * @since 1.0 + * + * @param string $error Error message to spit + * + * @return void + */ + protected function spit_error( $error ) { + printf( + '
%1$s %2$s
', + esc_html( 'Dismissible Notices Handler Error:' ), + wp_kses_post( $error ) + ); + } + + /** + * Sanitize a notice ID and return it + * + * @since 1.0 + * + * @param string $id + * + * @return string + */ + public function get_id( $id ) { + return sanitize_key( $id ); + } + + /** + * Get available notice types + * + * @since 1.0 + * @return array + */ + public function get_types() { + + $types = array( + 'error', + 'updated', + ); + + return apply_filters( 'dnh_notice_types', $types ); + + } + + /** + * Get the default arguments for a notice + * + * @since 1.0 + * @return array + */ + private function default_args() { + + $args = array( + 'screen' => '', // Coming soon + 'scope' => 'user', // Scope of the dismissal. Either user or global + 'cap' => '', // Required user capability + 'class' => '', // Additional class to add to the notice + ); + + return apply_filters( 'dnh_default_args', $args ); + + } + + /** + * Register a new notice + * + * @since 1.0 + * + * @param string $id Notice ID, used to identify it + * @param string $type Type of notice to display + * @param string $content Notice content + * @param array $args Additional parameters + * + * @return bool + */ + public function register_notice( $id, $type, $content, $args = array() ) { + + if ( is_null( self::$instance->notices ) ) { + self::$instance->notices = array(); + } + + $id = self::$instance->get_id( $id ); + $type = in_array( $t = sanitize_text_field( $type ), self::$instance->get_types() ) ? $t : 'updated'; + $content = wp_kses_post( $content ); + $args = wp_parse_args( $args, self::$instance->default_args() ); + + if ( array_key_exists( $id, self::$instance->notices ) ) { + return false; + } + + $notice = array( + 'type' => $type, + 'content' => $content, + ); + + $notice = array_merge( $notice, $args ); + + self::$instance->notices[ $id ] = $notice; + + return true; + + } + + /** + * Notice dismissal triggered by Ajax + * + * @since 1.0 + * @return void + */ + public function dismiss_notice_ajax() { + + if ( ! isset( $_POST['id'] ) ) { + echo 0; + exit; + } + + if ( empty( $_POST['id'] ) || false === strpos( $_POST['id'], 'dnh-' ) ) { + echo 0; + exit; + } + + $id = self::$instance->get_id( str_replace( 'dnh-', '', $_POST['id'] ) ); + + echo self::$instance->dismiss_notice( $id ); + exit; + + } + + /** + * Dismiss a notice + * + * @since 1.0 + * + * @param string $id ID of the notice to dismiss + * + * @return bool + */ + public function dismiss_notice( $id ) { + + $notice = self::$instance->get_notice( self::$instance->get_id( $id ) ); + + if ( false === $notice ) { + return false; + } + + if ( self::$instance->is_dismissed( $id ) ) { + return false; + } + + return 'user' === $notice['scope'] ? self::$instance->dismiss_user( $id ) : self::$instance->dismiss_global( $id ); + + } + + /** + * Dismiss notice for the current user + * + * @since 1.0 + * + * @param string $id Notice ID + * + * @return int|bool + */ + private function dismiss_user( $id ) { + + $dismissed = self::$instance->dismissed_user(); + + if ( in_array( $id, $dismissed ) ) { + return false; + } + + array_push( $dismissed, $id ); + + return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed ); + + } + + /** + * Dismiss notice globally on the site + * + * @since 1.0 + * + * @param string $id Notice ID + * + * @return bool + */ + private function dismiss_global( $id ) { + + $dismissed = self::$instance->dismissed_global(); + + if ( in_array( $id, $dismissed ) ) { + return false; + } + + array_push( $dismissed, $id ); + + return update_option( 'dnh_dismissed_notices', $dismissed ); + + } + + /** + * Restore a dismissed notice + * + * @since 1.0 + * + * @param string $id ID of the notice to restore + * + * @return bool + */ + public function restore_notice( $id ) { + + $id = self::$instance->get_id( $id ); + $notice = self::$instance->get_notice( $id ); + + if ( false === $notice ) { + return false; + } + + return 'user' === $notice['scope'] ? self::$instance->restore_user( $id ) : self::$instance->restore_global( $id ); + + } + + /** + * Restore a notice dismissed by the current user + * + * @since 1.0 + * + * @param string $id ID of the notice to restore + * + * @return bool + */ + private function restore_user( $id ) { + + $id = self::$instance->get_id( $id ); + $notice = self::$instance->get_notice( $id ); + + if ( false === $notice ) { + return false; + } + + $dismissed = self::$instance->dismissed_user(); + + if ( ! in_array( $id, $dismissed ) ) { + return false; + } + + $flip = array_flip( $dismissed ); + $key = $flip[ $id ]; + + unset( $dismissed[ $key ] ); + + return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed ); + + } + + /** + * Restore a notice dismissed globally + * + * @since 1.0 + * + * @param string $id ID of the notice to restore + * + * @return bool + */ + private function restore_global( $id ) { + + $id = self::$instance->get_id( $id ); + $notice = self::$instance->get_notice( $id ); + + if ( false === $notice ) { + return false; + } + + $dismissed = self::$instance->dismissed_global(); + + if ( ! in_array( $id, $dismissed ) ) { + return false; + } + + $flip = array_flip( $dismissed ); + $key = $flip[ $id ]; + + unset( $dismissed[ $key ] ); + + return update_option( 'dnh_dismissed_notices', $dismissed ); + + } + + /** + * Get all dismissed notices + * + * This includes notices dismissed globally or per user. + * + * @since 1.0 + * @return array + */ + public function dismissed_notices() { + + $user = self::$instance->dismissed_user(); + $global = self::$instance->dismissed_global(); + + return array_merge( $user, $global ); + + } + + /** + * Get user dismissed notices + * + * @since 1.0 + * @return array + */ + private function dismissed_user() { + + $dismissed = get_user_meta( get_current_user_id(), 'dnh_dismissed_notices', true ); + + if ( '' === $dismissed ) { + $dismissed = array(); + } + + return $dismissed; + + } + + /** + * Get globally dismissed notices + * + * @since 1.0 + * @return array + */ + private function dismissed_global() { + return get_option( 'dnh_dismissed_notices', array() ); + } + + /** + * Check if a notice has been dismissed + * + * @since 1.0 + * + * @param string $id Notice ID + * + * @return bool + */ + public function is_dismissed( $id ) { + + $dismissed = self::$instance->dismissed_notices(); + + if ( ! in_array( self::$instance->get_id( $id ), $dismissed ) ) { + return false; + } + + return true; + + } + + /** + * Get all the registered notices + * + * @since 1.0 + * @return array|null + */ + public function get_notices() { + return self::$instance->notices; + } + + /** + * Return a specific notice + * + * @since 1.0 + * + * @param string $id Notice ID + * + * @return array|false + */ + public function get_notice( $id ) { + + $id = self::$instance->get_id( $id ); + + if ( ! is_array( self::$instance->notices ) || ! array_key_exists( $id, self::$instance->notices ) ) { + return false; + } + + return self::$instance->notices[ $id ]; + + } + + } + + /** + * The main function responsible for returning the unique Dismissible Notices Handler instance + * + * Use this function like you would a global variable, except without needing + * to declare the global. + * + * @since 1.0 + * @return object Dismissible_Notices_Handler + */ + function DNH() { + return Dismissible_Notices_Handler::instance(); + } + + /** + * Get the library running + */ + DNH(); + +} diff --git a/dnh/includes/helper-functions.php b/dnh/includes/helper-functions.php new file mode 100755 index 0000000..fea5323 --- /dev/null +++ b/dnh/includes/helper-functions.php @@ -0,0 +1,83 @@ + + * + * @package Dismissible Notices Handler/Helper Functions + * @author Julien Liabeuf + * @version 1.0 + * @license GPL-2.0+ + * @link https://julienliabeuf.com + * @copyright 2016 Julien Liabeuf + */ + +// If this file is called directly, abort. +if ( ! defined( 'WPINC' ) ) { + die; +} + +/** + * Register a new notice + * + * @since 1.0 + * + * @param string $id Notice ID, used to identify it + * @param string $type Type of notice to display + * @param string $content Notice content + * @param array $args Additional parameters + * + * @return bool + */ +function dnh_register_notice( $id, $type, $content, $args = array() ) { + + if ( ! function_exists( 'DNH' ) ) { + return false; + } + + return DNH()->register_notice( $id, $type, $content, $args ); + +} + +/** + * Restore a previously dismissed notice + * + * @since 1.0 + * + * @param string $id ID of the notice to restore + * + * @return bool + */ +function dnh_restore_notice( $id ) { + + if ( ! function_exists( 'DNH' ) ) { + return false; + } + + return DNH()->restore_notice( $id ); + +} + +/** + * Check if a notice has been dismissed + * + * @since 1.0 + * + * @param string $id ID of the notice to check + * + * @return bool + */ +function dnh_is_dismissed( $id ) { + + if ( ! function_exists( 'DNH' ) ) { + return false; + } + + return DNH()->is_dismissed( $id ); + +} \ No newline at end of file diff --git a/functions.php b/functions.php index cb2a695..3abaa04 100755 --- a/functions.php +++ b/functions.php @@ -10,6 +10,14 @@ foreach ( glob( trailingslashit( get_template_directory() ) . 'licenses/functions/*.php' ) as $filename ) { include $filename; } +require_once( trailingslashit( get_template_directory() ) . 'dnh/handler.php' ); +new WP_Review_Me( array( + 'days_after' => 14, + 'type' => 'theme', + 'slug' => 'tracks', + 'message' => __( 'Hey! Sorry to interrupt, but you\'ve been using Tracks for a little while now. If you\'re happy with this theme, could you take a minute to leave a review? You won\'t see this notice again after closing it.', 'tracks' ) + ) +); if ( ! function_exists( ( 'ct_tracks_set_content_width' ) ) ) { function ct_tracks_set_content_width() { diff --git a/inc/review.php b/inc/review.php new file mode 100755 index 0000000..6c48cb8 --- /dev/null +++ b/inc/review.php @@ -0,0 +1,412 @@ + + * + * @package WP Review Me + * @author Julien Liabeuf + * @version 2.0.1 + * @license GPL-2.0+ + * @link https://julienliabeuf.com + * @copyright 2016 Julien Liabeuf + */ + +// If this file is called directly, abort. +if ( ! defined( 'WPINC' ) ) { + die; +} + +if ( ! class_exists( 'WP_Review_Me' ) ) { + + class WP_Review_Me { + + /** + * Library version + * + * @since 1.0 + * @var string + */ + public $version = '2.0.1'; + + /** + * Required version of PHP. + * + * @since 1.0 + * @var string + */ + public $php_version_required = '5.5'; + + /** + * Minimum version of WordPress required to use the library + * + * @since 1.0 + * @var string + */ + public $wordpress_version_required = '4.2'; + + /** + * Holds the unique identifying key for this particular instance + * + * @since 1.0 + * @var string + */ + protected $key; + + /** + * Link unique ID + * + * @since 1.0 + * @var string + */ + public $link_id; + + /** + * WP_Review_Me constructor. + * + * @since 1.0 + * + * @param array $args Object settings + */ + public function __construct( $args ) { + + $args = wp_parse_args( $args, $this->get_defaults() ); + $this->days = $args['days_after']; + $this->type = $args['type']; + $this->slug = $args['slug']; + $this->rating = $args['rating']; + $this->message = $args['message']; + $this->link_label = $args['link_label']; + $this->cap = $args['cap']; + $this->scope = $args['scope']; + + // Set the unique identifying key for this instance + $this->key = 'wrm_' . substr( md5( plugin_basename( __FILE__ ) ), 0, 20 ); + $this->link_id = 'wrm-review-link-' . $this->key; + + // Instantiate + $this->init(); + + } + + /** + * Get default object settings + * + * @since 1.0 + * @return array + */ + protected function get_defaults() { + + $defaults = array( + 'days_after' => 10, + 'type' => '', + 'slug' => '', + 'rating' => 5, + 'message' => sprintf( esc_html( 'Hey! It's been a little while that you've been using this product. You might not realize it, but user reviews are such a great help to us. We would be so grateful if you could take a minute to leave a review on WordPress.org. Many thanks in advance :)' ) ), + 'link_label' => esc_html__( 'Click here to leave your review', 'tracks' ), + // Parameters used in WP Dismissible Notices Handler + 'cap' => 'administrator', + 'scope' => 'global', + ); + + return $defaults; + + } + + /** + * Initialize the library + * + * @since 1.0 + * @return void + */ + private function init() { + + // Make sure WordPress is compatible + if ( ! $this->is_wp_compatible() ) { + return; + } + + // Make sure PHP is compatible + if ( ! $this->is_php_compatible() ) { + return; + } + + // Make sure the dependencies are loaded + if ( ! function_exists( 'dnh_register_notice' ) ) { + + $dnh_file = trailingslashit( plugin_dir_path( __FILE__ ) ) . 'vendor/julien731/wp-dismissible-notices-handler/handler.php'; + + if ( file_exists( $dnh_file ) ) { + require( $dnh_file ); + } + + if ( ! function_exists( 'dnh_register_notice' ) ) { + $this->spit_error( + sprintf( + esc_html( 'Dependencies are missing. Please run a %s.' ), + 'composer install' + ) + ); + + return; + } + } + + add_action( 'admin_footer', array( $this, 'script' ) ); + add_action( 'wp_ajax_wrm_clicked_review', array( $this, 'dismiss_notice' ) ); + + // And let's roll... maybe. + $this->maybe_prompt(); + + } + + /** + * Check if the current WordPress version fits the requirements + * + * @since 1.0 + * @return boolean + */ + private function is_wp_compatible() { + + if ( version_compare( get_bloginfo( 'version' ), $this->wordpress_version_required, '<' ) ) { + return false; + } + + return true; + + } + + /** + * Check if the version of PHP is compatible with this library + * + * @since 1.0 + * @return boolean + */ + private function is_php_compatible() { + + if ( version_compare( phpversion(), $this->php_version_required, '<' ) ) { + return false; + } + + return true; + + } + + /** + * Spits an error message at the top of the admin screen + * + * @since 1.0 + * + * @param string $error Error message to spit + * + * @return void + */ + protected function spit_error( $error ) { + printf( + '
%1$s %2$s
', + esc_html( 'WP Review Me Error:' ), + wp_kses_post( $error ) + ); + } + + /** + * Check if it is time to ask for a review + * + * @since 1.0 + * @return bool + */ + public function is_time() { + + $installed = (int) get_option( $this->key, false ); + + if ( 0 === $installed ) { + $this->setup_date(); + $installed = time(); + } + + if ( $installed + ( $this->days * 86400 ) > time() ) { + return false; + } + + return true; + + } + + /** + * Save the current date as the installation date + * + * @since 1.0 + * @return void + */ + protected function setup_date() { + update_option( $this->key, time() ); + } + + /** + * Get the review link + * + * @since 1.0 + * @return string + */ + protected function get_review_link() { + + $link = 'https://wordpress.org/support/'; + + switch ( $this->type ) { + + case 'theme': + $link .= 'theme/'; + break; + + case 'plugin': + $link .= 'plugin/'; + break; + + } + + $link .= $this->slug . '/reviews'; + $link = add_query_arg( 'rate', $this->rating, $link ); + $link = esc_url( $link . '#new-post' ); + + return $link; + + } + + /** + * Get the complete link tag + * + * @since 1.0 + * @return string + */ + protected function get_review_link_tag() { + + $link = $this->get_review_link(); + + return "$this->link_label"; + + } + + /** + * Trigger the notice if it is time to ask for a review + * + * @since 1.0 + * @return void + */ + protected function maybe_prompt() { + + if ( ! $this->is_time() ) { + return; + } + + dnh_register_notice( $this->key, 'updated', $this->get_message(), array( + 'scope' => $this->scope, + 'cap' => $this->cap + ) ); + + } + + /** + * Echo the JS script in the admin footer + * + * @since 1.0 + * @return void + */ + public function script() { ?> + + + + link_id ) { + echo "not this instance's job"; + die(); + } + + // Get the DNH notice ID ready + $notice_id = DNH()->get_id( str_replace( 'wrm-review-link-', '', $id ) ); + $dismissed = DNH()->dismiss_notice( $notice_id ); + + echo $dismissed; + + /** + * Fires right after the notice has been dismissed. This allows for various integrations to perform additional tasks. + * + * @since 1.0 + * + * @param string $id The notice ID + * @param string $notice_id The notice ID as defined by the DNH class + */ + do_action( 'wrm_after_notice_dismissed', $id, $notice_id ); + + // Stop execution here + die(); + + } + + /** + * Get the review prompt message + * + * @since 1.0 + * @return string + */ + protected function get_message() { + + $message = $this->message; + $link = $this->get_review_link_tag(); + $message = $message . ' ' . $link; + + return wp_kses_post( $message ); + + } + + } + +} \ No newline at end of file