X

Chrome Push Notifications Example Of Implementation

Chrome push notifications are one of the newer features added into Chrome browser. They are used to show notifications outside the web page context even if the user is not browsing the page he subscribed to.

Before we continue, make sure that you use  https or implement the code on localhost because service workers require secure origins to ensure that the service worker script is from the intended origin and hasn’t come about from a man-in-the-middle attack.

Example of implementation for Chrome push notifications:

As of Chrome version 42, the Push API and Notification API are available to developers.

The Push API in Chrome relies on a few different pieces of technology, including Web App Manifests and Service Workers. To get a better understanding of some of the other features of manifests and the offline capabilities of service workers, please check out the links above.

A simple code example can be found on the GoogleChrome Samples Repo.

In our example, notifications work as follows:

User enters the site and browses a category. In javascript we check to see if there is a cookie set with the name “category_notifications_ID” where “ID” is the id of our category in wordpress.

If there is no cookie set with name “category_notifications_ID” we open a modal window where we ask the user if he wants to receive notifications when a new post is added in that specific category.

var category_cookie = 'notification_category_'+cat_id;
	if(!getCookie(category_cookie)){ //If there is no cookie set, open modal
		setTimeout(function(){
			$('#notifications-signup').modal('show');	
		}, 1000)
		
	}

Here we have two choices, No thanks and Accept. Let’s talk about what happens in both cases depending of what the user chooses:

No thanks
In this case we just set a cookie with name “category_notifications_ID” and expire date set to 7 days.

 

Accept
In this case user will be asked by chrome browser to allow or block notifications from this website.

 

Chrome notification request:

 

Allow

If user accepts notifications, we then call the function Chrome Push Manager to register service worker with name service-worker_ID.js, obtain device id and register it.In this case, we use an ajax function to run some php code to store into database register id, category id, and the date of the registration id. This notification popup from chrome appears only one time. If everything works well, we then show to user a success notification and set a cookie with the name “category_notifications_ID” and expire date set to 1 year so that the next time when the user gets to this category  we will not show the popup to allow notifications.

Sample code:

//Category id
var cat_id = $('#notifications-signup').attr('data-categoryid');
//Category name
var cat_name = $('#notifications-signup').attr('data-categoryname');

//Set cookie
function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires="+d.toUTCString();
    document.cookie = cname + "=" + cvalue + "; " + expires + "; path=/";
}

//Get cookie by name
function getCookie(name) {
	var nameEQ = name + "=";
	//alert(document.cookie);
	var ca = document.cookie.split(';');
	for (var i = 0; i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0) == ' ')
		c = c.substring(1);
		if (c.indexOf(nameEQ) != -1)
			return c.substring(nameEQ.length, c.length);
	}
	return null;
}
//Cookie name
	var category_cookie = 'notification_category_'+cat_id;
	if(!getCookie(category_cookie)){ //If there is no cookie set, open modal
		setTimeout(function(){
			$('#notifications-signup').modal('show');	
		}, 1000)
		
	}
	
	//Set cookie on decline or close modal
	$('#notifications-signup .buttons .decline, #notifications-signup .close').click(function(e){
		e.preventDefault();
		//Set cookie to no thanks and expire in 7 days
		setCookie(category_cookie, 0, 7);	
	});
	
	$('#notifications-signup .buttons a.accept').click(function(e){
		e.preventDefault();
		// start Chrome Push Manager to obtain device id and register it
	    // a service worker will be launched in background to receive the incoming push notifications
	    var chromePushManager = new ChromePushManager('/blog/service-worker_'+cat_id+'.js', function(error, registrationId){
	    	$.ajax({
	            type: 'POST',
	            dataType: 'json',
	            url: WPaAjax.ajaxurl,
	            data: { 
	                'action': 'notifications_subscribe',
	                'cat_id' : cat_id,
	                'reg_id' : registrationId
	                },
	        	success: function(data){
	        		console.log(data);
	        		if (data.error==0) {
	        			//Set cookie to accept and expire date to 1 year
	        			setCookie(category_cookie, 1, 365);
	        			//Add some styling, hide close button and add success message
	        			$('#notifications-signup .close, #notifications-signup .modal-body').hide();  
	        			$('#notifications-signup .modal-header').css('padding', '15px')
	        			$('#notifications-signup .modal-header h4').html('You just got in on a good thing! You\'ll receive notifications on exciting articles about '+cat_name+'')
	        			//Hide popup after 2 seconds
	        			setTimeout(function(){
	        				$('#notifications-signup').modal('hide');	
	        			}, 2000)
	        			
	        		} else{
	        			$('#notifications-signup').modal('hide');
	        		}       		     		
	        	}
	        });	
	      
	      if (error) {
	        alert(error);
	      };
	    });	
	});

