Writing trivial extensions is not too hard

A client recently sought me out. A board user wanted to get email notifications when receiving private messages. These are disabled by default but can be enabled. Most users though won’t dig through the User Control Panel to find the setting: UCP > Board preferences > Edit notification options > Someone sends you a private message > Email.

The client wanted all his board users to have this enabled by default. Not one to ever reinvent the wheel, I searched for an extension. I found this one, in release candidate status, which allows an administrator to tweak notifications for a user on a case by case basis. But that didn’t meet the requirement.

I wasn’t sure how this setting was stored in the database. I did some experimentation by turning it on and off and looking in the phpbb_user_notifications table using phpMyAdmin. This provided a clue on what to do.

Private message email notification setting for user in the database

Essentially if I could add rows like this to this table, I’d effect the change needed. There were two aspects to this:

  • Ensure existing users has this setting set
  • Ensure that new users had this setting applied automatically with registration

If you know Structured Query Language (SQL) pretty well, it wasn’t that hard to figure out the SQL needed to ensure existing users who didn’t have this setting applied had it applied. This amounted to adding rows to the phpbb_user_notifications table for those who didn’t have a row for this permission already. The SQL was little complex but based on what I learned from testing in phpMyAdmin, straightforward and turned out to be:

INSERT INTO phpbb_user_notifications
SELECT ‘notification.type.pm’, 0, user_id, ‘notification.method.email’, 1
FROM phpbb_users
WHERE user_id not in
(SELECT user_id FROM phpbb_user_notifications WHERE item_type = ‘notification.type.pm’ and method = ‘notification.method.email’)
AND user_type = 0;

Having it work automatically with new registrations though was more complicated. Initially I didn’t consider the idea of writing a phpBB extension to do this. Rather, I proposed writing a SQL trigger to do this instead. But then I realized writing an extension would probably not be that big a deal and would have the advantage that if the user was deleted, phpBB’s code would delete these user notifications at the same time.

Writing extensions for phpBB is complex the first few times you try, as I discovered. The phpBB Group has this Wiki you can read to see if you can get your head around it. You need to be able to program in PHP but really you also generally need related web stills in HTML, CSS and Javascript too.

The more extensions I write though, the more I realize a lot of rather simple functionality can be done with rather trivial extensions. There is a skeleton extension you can install that will generate a lot of boilerplate code that makes it relatively easy to add the specialized functionality you need. I placed this on my test board and simply filled out the fields needed. Once installed a link appears on the navigation bar. The most baffling part for new extension authors is figuring out which components to check. Much of this comes from experience, but hovering over the component with your mouse helps explain that the component will do for you.

phpBB Skeleton extension interface

In this case, all I really needed was to check the PHP event listener and service checkboxes. This was for a client so it was not for general release, so I didn’t need to worry about database migrations. I didn’t need to add any Administration Control Panel user interface. It was to happen behind the scenes. When the form is submitted it creates a nice .zip file which can be decompressed and uploaded to your development environment. I enabled the extension.

What I needed to do was to add some logic during user registration. But where? And how? phpBB is all open source, so it’s a matter of reading the code. If you read the code, you realize that at some point the function user_add in /includes/functions_user.php is called and it does the grunt work of adding new users to lots of tables.

Inspecting the user_add function I found inside it a number of events that could be hooked into by my extension. I found the core.user_add_after event at the bottom of the function that looked like it would work. Essentially, after phpBB does all the normal stuff to create a user, you can have it do extra work. The event documentation shows:

/**
* Modify the notifications data to be inserted in the database when a user is added
*
* @event core.user_add_modify_notifications_data
* @var array    user_row            Array of user details submitted to user_add
* @var array    cp_data             Array of Custom profile fields submitted to user_add
* @var array    sql_ary             Array of data to be inserted when a user is added
* @var array    notifications_data  Array of notification data to be inserted when a user is added
* @since 3.2.2-RC1
*/
$vars = array('user_row', 'cp_data', 'sql_ary', 'notifications_data');
extract($phpbb_dispatcher->trigger_event('core.user_add_modify_notifications_data', compact($vars)));

The skeleton extension I used created a main_listener.php program in the /event folder where you can hook in code for events like these. To insert a row into the phpbb_users table, I would need to know the user_id that was created. The event passes a user_row array, so it seemed likely the user_id would be inside of it.

Since I was going to access the database, I needed main_listener.php to be able to use phpBB’s database abstraction layer service. So first I went into the /config/services.yml file for the extension and added this service. This was straightforward. Since I didn’t need to use the language service, I removed the language service too. The file became:

services:
phpbbservices.setemailpmnotifications.listener:
class: phpbbservices\setemailpmnotifications\event\main_listener
arguments:
- '@dbal.conn'
tags:
- { name: event.listener }

Next, to hook the database abstraction layer service into main_listener.php. I removed any references to the language service first, then hooked in the database service. The main_listener class looked in part like this:

public static function getSubscribedEvents()
{
   return [
      'core.user_add_after'           => 'user_add_enable_pm_email_notifications'
   ];
}

/* @var \phpbb\db\driver\factory */
protected $db;

/**
 * Constructor
 *
 * @param \phpbb\language\language $language  Language object
 */
public function __construct(\phpbb\db\driver\factory $db)
{
   $this->db     = $db;
}

The getSubscribedEvents function is where you hook in a custom function to an event, the core.user_add_after event in this case. I told phpBB to look for and execute a function in the program, user_add_enable_pm_email_notifications. Further down in main_listener.php I added a small amount of custom code to do what I wanted done inside this function:

public function user_add_enable_pm_email_notifications($vars)
{
$user_id = $vars['user_id'];
$sql = 'INSERT INTO ' . USER_NOTIFICATIONS_TABLE . "(item_type, item_id, user_id, method, notify)
VALUES ('notification.type.pm', 0, " . (int) $user_id . ", 'notification.method.email', 1)";
$this->db->sql_query($sql);
}

The incoming $vars variable contains a collection of stuff that can be read by my event. It turned out that $vars[‘user_id’] contained the user_id for the next user. Then, based on what I saw in the phpbb_user_notifications table, the SQL INSERT statement became straightforward to write and execute.

I think this demonstrates pretty well how a reasonably experienced web programmer can make a small custom extension to add some missing minor functionality that the phpBB Group did not think was needed.

Leave a Reply

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