Going wild with WordPress notifications

It is a nice security feature to receive a notification on my phone when someone logs in to my WordPress website

You want to receive a notification on you Android device, when someone logs in on your WordPress site? Perhaps as a security measurement?

In this post we will build a system that allows you to do so. In the first part of the post I will guide you though the WordPress side of this and in the second part I will show you how to create a very basic Android app that receives the notifications. At the end we will have two separate projects:

  • A firebase-actions plugin for WordPress
  • A Firebase Actions Android app that can receive the notifications

Note that in case you are more interested in developing an iOS app this shouldn’t be a problem, the steps for iOS should be almost the same as those for Android.

The application will be secured by using a server token to communicate with Firebase and on the Android side, only apps signed with your certificate will be able to register for cloud messages.

All code for this post is available on github:

  1. WordPress plugin: https://github.com/nickmartens/firebase-actions-wordpress.
  2. Android App: https://github.com/nickmartens/firebase-actions-android

Part 1: Creating a WordPress plugin

The first step is creating a WordPress plugin that can receive the events as they happen. As you may know, WordPress is written is PHP. Unfortunately PHP is not my most fluent languages, but sending a simple HTTP message to Firebase should not be too much of a problem. So, let’s get started!

Connecting to Firebase

First things first, to connect to firebase we first need to set up Firebase cloud messaging. Which is not that hard. The first step is to create a Firebase account at firebase.google.com. Next, you need to go to the Firebase console and get your server key and sender id. We will need these later when the plugin is ready to use.

Creating the WordPress plugin

To create a new plugin we simply need to create a new folder in WordPress’ plugins folder. I named mine firebase-actions. The one thing we need in there is a firebase-actions.php file that acts as the entry point of the plugin and is the file WordPress will load. This php file has the same name as the folder containing it.

The next step is to connect to interesting Wordpress hooks and send events to Firebase as these events happen. For the first implementation I choose five hooks that could be interesting. These hooks are login, authenticate, save_post, publish_post and publish page. So in that file, add the following code:

add_action( 'wp_login', __NAMESPACE__ . '\\fa_init_wp_login', 10, 2 );
add_action( 'wp_authenticate', __NAMESPACE__ . '\\fa_init_wp_authenticate' );
add_action( 'save_post', __NAMESPACE__ . '\\fa_init_save_post' );
add_action( 'publish_post', __NAMESPACE__ . '\\fa_init_publish_post', 10, 2 );
add_action( 'publish_page', __NAMESPACE__ . '\\fa_init_publish_page', 10, 2 );

To clarify, The first line connects the ‘wp_login‘ event, to the fa_init_wp_login function. So whenever there is a login event, the fa_init_wp_login function will be called. 10 is the priority of this connection and 2 tells WordPress that we would like to receive two parameters from the login call.

The implementation of the callback looks like this:

