Réaliser des notifications dans phpBB
Introduction :
Dans mon article concernant la sortie de phpBB 3.1, j'avais évoqué l'une des principales nouveautés : l'arrivée des notifications. En 6 ans le mécanisme de notification a beaucoup changé dans phpBB. Ainsi, le système a été entièrement revu entre la version 3.1 et 3.2. Cet article ne traite donc que des notifications à partir de phpBB 3.2.
Sur le principe, une notification n'est pas très compliquée à mettre en oeuvre une fois qu'on a compris la mécanique. Malheureusement la documentation avec des how to simples n’est pas légion. Le but de cet article est donc de vous partager mes recherches, et de me servir d'aide mémoire le jour où je voudrais me replonger dans mon code ou créer de nouvelles notifications.
Quelques liens :
Avant de commencer, voici quelques liens qui m'ont permis de comprendre puis réaliser mes extensions utilisant des notifications :
- Documentation officielle.
- Changements entre 3.1 et 3.2
- Ma demande d'aide sur le forum phpbb-fr.
- Skeleton.
Matérialisation de la notification :
Tout d'abord, il faut comprendre que le système de notification de phpBB regroupe un ensemble :
- Push mail.
- Notification sur le forum
Une extension peut donc avertir l'utilisateur par ces 2 moyens. Nous verrons par la suite qu'il est possible d'utiliser soit l'un soit l'autre, soit les deux. Tout en sachant bien que le dernier mot viendra à l'utilisateur qui pourra régler ses préférences via son profil (par exemple sur cette page sur notre forum).
Architecture du système de notification :
Comme expliqué sur la documentation officielle, ou si vous utilisez Skeleton, l'outil permettant, comme son nom l'indique, de créer le squelette de vos extensions, l'architecture de votre projet est la suivante :
vendor
├── package
│ ├── config # The config dir contains all service config files
│ │ ├── services.yml # A config YAML file
│ │ ├── notification.yml # ajout personnel pour séparer la partie extension de ce qui est purement notification
│ │ └── ...
│ ├── event # The event dir contains all PHP event listeners
│ │ ├── main_listener.php # A sample PHP event listener
│ │ └── ...
│ ├── ext.php # Contains enable, disable and purge steps
│ ├── language # Dir containing language files
│ │ ├── en # English language files (required)
│ │ │ ├── common.php # A language file used by the extension
│ │ │ ├── info_ucp_demo.php # A UCP language file used by the notification
│ │ │ └── ...
│ │ └── ...
│ ├── notification # Dir containing notification related classes
│ │ ├── type # Dir containing notification types
│ │ │ ├── sample.php # A sample notification type class
│ │ │ └── ...
│ │ └── ...
│ └── ...
└── ...
Concrètement, votre job sera donc :
- De décrire votre notification (et votre extension) dans les fichiers yml
- Gérer les traductions
- Faire votre listener
- Coder vos notifications :
- Destinataires
- Contenu
- etc.
Les fichiers yml :
C'est assez simple. Donc de mon côté, comme expliqué, j'ai séparé la configuration de l'extension de la partie notification.
Ainsi dans services.yml, j'ai tout simplement rajouté ceci au début :
imports:
- { resource: notifications.yml }
Et donc, dans le fichier notification.yml j'ai donc décris toutes les notifications utilisées par mon extension :
services:
aurelienazerty.sitenotification.notification.type.photolike:
class: aurelienazerty\sitenotification\notification\type\photolike
shared: false # service MUST not be shared for this to work!
parent: notification.type.base
calls:
- [set_config, ['@config']]
- [set_user_loader, ['@user_loader']]
- [set_controller_helper, ['@controller.helper']]
tags:
- { name: notification.type }
Ceci est à répéter pour chaque notification. Dans mon cas, j'en avait 3 :
- Pour les like / dislike sur les photos du site.
- Pour les vieux pronos de pronofoot.
- Pour les absences de prono sur pronofoot.
Les fichiers de traduction et le listener :
Partie assez basique :
- Dans common.php vous rajouterez les textes qui seront affichés lors de l'affichage de la notification.
- Dans info_ucp_sitenotification.php vous rajouterez ce qui concerne la partie gestion des notifications pour l'utilisateur.
La partie dans info_ucp_sitenotification.php est importante. Dans mon exemple :
$lang = array_merge($lang, array(
'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION' => 'Notification via le site',
'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION_PHOTOLIKE' => "Quelqu'un a réagi à l'un de vos commentaires",
'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION_OLDPRONO' => "En cas de pronostique ancien",
'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION_NOPRONO' => "En cas d'absence de prono",
));
On voit qu'il y a un groupe : NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION puis j'ai mes 3 notifications NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION_xxxx
Et pour se dépatouiller dans tout ça, RDV dans votre notification (voir le chapitre dédié).
codemière étape rajouter un évènement lors de l'affichage de la page de gestion des notifications pour l'utilisateur
static public function getSubscribedEvents() {
return array(
'core.user_setup' => 'load_language_on_setup',
);
}
et donc la partie rajout de vos fichier de langue :
/**
* Load language files during user setup
*
* @param phpbbeventdata $event The event object
*
* @return void
* @access public
*/
public function load_language_on_setup($event)
{
$lang_set_ext = $event['lang_set_ext'];
$lang_set_ext[] = array(
'ext_name' => 'aurelienazerty/sitenotification',
'lang_set' => 'common',
);
$event['lang_set_ext'] = $lang_set_ext;
}
Pour ma part, ce sera tout pour la partie listener. La mécanique de notification se faire via des cron / des bouts de code du site que je détaillerai plus loin.
Ext.php :
Dans ce fichier, pas de réflexion particulière à avoir. Il faut décrire les fonctions suivantes :
- enable_step
- disable_step
- purge_step
Qui sont toujours les mêmes :
/**
* Check whether or not the extension can be enabled.
* The current phpBB version should meet or exceed
* the minimum version required by this extension:
*
* Requires phpBB 3.2.1 and PHP 5.4.
*
* @return bool
* @access public
*/
public function is_enableable()
{
$config = $this->container->get('config');
return phpbb_version_compare($config['version'], '3.2.1', '>=') && version_compare(PHP_VERSION, '5.4', '>=');
}
/**
* Overwrite enable_step to enable extension notifications before any included migrations are installed.
*
* @param mixed $old_state State returned by codevious call of this method
*
* @return mixed Returns false after last step, otherwise temporary state
* @access public
*/
public function enable_step($old_state)
{
switch ($old_state)
{
case '': // Empty means nothing has run yet
// Enable notifications
return $this->notification_handler('enable', $this->notification_types());
default:
// Run parent enable step method
return parent::enable_step($old_state);
}
}
/**
* Overwrite disable_step to disable extension notifications before the extension is disabled.
*
* @param mixed $old_state State returned by codevious call of this method
*
* @return mixed Returns false after last step, otherwise temporary state
* @access public
*/
public function disable_step($old_state)
{
switch ($old_state)
{
case '': // Empty means nothing has run yet
// Disable notifications
return $this->notification_handler('disable', $this->notification_types());
default:
// Run parent disable step method
return parent::disable_step($old_state);
}
}
/**
* Overwrite purge_step to purge extension notifications before any included and installed migrations are reverted.
*
* @param mixed $old_state State returned by codevious call of this method
*
* @return mixed Returns false after last step, otherwise temporary state
* @access public
*/
public function purge_step($old_state)
{
switch ($old_state)
{
case '': // Empty means nothing has run yet
// Purge notifications
return $this->notification_handler('purge', $this->notification_types());
default:
// Run parent purge step method
return parent::purge_step($old_state);
}
}
La petite astuce lorsque vous avez plusieurs notifications c'est de rajouter une fonction notification_handler :
/**
* Notification handler to call notification enable/disable/purge steps
*
* @author VSEphpbb (Matt Friedman)
* @copyright (c) 2014 phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* @param string $step The step (enable, disable, purge)
* @param array $notification_types The notification type names
*
* @return string Return notifications as temporary state
* @access protected
*/
protected function notification_handler($step, $notification_types)
{
/** @type phpbbnotificationmanager $phpbb_notifications */
$phpbb_notifications = $this->container->get('notification_manager');
foreach ($notification_types as $notification_type)
{
call_user_func(array($phpbb_notifications, $step . '_notifications'), $notification_type);
}
return 'notifications';
}
Et rajouter cette ligne :
/**
* Returns the list of notification types
*
* @return array
* @access protected
*/
protected function notification_types()
{
return array(
'aurelienazerty.sitenotification.notification.type.photolike',
'aurelienazerty.sitenotification.notification.type.oldprono',
'aurelienazerty.sitenotification.notification.type.noprono',
);
}
Vous l'aurez compris, ce n'est pas la partie la plus intéressante du code.
La notification :
C'est le cœur de votre extension, mais ce n'est pas la plus compliquée.
Les fonctions importantes sont les suivantes :
Pour les langues :
/**
* Notification option data (for outputting to the user)
*
* @var bool array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
public static $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION_PHOTOLIKE',
'group' => 'NOTIFICATION_TYPE_AURELIENAZERTY_SITENOTIFICATION',
);
Les fonctions suivantes permettent de déterminer les utilisateurs à notifier. Je n'ai pas modifié le code type :
/**
* Find the users who want to receive notifications
*
* @param array $data The type specific data
* @param array $options Options for finding users for notification
* ignore_users => array of users and user types that should not receive notifications from this type because they've already been notified
* e.g.: array(2 => array(''), 3 => array('', 'email'), ...)
*
* @return array
*/
public function find_users_for_notification($data, $options = array())
{
// Return an array of users to be notified, storing the user_ids as the array keys
return $this->check_user_notification_options(array($data['owner_commentaire_id']), $options);
}
/**
* Users needed to query before this notification can be displayed
*
* @return array Array of user_ids
*/
public function users_to_query()
{
return array($this->get_data('liker_id'));
}
Le point le plus important reste la fonction create_insert_array, ce sont tout simplement les données qui vous permettront de créer le lien de redirection et personnaliser le message affiché, etc., etc. :
/**
* Function for codeparing the data for insertion in an SQL query
* (The service handles insertion)
*
* @param array $data The type specific data
* @param array $code_create_data Data from code_create_insert_array()
*/
public function create_insert_array($data, $code_create_data = array())
{
$this->set_data('photo_id', $data['photo_id']);
$this->set_data('liker_id', $data['liker_id']);
$this->set_data('commentaire_id', $data['commentaire_id']);
$this->set_data('owner_commentaire_id', $data['owner_commentaire_id']);
$this->set_data('reaction_type', $data['reaction_type']);
parent::create_insert_array($data, $code_create_data);
}
De mon côté get_item_id et get_item_parent_id ne me sont pas utile. Pour garder le principe souvent appliqué dans les extensions post/topic ou topic/forum, dans mes notifications des prono, j'ai utilisé le couple grille/compétition. Mais comme je viens de le dire, le code ne sert pas dans mon cas.
Ce qui m'a intéressé, ce sont les fonctions get_url (qui comme son nom l'indique va créer le lien sur lequel va aller l'utilisateur lorsqu'il clique sur la notification) et get_title (qui comme sont nom l'indique là aussi, est le titre de la notification). Inutile de vous copier coller mon code, c'est vraiment spécifique à votre notification. La seule chose à retenir, c'est que vous accédez à vos données définies danscreate_insert_array via $this->get_data('votre_donnée') .
Pour la partie mail, il faut écrire les fonctions get_email_template et get_email_template_variables. Pour ma part, je ne souhaitais pas faire de mail (c'est géré par ailleurs) donc mes fonctions retournent respectivement false ou un tableau vide.
Appel via des scripts externes et CRON :
Pour faire proprement, du moins via l'architecture de phpBB, les appels se font via le listner. Malheureusement dans mon cas, c'est via des crons que je cherche à notifier les utilisateurs.
Tout d'abord, il faut créer votre tableau de donnée tel que défini dans create_insert_array (dans mon cas, mon tableau s'appelle $notif). Ensuite, il faut l'envoyer dans votre notification.
Et là, c'est extrêmement simple. En effet, il faut savoir c'est que lorsque vous intégrer phpBB dans vos scripts via :
define('IN_PHPBB', true);
$phpbb_root_path = CHEMIN_VERS_PHPBB;
$phpEx = substr(strrchr(__FILE__, '.'), 1);
include($phpbb_root_path . 'common.' . $phpEx);
Vous avez une variable phpbb_container. Via cette variable, vous allez pouvoir appeler notre notification ainsi :
$phpbb_container
->get('notification_manager')
->add_notifications(
'aurelienazerty.sitenotification.notification.type.noprono',
$notif
);
Voilà, c'est tout !
Conclusion :
Comme vous pouvez le voir, l'ajout de notification est assez simple une fois la logique de phpBB compris. Je profite de cet article pour remercier Skouat de phpbb-fr qui m'a aidé à comprendre cette logique.