Viewing file: module.php (12.84 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /** * WPMUDEV Frash - Free Dashboard Notification module. * Used by wordpress.org hosted plugins. * * TODO: * - Form submit handler to submit email address to getdrip.com * - Show the WPMUDEV logo in left side * * @version 1.0.0 * @author Incsub (Philipp Stracker) */ if ( ! class_exists( 'WDev_Frash' ) ) { class WDev_Frash {
/** * The text-domain of the module. * * @since 1.0.0 */ const LANG = 'wdev-frash';
/** * List of all registered plugins. * * @since 1.0.0 * @var array */ protected $plugins = array();
/** * Module options that are stored in database. * Timestamps are stored here. * * Note that this option is stored in site-meta for multisite installs. * * @since 1.0.0 * @var array */ protected $stored = array();
/** * Initializes and returns the singleton instance. * * @since 1.0.0 */ static public function instance() { static $Inst = null;
if ( null === $Inst ) { $Inst = new WDev_Frash(); }
return $Inst; }
/** * Set up the WDev_Frash module. Private singleton constructor. * * @since 1.0.0 */ private function __construct() { $this->read_stored_data();
$this->add_action( 'wdev-register-plugin', 5 ); $this->add_action( 'load-index.php' );
$this->add_action( 'wp_ajax_frash_act' ); $this->add_action( 'wp_ajax_frash_dismiss' ); }
/** * Load persistent module-data from the WP Database. * * @since 1.0.0 */ protected function read_stored_data() { $data = get_site_option( 'wdev-frash', false, false );
if ( ! is_array( $data ) ) { $data = array(); }
// A list of all plugins with timestamp of first registration. if ( ! isset( $data['plugins'] ) || ! is_array( $data['plugins'] ) ) { $data['plugins'] = array(); }
// A list with pending messages and earliest timestamp for display. if ( ! isset( $data['queue'] ) || ! is_array( $data['queue'] ) ) { $data['queue'] = array(); }
// A list with all messages that were handles already. if ( ! isset( $data['done'] ) || ! is_array( $data['done'] ) ) { $data['done'] = array(); }
$this->stored = $data; }
/** * Save persistent module-data to the WP database. * * @since 1.0.0 */ protected function store_data() { update_site_option( 'wdev-frash', $this->stored ); }
/** * Action handler for 'wdev-register-plugin' * Register an active plugin. * * @since 1.0.0 * @param string $plugin_id WordPress plugin-ID (see: plugin_basename). * @param string $title Plugin name for display. * @param string $url_wp URL to the plugin on wp.org (domain not needed) * @param string $cta_email Title of the Email CTA button. * @param string $drip_plugin Optional. Plugin-param for the getdrip rule. */ public function wdev_register_plugin( $plugin_id, $title, $url_wp, $cta_email = '', $drip_plugin = '' ) { // Ignore incorrectly registered plugins to avoid errors later. if ( empty( $plugin_id ) ) { return; } if ( empty( $title ) ) { return; } if ( empty( $url_wp ) ) { return; }
if ( false === strpos( $url_wp, '://' ) ) { $url_wp = 'https://wordpress.org/' . trim( $url_wp, '/' ); }
$this->plugins[$plugin_id] = (object) array( 'id' => $plugin_id, 'title' => $title, 'url_wp' => $url_wp, 'cta_email' => $cta_email, 'drip_plugin' => $drip_plugin, );
/* * When the plugin is registered the first time we store some infos * in the persistent module-data that help us later to find out * if/which message should be displayed. */ if ( empty( $this->stored['plugins'][$plugin_id] ) ) { // First register the plugin permanently. $this->stored['plugins'][$plugin_id] = time();
// Second schedule the messages to display. $hash = md5( $plugin_id . '-email' ); $this->stored['queue'][$hash] = array( 'plugin' => $plugin_id, 'type' => 'email', 'show_at' => time(), // Earliest time to display note. );
$hash = md5( $plugin_id . '-rate' ); $this->stored['queue'][$hash] = array( 'plugin' => $plugin_id, 'type' => 'rate', 'show_at' => time() + 7 * DAY_IN_SECONDS, );
// Finally save the details. $this->store_data(); } }
/** * Ajax handler called when the user chooses the CTA button. * * @since 1.0.0 */ public function wp_ajax_frash_act() { $plugin = $_POST['plugin_id']; $type = $_POST['type'];
$this->mark_as_done( $plugin, $type, 'ok' );
echo 1; exit; }
/** * Ajax handler called when the user chooses the dismiss button. * * @since 1.0.0 */ public function wp_ajax_frash_dismiss() { $plugin = $_POST['plugin_id']; $type = $_POST['type'];
$this->mark_as_done( $plugin, $type, 'ignore' );
echo 1; exit; }
/** * Action handler for 'load-index.php' * Set-up the Dashboard notification. * * @since 1.0.0 */ public function load_index_php() { if ( is_super_admin() ) { $this->add_action( 'all_admin_notices' ); } }
/** * Action handler for 'admin_notices' * Display the Dashboard notification. * * @since 1.0.0 */ public function all_admin_notices() { $info = $this->choose_message(); if ( ! $info ) { return; }
$this->render_message( $info ); }
/** * Check to see if there is a pending message to display and returns * the message details if there is. * * Note that this function is only called on the main Dashboard screen * and only when logged in as super-admin. * * @since 1.0.0 * @return object|false * string $type [rate|email] Which message type? * string $plugin WordPress plugin ID? */ protected function choose_message() { $obj = false; $chosen = false; $earliest = false;
$now = time();
// The "current" time can be changed via $_GET to test the module. if ( defined( 'WP_DEBUG' ) && WP_DEBUG && ! empty( $_GET['time'] ) ) { $custom_time = $_GET['time']; if ( ' ' == $custom_time[0] ) { $custom_time[0] = '+'; } if ( $custom_time ) { $now = strtotime( $custom_time ); } if ( ! $now ) { $now = time(); } }
$tomorrow = $now + DAY_IN_SECONDS;
foreach ( $this->stored['queue'] as $hash => $item ) { $show_at = intval( $item['show_at'] ); $is_sticky = ! empty( $item['sticky'] );
if ( ! isset( $this->plugins[ $item['plugin'] ] ) ) { // Deactivated plugin before the message was displayed. continue; } $plugin = $this->plugins[ $item['plugin'] ];
$can_display = true; if ( wp_is_mobile() ) { // Do not display rating message on mobile devices. if ( 'rate' == $item['type'] ) { $can_display = false; } } if ( 'email' == $item['type'] ) { if ( ! $plugin->drip_plugin || ! $plugin->cta_email ) { // Do not display email message with missing email params. $can_display = false; } } if ( $now < $show_at ) { // Do not display messages that are not due yet. $can_display = false; }
if ( ! $can_display ) { continue; }
if ( $is_sticky ) { // If sticky item is present then choose it! $chosen = $hash; break; } elseif ( ! $earliest || $earliest < $show_at ) { $earliest = $show_at; $chosen = $hash; // Don't use `break` because a sticky item might follow... // Find the item with the earliest schedule. } }
if ( $chosen ) { // Make the chosen item sticky. $this->stored['queue'][$chosen]['sticky'] = true;
// Re-schedule other messages that are due today. foreach ( $this->stored['queue'] as $hash => $item ) { $show_at = intval( $item['show_at'] );
if ( empty( $item['sticky'] ) && $tomorrow > $show_at ) { $this->stored['queue'][$hash]['show_at'] = $tomorrow; } }
// Save the changes. $this->store_data();
$obj = (object) $this->stored['queue'][$chosen]; }
return $obj; }
/** * Moves a message from the queue to the done list. * * @since 1.0.0 * @param string $plugin Plugin ID. * @param string $type [rate|email] Message type. * @param string $state [ok|ignore] Button clicked. */ protected function mark_as_done( $plugin, $type, $state ) { $done_item = false;
foreach ( $this->stored['queue'] as $hash => $item ) { unset( $this->stored['queue'][$hash]['sticky'] );
if ( $item['plugin'] == $plugin && $item['type'] == $type ) { $done_item = $item; unset( $this->stored['queue'][$hash] ); } }
if ( $done_item ) { $done_item['state'] = $state; $done_item['hash'] = $hash; $done_item['handled_at'] = time(); unset( $done_item['sticky'] );
$this->stored['done'][] = $done_item; $this->store_data(); } }
/** * Renders the actual Notification message. * * @since 1.0.0 */ protected function render_message( $info ) { $plugin = $this->plugins[$info->plugin]; $css_url = plugin_dir_url( __FILE__ ) . '/admin.css'; $js_url = plugin_dir_url( __FILE__ ) . '/admin.js';
?> <link rel="stylesheet" type="text/css" href="<?php echo esc_url( $css_url ); ?>" /> <div class="notice frash-notice frash-notice-<?php echo esc_attr( $info->type ); ?>" style="display:none"> <input type="hidden" name="type" value="<?php echo esc_attr( $info->type ); ?>" /> <input type="hidden" name="plugin_id" value="<?php echo esc_attr( $info->plugin ); ?>" /> <input type="hidden" name="url_wp" value="<?php echo esc_attr( $plugin->url_wp ); ?>" /> <input type="hidden" name="drip_plugin" value="<?php echo esc_attr( $plugin->drip_plugin ); ?>" /> <?php if ( 'email' == $info->type ) { $this->render_email_message( $plugin ); } elseif ( 'rate' == $info->type ) { $this->render_rate_message( $plugin ); } ?> </div> <script src="<?php echo esc_url( $js_url ); ?>"></script> <?php }
/** * Output the contents of the email message. * No return value. The code is directly output. * * @since 1.0.0 */ protected function render_email_message( $plugin ) { $user = wp_get_current_user(); $user_name = $user->display_name; $admin_email = get_site_option( 'admin_email' );
$msg = __( "We're happy that you've chosen to install %s! Are you interested in how to make the most of this plugin? How would you like a quick 5 day email crash course with actionable advice on building your membership site? Only the info you want, no subscription!", self::LANG ); $msg = apply_filters( 'wdev-email-message-' . $plugin->id, $msg );
?> <div class="frash-notice-logo"><span></span></div> <div class="frash-notice-message"> <?php printf( $msg, '<strong>' . $plugin->title . '</strong>' ); ?> </div> <div class="frash-notice-cta"> <input type="email" name="email" value="<?php echo esc_attr( $admin_email ); ?>" /> <button class="frash-notice-act button-primary" data-msg="<?php _e( 'Thanks :)', self::LANG ); ?>"> <?php echo esc_html( $plugin->cta_email ); ?> </button> <button class="frash-notice-dismiss" data-msg="<?php _e( 'Saving', self::LANG ); ?>"> <?php _e( 'No thanks', self::LANG ); ?> </button> </div> <?php }
/** * Output the contents of the rate-this-plugin message. * No return value. The code is directly output. * * @since 1.0.0 */ protected function render_rate_message( $plugin ) { $user = wp_get_current_user(); $user_name = $user->display_name;
$msg = __( "Hey %s, you've been using %s for a while now, and we hope you're happy with it.", self::LANG ) . '<br />'. __( "We've spent countless hours developing this free plugin for you, and we would really appreciate it if you dropped us a quick rating!", self::LANG ); $msg = apply_filters( 'wdev-rating-message-' . $plugin->id, $msg );
?> <div class="frash-notice-logo"><span></span></div> <div class="frash-notice-message"> <?php printf( $msg, '<strong>' . $user_name . '</strong>', '<strong>' . $plugin->title . '</strong>' ); ?> </div> <div class="frash-notice-cta"> <button class="frash-notice-act button-primary" data-msg="<?php _e( 'Thanks :)', self::LANG ); ?>"> <?php printf( __( 'Rate %s', self::LANG ), esc_html( $plugin->title ) ); ?> </button> <button class="frash-notice-dismiss" data-msg="<?php _e( 'Saving', self::LANG ); ?>"> <?php _e( 'No thanks', self::LANG ); ?> </button> </div> <?php }
/** * Registers a new action handler. The callback function has the same * name as the action hook. * * @since 1.0.0 */ protected function add_action( $hook, $params = 1 ) { $method_name = strtolower( $hook ); $method_name = preg_replace( '/[^a-z0-9]/', '_', $method_name ); $handler = array( $this, $method_name ); add_action( $hook, $handler, 5, $params ); } }
// Initialize the module. WDev_Frash::instance(); }
|