├── .gitignore ├── README.md └── wp-heartbeat-notify ├── core ├── class-my-plugin.php └── class-wp-heartbeat-notify.php ├── css └── wp-heartbeat-notify.css ├── js ├── wp-heartbeat-notify.js └── wp-heartbeat-notify.min.js ├── langs ├── wp-heartbeat-notify-it_IT.mo └── wp-heartbeat-notify-it_IT.po ├── screenshot-1.jpg └── wp-heartbeat-notify.php /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | wp-heartbeat-notify/.DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wp-heartbeat-notify 2 | 3 | #### WordPress realtime new post notifications based on heartbeat API 4 | 5 | Based on **WordPress 3.6 heartbeat API**, Wp Heartbeat Notify, display a realtime custom message to your visitor each time a new post is published with a link redirecting to it. Still in beta version, this plugin has been **full tested only on WordPress 3.6-beta3**. 6 | 7 | ![wp-heartbeat-notify screenshot](https://github.com/micc83/wp-heartbeat-notify/raw/master/wp-heartbeat-notify/screenshot-1.jpg) 8 | 9 | ## Install 10 | 11 | Just upload `wp-heartbeat-notify` to your WordPress `wp-content/plugin/` folder. Go to *Plugins->Installed plugins* WordPress menu and activate it. That's it... You just have to write a new article to see it in action (Clearly, the notification will not be displayed to the user who created the event. So, for the purposes of testing, I suggest you open another browser where you're not logged in). 12 | 13 | ## Purpose 14 | 15 | This is probably the first, or at least one of the first WordPress plugin that makes use of the Heartbeat API. Its purpose is almost purely didactic and I hope that the solution that I have implemented it can be useful for developing new ideas. 16 | 17 | The plugin, however, is fully functional and can increase the functionality with a few lines of code, as described below. 18 | 19 | ## Under the hood 20 | 21 | The functioning of the plugin is quite simple. 22 | 23 | * All scripts and styles necessary for the functioning of heartbeat are queued 24 | * To generate a notification you hook onto an action or a filter of WordPress and you create a transient that match the length of the heartbeat rate 25 | * wp-heartbeat-notify, every few seconds, check the presence of transient and output the content as a notification 26 | 27 | Easy... Isn't it? 28 | 29 | ## Add new notice 30 | 31 | To add a new notice, as stated before, you just have to hook into an action or filter and run `Wp_Heartbeat_Notify::notify( $args )` as in the following example: 32 | 33 | ### Add a notice on registered users comments 34 | 35 | ```php 36 | 37 | // Let's hook into Comment publication 38 | add_filter ( 'comment_post', 'notify_new_comment' ); 39 | function notify_new_comment( $comment_id ) { 40 | 41 | // Retrieve the comment 42 | $comment = get_comment( $comment_id ); 43 | 44 | // Check if the user is registered 45 | if ( ! $comment->user_id > 0 ) 46 | return; 47 | 48 | // Get the comment link 49 | $comment_link = get_comment_link( $comment_id ); 50 | 51 | // Here's the magic 52 | Wp_Heartbeat_Notify::notify( array( 53 | 'title' => 'New Comment by ' . $comment->comment_author, 54 | 'content' => 'There\'s a new comment, why don\'t you give it a look?', 55 | 'type' => 'info' 56 | ) ); 57 | 58 | } 59 | ``` 60 | 61 | ## Support and contacts 62 | 63 | If you need support you can find me on [twitter](https://twitter.com/Micc1983) or comment on the dedicated page on my [website](http://codeb.it/). 64 | -------------------------------------------------------------------------------- /wp-heartbeat-notify/core/class-my-plugin.php: -------------------------------------------------------------------------------- 1 | '3.0', 17 | 'php' => '5.0.0' 18 | ), 19 | 20 | // Plugin uri for linking 21 | $uri, 22 | 23 | // Plugin folder 24 | $dir, 25 | 26 | // Plugin version option name 27 | $version_option, 28 | 29 | // This will store plugin file location 30 | $file, 31 | 32 | // If version not set 33 | $version = '0.0.1'; 34 | 35 | function __construct( $file , array $args = array() ) { 36 | 37 | // Get the plugin main file 38 | $this->file = $file; 39 | 40 | // Get the plugin uri 41 | $this->uri = plugin_dir_url( $file ); 42 | 43 | // Get the plugin dir 44 | $this->dir = plugin_dir_path( $file ); 45 | 46 | // Set the domain and textdomain based on class name 47 | $this->textdomain = $this->domain = basename( $file, '.php' ); 48 | 49 | // Set required software versions 50 | if ( isset( $args['required'] ) ) 51 | $this->required = array_merge( $this->required, $args['required'] ); 52 | 53 | // Plugin data to retrieve 54 | $plugin_data_fields = array( 55 | 'name' => 'Plugin Name', 56 | 'pluginuri' => 'Plugin URI', 57 | 'version' => 'Version', 58 | 'description' => 'Description', 59 | 'author' => 'Author', 60 | 'authoruri' => 'Author URI', 61 | 'textdomain' => 'Text Domain' 62 | ); 63 | 64 | // Retrieve current plugin version 65 | $plugin_data = get_file_data( $this->file, $plugin_data_fields ); 66 | 67 | foreach ( $plugin_data_fields as $key => $value ) { 68 | if ( isset( $plugin_data[ $key ] ) && !empty( $plugin_data[ $key ] ) ) { 69 | 70 | $this->{$key} = $plugin_data[ $key ]; 71 | 72 | } 73 | } 74 | 75 | // Set the name of version option 76 | $this->version_option = $this->domain . '_version'; 77 | 78 | // Register activation hook 79 | register_activation_hook( $this->file, array( $this, 'plugin_activation' ) ); 80 | 81 | // Localize the plugin 82 | add_action( 'plugins_loaded', array( $this, 'plugin_init' ) ); 83 | 84 | // On install 85 | add_action( 'init', array( $this, 'plugin_install' ) ); 86 | 87 | // On update 88 | add_action( 'init', array( $this, 'plugin_update' ) ); 89 | 90 | } 91 | 92 | /** 93 | * Check for software version compatibility 94 | */ 95 | function plugin_activation() { 96 | 97 | // Check WP Version 98 | if ( version_compare( get_bloginfo('version'), $this->required['wordpress'], '<') ) 99 | $error = sprintf( __( 'This plugin requires WordPress %s or greater.', $this->domain ), $this->required['wordpress'] ); 100 | 101 | // Check PHP Version 102 | if ( version_compare( PHP_VERSION, $this->required['php'], '<' ) ) 103 | $error = sprintf( __( 'This plugin requires PHP %s or greater.', $this->domain ), $this->required['php'] ); 104 | 105 | if ( isset( $error ) ){ 106 | 107 | // Deactivate plugin and DIE!!! 108 | deactivate_plugins( basename( $this->file ) ); 109 | wp_die( $error ); 110 | 111 | } 112 | 113 | } 114 | 115 | /** 116 | * Localize the plugin 117 | */ 118 | function plugin_init() { 119 | 120 | load_plugin_textdomain( $this->textdomain, false, dirname( plugin_basename( $this->file ) ) . '/langs/' ); 121 | 122 | } 123 | 124 | /** 125 | * Plugin update 126 | * 127 | * @uses do_action() 128 | */ 129 | function plugin_update() { 130 | 131 | // if version is already set and new version is higher than current 132 | if ( false !== get_option( $this->version_option ) 133 | && version_compare( $this->version, get_option( $this->version_option ), '>' ) ) { 134 | 135 | update_option( $this->version_option, $this->version ); 136 | do_action( $this->domain . '_update' ); 137 | 138 | } 139 | 140 | } 141 | 142 | /** 143 | * First plugin install 144 | * 145 | * @uses do_action() 146 | */ 147 | function plugin_install() { 148 | 149 | // If version in not set 150 | if ( false === get_option( $this->version_option ) ) { 151 | 152 | do_action( $this->domain . '_install' ); 153 | update_option( $this->version_option, $this->version ); 154 | 155 | } 156 | 157 | } 158 | 159 | } // Class My_Plugin 160 | -------------------------------------------------------------------------------- /wp-heartbeat-notify/core/class-wp-heartbeat-notify.php: -------------------------------------------------------------------------------- 1 | array( 'admin', 'front' ), 11 | 12 | // User needed capability 13 | 'capability' => false, 14 | 15 | // Include jQuery or use theme one 16 | 'native_jquery' => true, 17 | 18 | // Heartbeat rate - Only for testing purposes (default: 15) 19 | 'interval' => 'auto', 20 | 21 | // Domain - So you'll be able to handle multiple instance 22 | 'domain' => 'whn', 23 | 24 | // Base url to link js and css (must be set) 25 | 'base_url' => '' 26 | 27 | ); 28 | 29 | /** 30 | * WordPress Heartbeat Notifications Class Constructor 31 | * 32 | * @param Array $args Class options 33 | */ 34 | function __construct( $args = array() ) { 35 | 36 | if ( is_array( $args ) ) 37 | $this->args = array_merge( $this->args, $args ); 38 | 39 | if ( empty( $args['base_url'] ) ) 40 | trigger_error( 'You have to set the url of the js and css folder' , E_USER_NOTICE ); 41 | 42 | if ( ! $this->has_needed_capability() || !has_filter('heartbeat_settings') ) 43 | return; 44 | 45 | if ( 'auto' != $this->args['interval'] && is_int( $this->args['interval'] ) ) 46 | add_filter( 'heartbeat_settings', array( $this, 'change_hearbeat_rate' ) ); 47 | 48 | if ( in_array( 'admin', $this->args['context'] ) ) 49 | add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_my_stuff' ) ); 50 | 51 | if ( in_array( 'front', $this->args['context'] ) ){ 52 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_my_stuff' ) ); 53 | add_filter( 'heartbeat_nopriv_send', array( $this, 'send_data_to_heartbeat' ), 10, 2 ); 54 | } 55 | 56 | add_filter( 'heartbeat_send', array( $this, 'send_data_to_heartbeat' ), 10, 2 ); 57 | 58 | } 59 | 60 | /** 61 | * Enqueue all the needed stuff 62 | * 63 | * Bum, bum... it's aliveeeeee!!! 64 | */ 65 | function enqueue_my_stuff() { 66 | 67 | $dependency = array( 'heartbeat' ); 68 | 69 | if ( $this->args['native_jquery'] ) 70 | $dependency[] = 'jquery'; 71 | 72 | wp_enqueue_script( 'wp_hearbeat_notify_js', $this->args['base_url'] . '/js/wp-heartbeat-notify.min.js', $dependency, null, false ); 73 | wp_enqueue_style( 'wp_hearbeat_notify_css', $this->args['base_url'] . '/css/wp-heartbeat-notify.css' ); 74 | 75 | } 76 | 77 | /** 78 | * Check if the user has needed capability 79 | * 80 | * @return Bool 81 | */ 82 | private function has_needed_capability() { 83 | 84 | if( !$this->args['capability'] || current_user_can( $this->args['capability'] ) ) 85 | return true; 86 | 87 | return false; 88 | 89 | } 90 | 91 | /** 92 | * Add data to the heartbeat ajax call (filter) 93 | * 94 | * @arg Array $data Original data to be sent to Hearbeat 95 | * @arg String $screen_id Admin Screen ID 96 | * 97 | * @return Array Modified array of data to be sent to Hearbeat 98 | */ 99 | function send_data_to_heartbeat( $data, $screen_id ) { 100 | 101 | global $wpdb; 102 | 103 | $sql = $wpdb->prepare( 104 | "SELECT * FROM $wpdb->options WHERE option_name LIKE %s", 105 | '_transient_' . $this->args['domain'] . '_%' 106 | ); 107 | 108 | $notifications = $wpdb->get_results( $sql ); 109 | 110 | if ( empty( $notifications ) ) 111 | return $data; 112 | 113 | $current_user = wp_get_current_user(); 114 | 115 | foreach ( $notifications as $db_notification ) { 116 | 117 | $id = str_replace( '_transient_', '', $db_notification->option_name ); 118 | 119 | if ( false !== ( $notification = get_transient( $id ) ) && $notification['user'] != md5( $current_user->user_login ) ) 120 | $data['message'][ $id ] = $notification; 121 | 122 | } 123 | 124 | return $data; 125 | 126 | } 127 | 128 | /** 129 | * Change Hearbeat rate (filter) 130 | * 131 | * Only for testing purposes 132 | * 133 | * @arg Array $settings 134 | * 135 | * @return Array 136 | */ 137 | function change_hearbeat_rate( $settings ) { 138 | 139 | $settings['interval'] = $this->args['interval']; 140 | 141 | return $settings; 142 | 143 | } 144 | 145 | /** 146 | * Store the notification in the DB 147 | * 148 | * @arg Array $args Options 149 | */ 150 | static function notify( array $args ) { 151 | 152 | $current_user = wp_get_current_user(); 153 | 154 | $default_args = array( 155 | 'title' => '', 156 | 'content' => 'Notification message not set', 157 | 'type' => 'error', 158 | 'domain' => 'whn', 159 | 'user' => md5( $current_user->user_login ) 160 | ); 161 | 162 | $args = array_merge( $default_args, $args ); 163 | 164 | // If you want/need to change hearbeat rate remember to change transient duration 165 | set_transient( $args['domain'] . '_' . mt_rand( 100000, 999999 ), $args, 15 ); 166 | 167 | } 168 | 169 | } // Class Wp_Hearbeat_Notify 170 | -------------------------------------------------------------------------------- /wp-heartbeat-notify/css/wp-heartbeat-notify.css: -------------------------------------------------------------------------------- 1 | .admin-bar #popup_container { margin-top: 28px; } 2 | #popup_container { position:fixed;bottom:0;right:0;overflow:hidden;width:25%;paddind:10px;z-index: 10000; } 3 | #popup_container .popup_notification { padding:10px 20px;margin:10px;position:relative;font-size:12px;border-radius:3px;-moz-border-radius:3px; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.3); -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.3); box-shadow: 0 1px 2px rgba(0,0,0,0.3);line-height: 20px;font-family: Tahoma,Geneva,Arial,sans-serif;text-shadow: 0 1px 1px rgba(255,255,255,0.9);} 4 | #popup_container .popup_notification a {color: #21759b;} 5 | #popup_container .popup_notification .title {display:block;font-weight: 800;font-size: 13px;line-height: 22px;} 6 | #popup_container .popup_notification.update {background-color: rgb(223, 242, 191);color: rgb(70, 136, 71);border:1px solid rgb(70, 136, 71);} 7 | #popup_container .popup_notification.info {background-color: rgb(189, 229, 248);color: rgb(0, 82, 155);border:1px solid rgb(0, 82, 155);} 8 | #popup_container .popup_notification.warning {background-color: rgb(254, 239, 179);color: rgb(159, 96, 0);border:1px solid rgb(159, 96, 0);} 9 | #popup_container .popup_notification.error {background-color: rgb(255, 186, 186);color: rgb(216, 0, 12);border:1px solid rgb(216, 0, 12);} 10 | #popup_container .popup_notification .close {position:absolute;top:-5px;right:0;padding: 6px 7px;cursor:pointer;font-size:12px;font-weight:bold;} -------------------------------------------------------------------------------- /wp-heartbeat-notify/js/wp-heartbeat-notify.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready( function($) { 2 | 3 | function send_popup( title, text, popup_class, delay ) { 4 | 5 | // Initialize parameters 6 | title = title !== '' ? '' + title + '' : ''; 7 | text = text !== '' ? text : ''; 8 | popup_class = popup_class !== '' ? popup_class : 'update'; 9 | delay = typeof delay === 'number' ? delay : 20000; 10 | 11 | var object = $('
', { 12 | class: 'popup_notification ' + popup_class, 13 | html: title + text + '×' 14 | }); 15 | 16 | $('#popup_container').prepend(object); 17 | 18 | $(object).hide().fadeIn(500); 19 | 20 | setTimeout(function() { 21 | 22 | $(object).slideUp(500); 23 | 24 | }, delay); 25 | 26 | } 27 | 28 | $('
', { id: 'popup_container' } ).appendTo('body'); 29 | $('body').on('click', '.close', function () { $(this).parent().slideUp(200); }); 30 | 31 | var blabla; 32 | 33 | // Bum bum 34 | $(document).on( 'heartbeat-tick.my_tick', function( e, data ) { 35 | 36 | // To understand better how it works just uncomment following lines and give a look at browser console 37 | // send_popup('tik tak'); 38 | // console.log(data); 39 | 40 | if ( !data['message'] ) 41 | return; 42 | 43 | $.each( data['message'], function( index, notification ) { 44 | 45 | if ( index != blabla ){ 46 | 47 | send_popup( notification['title'], notification['content'], notification['type'] ); 48 | 49 | } 50 | blabla = index; 51 | 52 | } ) ; 53 | 54 | 55 | }); 56 | 57 | }); -------------------------------------------------------------------------------- /wp-heartbeat-notify/js/wp-heartbeat-notify.min.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function($){function send_popup(a,b,c,d){a=a!==''?''+a+'':'';b=b!==''?b:'';c=c!==''?c:'update';d=typeof d==='number'?d:20000;var e=$('
',{class:'popup_notification '+c,html:a+b+'×'});$('#popup_container').prepend(e);$(e).hide().fadeIn(500);setTimeout(function(){$(e).slideUp(500)},d)}$('
',{id:'popup_container'}).appendTo('body');$('body').on('click','.close',function(){$(this).parent().slideUp(200)});var f;$(document).on('heartbeat-tick.my_tick',function(e,c){if(!c['message'])return;$.each(c['message'],function(a,b){if(a!=f){send_popup(b['title'],b['content'],b['type'])}f=a})})}); -------------------------------------------------------------------------------- /wp-heartbeat-notify/langs/wp-heartbeat-notify-it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micc83/wp-heartbeat-notify/83a629fabe76be6a299ba2eaf7f9f10395782e6a/wp-heartbeat-notify/langs/wp-heartbeat-notify-it_IT.mo -------------------------------------------------------------------------------- /wp-heartbeat-notify/langs/wp-heartbeat-notify-it_IT.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Wp Heartbeat Notify\n" 4 | "POT-Creation-Date: 2013-06-19 23:58+0100\n" 5 | "PO-Revision-Date: 2013-06-19 23:58+0100\n" 6 | "Last-Translator: Comodolab \n" 7 | "Language-Team: codeb.it \n" 8 | "Language: Italiano\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "X-Generator: Poedit 1.5.4\n" 13 | "X-Poedit-KeywordsList: _e;__\n" 14 | "X-Poedit-Basepath: .\n" 15 | "X-Poedit-SourceCharset: UTF-8\n" 16 | "X-Poedit-SearchPath-0: ..\n" 17 | 18 | #: ../wp-heartbeat-notify.php:41 19 | msgid "New Article" 20 | msgstr "Nuovo articolo" 21 | 22 | #: ../wp-heartbeat-notify.php:42 23 | msgid "There's a new post, why don't you give a look at" 24 | msgstr "C'è un nuovo articolo, perchè non dai un'occhiata a" 25 | 26 | #: ../core/class-my-plugin.php:99 27 | #, php-format 28 | msgid "This plugin requires WordPress %s or greater." 29 | msgstr "Questo plugin necessita di WordPress %s o superiore" 30 | 31 | #: ../core/class-my-plugin.php:103 32 | #, php-format 33 | msgid "This plugin requires PHP %s or greater." 34 | msgstr "Questo plugin necessita di PHP %s o superiore" 35 | -------------------------------------------------------------------------------- /wp-heartbeat-notify/screenshot-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micc83/wp-heartbeat-notify/83a629fabe76be6a299ba2eaf7f9f10395782e6a/wp-heartbeat-notify/screenshot-1.jpg -------------------------------------------------------------------------------- /wp-heartbeat-notify/wp-heartbeat-notify.php: -------------------------------------------------------------------------------- 1 | WordPress 3.6 heartbeat API, Wp Heartbeat Notify, display a realtime custom message to your visitor each time a new post is published with a link redirecting to it. Still in beta version, this plugin has been full tested only on WordPress 3.6-beta3. 6 | Version: 0.0.1 7 | Author: Alessandro Benoit 8 | Author URI: http://codeb.it 9 | License: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 10 | */ 11 | 12 | require_once( 'core/class-my-plugin.php' ); 13 | require_once( 'core/class-wp-heartbeat-notify.php' ); 14 | 15 | global $my_plugin; 16 | 17 | // Create the plugin and store it as a global 18 | $my_plugin = new My_Plugin( 19 | __FILE__, 20 | array( 21 | 'required' => array( 22 | 'wordpress' => '3.6' // WordPress 3.6 is required 23 | ) 24 | ) 25 | ); 26 | 27 | // Instantiate Heartbeat notifications 28 | add_action( 'init', 'initialize_wp_heartbeat_notify' ); 29 | function initialize_wp_heartbeat_notify () { 30 | 31 | global $my_plugin; 32 | 33 | new Wp_Heartbeat_Notify( array( 34 | 'context' => array( 'front' ), // This plugin is supposed to work only on the front end 35 | 'base_url' => $my_plugin->uri // Set js and css base url 36 | )); 37 | 38 | } 39 | 40 | 41 | // Let's hook into Post publication 42 | add_filter ( 'publish_post', 'notify_published_post' ); 43 | function notify_published_post( $post_id ) { 44 | 45 | global $my_plugin; 46 | 47 | // That's it. Easy... isn'it? 48 | Wp_Heartbeat_Notify::notify( array( 49 | 'title' => __( 'New Article', $my_plugin->textdomain ), 50 | 'content' => __( 'There\'s a new post, why don\'t you give a look at', $my_plugin->textdomain ) . 51 | ' ' . get_the_title( $post_id ) . '', 52 | 'type' => 'update' 53 | ) ); 54 | 55 | return $post_id; 56 | 57 | } 58 | --------------------------------------------------------------------------------