Chrome Push Manager sample code:

var ChromePushManager = function(serviceWorkerPath, callback){
	if ('serviceWorker' in navigator) {
		navigator.serviceWorker.register(serviceWorkerPath, {scope: './'})
		.then(ChromePushManager.initialiseState(callback));
	} else {
		callback('Service workers aren\'t supported in this browser.', null);
	}
}

ChromePushManager.initialiseState = function (callback) {  
  // Are Notifications supported in the service worker?  
  if (!('showNotification' in ServiceWorkerRegistration.prototype)) {  
    callback('Notifications aren\'t supported.', null);  
  } else if (Notification.permission === 'denied') {  
    callback('You have blocked notifications for this website. Please unblock them to get our notifications.', null);  
  } else if (!('PushManager' in window)) {  
    callback('Push messaging isn\'t supported.', null);  
  } else {
  	ChromePushManager.subscribeBrowserId(callback);
  }
}

ChromePushManager.subscribeBrowserId = function(callback) { 
  navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {  
    serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true})  
      .then(function(subscription) {
        var register = ChromePushManager.getRegistrationId(subscription);
        callback(null, register);
      })  
      .catch(function(e) {  
        if (Notification.permission === 'denied') {
          callback('Permission for Notifications was denied', null);
        } else {  
          callback('Unable to subscribe to notifications.', null);  
        }  
      });  
  }); 
}

ChromePushManager.getRegistrationId = function(pushSubscription) {
  if (pushSubscription.subscriptionId) {
    return pushSubscription.subscriptionId;
  }

  var endpoint = 'https://android.googleapis.com/gcm/send/';
  parts = pushSubscription.endpoint.split(endpoint);

  if(parts.length > 1)
  {
    return parts[1];
  }

}

 

Block

In this case we just set a cookie with name “category_notifications_ID” and expire date set to 1 year as well and dismiss the popup.

 

 

Send notifications to users when we post a new article

First we  add an action on publish_post in functions.php, so everytime we hit the “Publish” button, it will run our code too as following:

  • check if the modified date is equal to post date. This means that the post is a new entry, not an updated one.
  • get all categories that this post is part of
  • make a foreach through all categories and get registration ids into an array based on category
  • run function “send_gcm_notify()” for each category and send notifications to registered users ids. This function will send a notification to Google Cloud Messaging which will trigger our notification via the service worker registered on your browser.

Sample of send_gcm_notify() function:

//Send Google cloud notification
define("GOOGLE_API_KEY", "YOUR_API_KEY_HERE");
define("GOOGLE_GCM_URL", "https://android.googleapis.com/gcm/send");

function send_gcm_notify($reg_ids, $message) {
 
    $fields = array(
		'registration_ids'  => $reg_ids,
		'data'              => array( "message" => $message ),
	);
				
	$headers = array(
		'Authorization: key=' . GOOGLE_API_KEY,
		'Content-Type: application/json'
	);
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, GOOGLE_GCM_URL);
	curl_setopt($ch, CURLOPT_POST, true);
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));

	$result = curl_exec($ch);
	if ($result === FALSE) {
		die('Problem occurred: ' . curl_error($ch));
	}
	curl_close($ch);
 }

Service Worker Push Event Listener

When a push message is received, a push event will be dispatched in your service worker we registered earlier for a specific category.

Sample of service-worker.js:

// The service worker running in background to receive the incoming push notifications 
// A push has arrived ...
self.addEventListener('push', function(event) {
  // Since there is no payload data with the first version  
  // of push messages, we'll use some static content. 
  // However you could grab some data from  
  // an API and use it to populate a notification

  console.log('Received a push message', event);

  var title = 'Design19 Blog';  
  var body = 'HEY! You\'ve got new unread articles on Web Design on Design19 Blog!';  
  var icon = 'design19.jpg'; 

	self.registration.showNotification(title, {  
	  body: body,  
	  icon: icon
	})  
});


// The user has clicked on the notification ...
self.addEventListener('notificationclick', function(event) {  
  // Android doesn't close the notification when you click on it  
  event.notification.close();

  // This looks to see if the current is already open and  focuses if it is  
  event.waitUntil(
    clients.matchAll({  
      type: "window"  
    })
    .then(function(clientList) {  
      for (var i = 0; i < clientList.length; i++) {  
        var client = clientList[i];  
        if (client.url == '/' && 'focus' in client)  
          return client.focus();  
      }  
      if (clients.openWindow) {
        return clients.openWindow('https://www.design19.org/blog/category/web-design/');  
      }
    })
  );
});

 

This is it…enjoy engaging with your users! If you have any questions or suggestions, do not hesitate to write a comment below.

Update (Send Chrome Push Notifications with custom text and image)