function fa_init_wp_login( $user_login, $user ) {
   _do_post( "login", 'User: ' . $user_login . ' logged in', $user_login, null );

So the only thing it really does is forward the event to the _do_post method. This method does the actual work:

function _do_post( $refPath, $title, $message, $url ) {
    $options = get_option( 'fa_options' );
    $server_key = $options['server_key'];

    $data = [
        'title' => print_r( $title, true ),
        'body' => print_r( $message, true ),
        'url' => print_r( $url, true ),
        'request_time' => print_r( $_SERVER['REQUEST_TIME'], true ),
        'remote_addr' => print_r( getenv( 'REMOTE_ADDR' ), true ),
        'forwarded_for' => print_r( getenv( 'HTTP_FORWARDED_FOR' ), true )

    $topic = '/topics/' . $refPath;

    $body = [
        'data' => $data,
        'to' => $topic

    $headers = array
        'Authorization: key=' . $server_key,
        'Content-Type: application/json'

    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, '//fcm.googleapis.com/fcm/send' );
    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( $body ) );
    $result = curl_exec( $ch );
    curl_close( $ch );
    error_log( "fcm result: " . $result );

As you can see, this method connects to Firebase, using the server key. Next it sends the message and it adds a few more details about the request. Perhaps the most interesting part is the way the data is composed before it is sent to the server. Firebase requires your data message to be strings only. For that reason I print_r($var, true) all of the values. This ensures every value is sent as a string. That is all we need to be able to send an event to Firebase.

As you can see, the $server_key variable get initialized from the options. But for this to work, we actually need to create an options page so we can save the configuration options within the WordPress system.

Creating a configuration page

A configuration page makes it easier to dynamically configure the plugin. Most of the details about configuration pages are out of scope for this post. You can find everything related to doing this in the tutorial to create option pages at the WordPress Codex. I will highlight the interesting bits.

To get your admin page in the admin section, you need to register two callbacks. Like this:

add_action( 'admin_menu', array( $this, 'add_plugin_page' ) );
add_action( 'admin_init', array( $this, 'page_init' ) );

This is again just a simple add_action call, just like registering the hooks for the interesting events. The next step is configuring the settings page. The full implementation looks like this:

class FirebaseSettingsPage {
	 * Holds the values to be used in the fields callbacks
	private $options;

	 * Start up
	public function __construct() {
		add_action( 'admin_menu', array( $this, 'add_plugin_page' ) );
		add_action( 'admin_init', array( $this, 'page_init' ) );

	 * Add options page
	public function add_plugin_page() {
		// This page will be under "Settings"
			'Firebase Actions Admin',
			'Firebase Actions',
			array( $this, 'create_admin_page' )

	 * Options page callback
	public function create_admin_page() {
		// Set class property
		$this->options = get_option( 'fa_options' );
<div class="wrap">
<h1>Firebase Actions</h1>
<form method="post" action="options.php">
				<?php // This prints out all hidden setting fields 
                settings_fields( 'my_option_group' ); 
                do_settings_sections( 'my-setting-admin' ); 
                submit_button(); ?>

	 * Register and add settings
	public function page_init() {
			'my_option_group', // Option group
			'fa_options', // Option name
			array( $this, 'sanitize' ) // Sanitize

			'setting_section_id', // ID
			'Firebase Actions Settings', // Title
			array( $this, 'print_section_info' ), // Callback
			'my-setting-admin' // Page

			'Server key',
			array( $this, 'server_key_callback' ),

			'sender_id', // ID
			'Sender id', // Title
			array( $this, 'sender_id_callback' ), // Callback
			'my-setting-admin', // Page
			'setting_section_id' // Section

	 * Sanitize each setting field as needed
	 * @param array $input Contains all settings fields as array keys
	public function sanitize( $input ) {
		$new_input = array();
		if ( isset( $input['sender_id'] ) ) {
			$new_input['sender_id'] = sanitize_text_field( $input['sender_id'] );

		if ( isset( $input['server_key'] ) ) {
			$new_input['server_key'] = sanitize_text_field( $input['server_key'] );

		return $new_input;

	 * Print the Section text
	public function print_section_info() {
		print 'Configure your settings below:';
		$options = get_option( 'fa_options' );

		if ( ! $options ) {
			print '
<b>Warning:</b> No configuration found. You need to set the server key and sender id first';

		$server_key = $options['server_key'];
		$sender_id  = $options['sender_id'];

		if ( ! $server_key || ! $sender_id ) {
			print '
<b>Warning:</b> No configuration found. You need to set the server key and sender id first';

	 * Get the settings option array and print one of its values
	public function sender_id_callback() {
			'<input type="text" id="sender_id" name="fa_options[sender_id]" value="%s" />',
			isset( $this->options['sender_id'] ) ? esc_attr( $this->options['sender_id'] ) : ''

	 * Get the settings option array and print one of its values
	public function server_key_callback() {
			'<textarea id="server_key" name="fa_options[server_key]">%s</textarea>',
			isset( $this->options['server_key'] ) ? esc_attr( $this->options['server_key'] ) : ''

The interesting bits are: the page_init method and the sanitize method. The first method adds the additional settings fields to the screen. The second method cleans up the user input and returns the cleaned up array, which is saved by WordPress.

The generated page then looks like this:

Configuration page

That is all for the WordPress plugin!

Part 2: The Android App

Now that we have a plugin that can send the data to the Firebase server, the next step it to create a simple Android App that receives the notifications from Firebase.

Connect to Firebase

The first part is to connect the App to Firebase. You can do this from the tools -> Firebase menu in Android Studio. Just open the cloud messaging part and follow the instructions on the screen.

The setup screen should look like this:

Firebase assistent

Once that is done, and the dependencies are set we can add the code to receive the messages. In case you run into problems linking the App, you need to download the google-services.json from the Firebase console and replace the one in your Android project.

Registering for notifications

Update the generated MainActivity with the code to register to the topics.

public class MainActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState) {


This is all that is needed to register to Firebase. The topics are the same topics we used in the WordPress plugin:

    _do_post( "login", 'User: ' . $user_login . ' logged in', $user_login, null );

That first parameter in the _do_post call, is the topic to post to.

Handling notifications

Notifications are received by a FirebaseMessagingService. If you followed the instructions when you connected to Firebase in Android Studio, you should have created such a class.

My implementation looks like this:

public class MessageService extends FirebaseMessagingService {

    private static final String TAG = "MessageService";

    public void onMessageReceived(RemoteMessage remoteMessage) {

        String from = remoteMessage.getFrom();

        // Check if message contains a data payload.
        Map<String, String> data = remoteMessage.getData();
        if (data.size() > 0) {
            Log.d(TAG, "Message data payload: " + data);
            switch (from) {
                case "/topics/log":
                case "/topics/login":
                case "/topics/new_page":

        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());


    private void onNewPageMessage(Map<String, String> data) {
        String title = data.get("title");
        if (!TextUtils.isEmpty(title)) {
            String body = data.get("body");
            Notification notification = new NotificationCompat.Builder(this)
                    .setGroup("Warmbeer blog")
                    .setStyle(new NotificationCompat.BigTextStyle()
            showNotification(5, notification);

    // More methods handling the other topics

    private void showNotification(int id, Notification notification) {
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.notify(id, notification);
        Notification group = new NotificationCompat.Builder(this)
                .setGroup("Warmbeer blog")
        nm.notify(12, group);

When a message is received is the onMessageReceived method will be called for you. The first thing we do there, is getting a hold of the topic this message was sent to. With the topic we can determine what data the message contains and extract that data to create a notification.

The code to generate the new_page notification in the WordPress plugin is as follows:

function fa_init_publish_page( $ID, $post )
    $author = $post->post_author; /* Post author ID. */
    $name = get_the_author_meta( 'display_name', $author );
    $title = $post->post_title;
    $permalink = get_permalink( $ID );
    $subject = sprintf( 'New page published: %s', $title );
    $message = sprintf( '%s published a new page: “%s”.', $name, $title );
    $message .= sprintf( 'View: %s', $permalink );
    _do_post( 'new_page', $subject, $message, $permalink );

As you can see, the more data we add to the notification, the more we can use in the app. After processing the data, the notification looks like this:

What’s next?

Now that we can post notifications from WordPress, it may also be useful to post information from the server itself. Like for example the output of certain cron jobs.

For example, let’s say we have an SSL certificate from let’s encrypt on the server, that we try to update every week. It would be useful to know the output of the command. From a weekly cron job we can call a script like this:

/usr/bin/letsencrypt renew | tee -a /var/log/le_renew.log | post-out-to-firebase.sh

As you can see the output is sent to another script that deals with posting the output to the Firebase server. This script looks like this:


jq -n --arg message "$output" \
      --arg topic "ssl" \
   '{to: "/topics/log", data: { topic: $topic, message: $message}}'|
curl -H "Content-Type: application/json" \
   -H 'Authorization: key=SERVER_SECRET_HERE' \
   -X POST \
   -d@- \

By using this setup my WordPress server sends me the output of the certificate renewal call every time it tries to renew. This is a very useful way to track the status of my certificate. This could be used for tons of other things as well. For example checking if a reboot is required because of an update, or to send me the output of certain other cron-jobs.

As you can see it was very easy to connect WordPress events to an app, and creating a simple WordPress plugin is really simple as well. Right now I created an Android app, but as noted before, creating an iOS app with this would also be quite easy. Firebase has support for iOS, so I can imagine it would also not be too difficult to create an iOS app for this. I am really interested to hear what else you can build with this!

Cheers, Nick!

3 Replies to “Going wild with WordPress notifications”

  1. Hi, Nick, thank you very much for this nice tutorial. I followed these steps, and everything worked fine. I developed an app, and published it to PlayStore. Everything worked fine during testing (posting test posts on a production website), but since I published it, notifications started giving me problems.

    Notifications usually don’t get recieved, only sometimes (1 out of 10 times), users recieve notification. First i thought the problem was >4KB size, so i shortened the content of the post to 60 characters before sending it to Firebase, and it worked 2 times, and the next time I had to update post 15 times before it send a notification.

    On the Android side, everything is okay – it shows the notifications the way i programmed it.

    Do you have any idea what could be the problem, it’s an app I got payed for, and it is not working the way it should. I really need to fix this. Thank you

    1. I just used exit($result) after executing curl, and it said that JSON failed.. I am sending Cyrilic text, so i found out that i had to utf_8_encode strings
      But now my app is showing wierd symbols.

      1. Hey Zeljko, Thank you for your remarks! Definitely sounds like an encoding issue. I will look into this and post an answer when I find one.

Leave a Reply

Your email address will not be published. Required fields are marked *