In this update we will show you how to implement the notifications to send custom text to your subscribers.
In our case we are sending the title of the newely published article as notification title, body as notification description, the featured image of the article as an icon of our notification and the permalink of our article so when user clicks the notification, it will open up a page with the new article.

We are doing this in 2 steps, so let’s start.

1. Add a file named json-data.php to the root, next to wp-config.php file

Sample code of json-data.php

<?php
require_once( dirname(__FILE__) . '/wp-load.php' );

header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
$args = array(
    'numberposts' => 1,
    'order' => 'DESC',
    'post_type' => 'post',
    'post_status' => 'publish');

$latest_post = wp_get_recent_posts( $args, ARRAY_A );

$post_id = $latest_post[0]['ID'];

$image_src = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'notification_thumb' );
if (!empty($image_src)) :
	$image_src = $image_src[0];
else:
	$image_src = 'design19.jpg';
endif;

$post = get_post($post_id); 
$content = htmlspecialchars($post->post_content);

$myArray = array('notification'=>array('url'=>get_the_permalink($post_id),'title'=>get_the_title($post_id), 'message'=>$content, 'tag'=>'same', 'icon'=>$image_src));
$myJSONString = json_encode($myArray);
echo $myJSONString;
?>

Let’s talk about the content of this file and why we are using it.

  • Require wp-load.php so you can have access to all wordpress classes and functions.
  • Set json headers
  • Get the latest post id
  • Get the content of the last post (Title, body, featured image, permalink) and put all data intto an array
  • Use json_encode() function to encode your array in JSON and then print it

So basically in this file we have all the data we need for our notification in json format.

2. Edit your service-worker.js file so it will take all the data from the json file we previously created in step 1

Sample code of service-worker.js

 var url = "path/to/your/json/file/json-data.php?param="+Math.random();
 self.addEventListener('push', function(event) {    
  event.waitUntil(  
    fetch(url).then(function(response) {  
      if (response.status !== 200) {  
        // Either show a message to the user explaining the error  
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page  
        console.log('Looks like there was a problem. Status Code: ' + response.status);  
        throw new Error();  
      }

      // Examine the text in the response  
      return response.json().then(function(data) {  
        if (data.error || !data.notification) {  
          console.log('The API returned an error.', data.error);  
          throw new Error();  
        }  
        var title = data.notification.title;  
        var message = data.notification.message;  
        var icon = data.notification.icon;  

        return self.registration.showNotification(title, {  
          body: message,  
          icon: icon,  
          data: {
            url: data.notification.url
          }  
        });  
      });  
    }).catch(function(err) {  
      console.log('Unable to retrieve data', err);

      var title = 'An error occurred';
      var message = 'We were unable to get the information for this push message';  
      var icon = 'img/design19.jpg';  
      var notificationTag = 'notification-error';  
      return self.registration.showNotification(title, {  
          body: message,  
          icon: icon,  
          tag: notificationTag  
        });  
    })  
  );  
});

// The user has clicked on the notification ...
self.addEventListener('notificationclick', function(event) {  
  console.log(event.notification.data.url);
  // Android doesn't close the notification when you click on it  
  // See: http://crbug.com/463146  
  event.notification.close();

  // This looks to see if the current is already open and  
  // focuses if it is  
  event.waitUntil(
    clients.matchAll({  
      type: "window"  
    })
    .then(function(clientList) {  
      for (var i = 0; i < clientList.length; i++) {  
        var client = clientList[i];  
        if (client.url == '/' && 'focus' in client)  
          return client.focus();  
      }  
      if (clients.openWindow) {
        return clients.openWindow(event.notification.data.url);  
      }
    })
  );
});

Don’t forget to change the path to your .json file.  In this case we added the “?param=”+Math.random()” parameter to our url just to be shure that it will always get the new json and it will not cache it.

The new service worker file works like this:

  • When a notification is received, it will try to fetch the json data from the file we previously talked about
  • If all is ok it will grab the json data in a “data” variable and there you can access your data from the json file.

Note:
Until a subscriber to your notifications returns to your website, the service-worker.js file registered on his browser it will be the one registered at the begining. When visiting your website again, service worker will update itself to the newer version.

 

 

Bogdan Rusu: Working in development for more then 10 years, Bogdan Rusu is the CTO of Design19 web agency. Highly skilled in PHP development and usage of CMS like Wordpress, Magento, Zend framework, but also custom built platforms based on PHP, Bogdan has driven the team to a higher level of proficiency in development.

View Comments

  • Hello, I am getting an error while sending the curl request, Error 401 Unauthorized. The service worker is successfully registered. The API key I am using is unrestricted. I am using localhost. Any idea why this might be happening?

  • Hi,
    Thanks for sharing info.
    I am looking for , how we send payload/ data with push notification to client end.