2012-09-08 10:49:58 -05:00
< ? php
/**
*
* @ package notifications
* @ copyright ( c ) 2012 phpBB Group
* @ license http :// opensource . org / licenses / gpl - 2.0 . php GNU General Public License v2
*
*/
2012-09-08 11:40:02 -05:00
use Symfony\Component\DependencyInjection\ContainerBuilder ;
2012-09-08 10:49:58 -05:00
/**
* @ ignore
*/
if ( ! defined ( 'IN_PHPBB' ))
{
exit ;
}
/**
* Notifications service class
* @ package notifications
*/
class phpbb_notifications_service
{
protected $phpbb_container ;
protected $db ;
/**
* Users loaded from the DB
*
* @ var array Array of user data that we ' ve loaded from the DB
*/
2012-09-09 10:19:46 -05:00
protected $users = array ();
2012-09-08 10:49:58 -05:00
2012-09-08 11:40:02 -05:00
public function __construct ( ContainerBuilder $phpbb_container )
2012-09-08 10:49:58 -05:00
{
$this -> phpbb_container = $phpbb_container ;
// Some common things we're going to use
$this -> db = $phpbb_container -> get ( 'dbal.conn' );
}
/**
* Load the user ' s notifications
*
* @ param array $options Optional options to control what notifications are loaded
2012-09-08 11:40:02 -05:00
* user_id User id to load notifications for ( Default : $user -> data [ 'user_id' ])
2012-09-08 17:28:13 -05:00
* order_by Order by ( Default : time )
* order_dir Order direction ( Default : DESC )
2012-09-08 11:40:02 -05:00
* limit Number of notifications to load ( Default : 5 )
* start Notifications offset ( Default : 0 )
2012-09-14 18:30:12 -05:00
* all_unread Load all unread messages ? ( Default : true )
2012-09-08 10:49:58 -05:00
*/
public function load_notifications ( $options = array ())
{
$user = $this -> phpbb_container -> get ( 'user' );
// Merge default options
$options = array_merge ( array (
'user_id' => $user -> data [ 'user_id' ],
2012-09-08 17:28:13 -05:00
'order_by' => 'time' ,
'order_dir' => 'DESC' ,
2012-09-09 14:55:40 -05:00
'limit' => 5 ,
'start' => 0 ,
2012-09-14 18:30:12 -05:00
'all_unread' => true ,
2012-09-08 10:49:58 -05:00
), $options );
2012-09-15 11:59:30 -05:00
// Anonymous users and bots never receive notifications
if ( $options [ 'user_id' ] == $user -> data [ 'user_id' ] && ( $user -> data [ 'user_id' ] == ANONYMOUS || $user -> data [ 'user_type' ] == USER_IGNORE ))
{
2012-09-15 13:51:02 -05:00
return array (
'notifications' => array (),
'unread_count' => 0 ,
);
2012-09-15 11:59:30 -05:00
}
2012-09-08 10:49:58 -05:00
$notifications = $user_ids = array ();
2012-09-12 22:29:48 -05:00
$load_special = array ();
2012-09-08 10:49:58 -05:00
2012-09-14 18:30:12 -05:00
// Get the total number of unread notifications
$sql = ' SELECT COUNT ( * ) AS count
FROM ' . NOTIFICATIONS_TABLE . '
WHERE user_id = ' . (int) $options[' user_id '] . '
AND unread = 1 ' ;
$result = $this -> db -> sql_query ( $sql );
$count = $this -> db -> sql_fetchfield ( 'count' , $result );
$this -> db -> sql_freeresult ( $result );
$rowset = array ();
// Get the main notifications
2012-09-09 17:23:32 -05:00
$sql = ' SELECT *
FROM ' . NOTIFICATIONS_TABLE . '
2012-09-08 17:28:13 -05:00
WHERE user_id = ' . (int) $options[' user_id '] . '
ORDER BY ' . $this->db->sql_escape($options[' order_by ']) . ' ' . $this->db->sql_escape($options[' order_dir ' ]);
2012-09-08 10:49:58 -05:00
$result = $this -> db -> sql_query_limit ( $sql , $options [ 'limit' ], $options [ 'start' ]);
while ( $row = $this -> db -> sql_fetchrow ( $result ))
2012-09-14 18:30:12 -05:00
{
$rowset [ $row [ 'notification_id' ]] = $row ;
}
$this -> db -> sql_freeresult ( $result );
// Get all unread notifications
2012-09-15 11:59:30 -05:00
if ( $options [ 'all_unread' ] && ! empty ( $rowset ))
2012-09-14 18:30:12 -05:00
{
$sql = ' SELECT *
FROM ' . NOTIFICATIONS_TABLE . '
WHERE user_id = ' . (int) $options[' user_id '] . '
AND unread = 1
AND ' . $this->db->sql_in_set(' notification_id ', array_keys($rowset), true) . '
ORDER BY ' . $this->db->sql_escape($options[' order_by ']) . ' ' . $this->db->sql_escape($options[' order_dir ' ]);
$result = $this -> db -> sql_query_limit ( $sql , $options [ 'limit' ], $options [ 'start' ]);
while ( $row = $this -> db -> sql_fetchrow ( $result ))
{
$rowset [ $row [ 'notification_id' ]] = $row ;
}
$this -> db -> sql_freeresult ( $result );
}
foreach ( $rowset as $row )
2012-09-08 10:49:58 -05:00
{
2012-09-08 12:28:58 -05:00
$item_type_class_name = $this -> get_item_type_class_name ( $row [ 'item_type' ], true );
2012-09-08 10:49:58 -05:00
2012-09-08 12:28:58 -05:00
$notification = new $item_type_class_name ( $this -> phpbb_container , $row );
2012-09-08 10:49:58 -05:00
2012-09-12 22:29:48 -05:00
// Array of user_ids to query all at once
2012-09-08 10:49:58 -05:00
$user_ids = array_merge ( $user_ids , $notification -> users_to_query ());
2012-09-12 22:29:48 -05:00
// Some notification types also require querying additional tables themselves
if ( ! isset ( $load_special [ $row [ 'item_type' ]]))
{
$load_special [ $row [ 'item_type' ]] = array ();
}
$load_special [ $row [ 'item_type' ]] = array_merge ( $load_special [ $row [ 'item_type' ]], $notification -> get_load_special ());
2012-09-08 12:05:55 -05:00
$notifications [] = $notification ;
2012-09-08 10:49:58 -05:00
}
2012-09-09 10:19:46 -05:00
$this -> load_users ( $user_ids );
2012-09-08 10:49:58 -05:00
2012-09-12 22:29:48 -05:00
// Allow each type to load it's own special items
foreach ( $load_special as $item_type => $data )
{
$item_type_class_name = $this -> get_item_type_class_name ( $item_type , true );
$item_type_class_name :: load_special ( $this -> phpbb_container , $data , $notifications );
}
2012-09-14 18:30:12 -05:00
return array (
'notifications' => $notifications ,
'unread_count' => $count ,
);
2012-09-08 10:49:58 -05:00
}
2012-09-14 15:59:13 -05:00
/**
* Mark notifications read
*
2012-09-14 16:54:20 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $data is identical for the specified types )
2012-09-14 15:59:13 -05:00
* @ param bool | int | array $item_id Item id or array of item ids . False to mark read for all item ids
* @ param bool | int | array $user_id User id or array of user ids . False to mark read for all user ids
* @ param bool | int $time Time at which to mark all notifications prior to as read . False to mark all as read . ( Default : False )
*/
public function mark_notifications_read ( $item_type , $item_id , $user_id , $time = false )
{
2012-09-14 16:54:20 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
$this -> mark_notifications_read ( $type , $item_id , $user_id , $time );
}
return ;
}
2012-09-14 15:59:13 -05:00
$time = ( $time ) ? : time ();
$this -> get_item_type_class_name ( $item_type );
$sql = 'UPDATE ' . NOTIFICATIONS_TABLE . "
SET unread = 0
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
AND time <= " . $time .
(( $item_id !== false ) ? ' AND ' . ( is_array ( $item_id ) ? $this -> db -> sql_in_set ( 'item_id' , $item_id ) : 'item_id = ' . ( int ) $item_id ) : '' ) .
(( $user_id !== false ) ? ' AND ' . ( is_array ( $user_id ) ? $this -> db -> sql_in_set ( 'user_id' , $user_id ) : 'user_id = ' . ( int ) $user_id ) : '' );
$this -> db -> sql_query ( $sql );
}
/**
* Mark notifications read from a parent identifier
*
2012-09-14 16:54:20 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $data is identical for the specified types )
2012-09-14 15:59:13 -05:00
* @ param bool | int | array $item_parent_id Item parent id or array of item parent ids . False to mark read for all item parent ids
* @ param bool | int | array $user_id User id or array of user ids . False to mark read for all user ids
* @ param bool | int $time Time at which to mark all notifications prior to as read . False to mark all as read . ( Default : False )
*/
public function mark_notifications_read_by_parent ( $item_type , $item_parent_id , $user_id , $time = false )
{
2012-09-14 16:54:20 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
2012-09-14 18:05:13 -05:00
$this -> mark_notifications_read_by_parent ( $type , $item_parent_id , $user_id , $time );
2012-09-14 16:54:20 -05:00
}
return ;
}
2012-09-14 15:59:13 -05:00
$time = ( $time ) ? : time ();
$item_type_class_name = $this -> get_item_type_class_name ( $item_type );
$sql = 'UPDATE ' . NOTIFICATIONS_TABLE . "
SET unread = 0
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
AND time <= " . $time .
(( $item_parent_id !== false ) ? ' AND ' . ( is_array ( $item_parent_id ) ? $this -> db -> sql_in_set ( 'item_parent_id' , $item_parent_id ) : 'item_parent_id = ' . ( int ) $item_parent_id ) : '' ) .
(( $user_id !== false ) ? ' AND ' . ( is_array ( $user_id ) ? $this -> db -> sql_in_set ( 'user_id' , $user_id ) : 'user_id = ' . ( int ) $user_id ) : '' );
$this -> db -> sql_query ( $sql );
}
2012-09-08 11:40:02 -05:00
/**
* Add a notification
*
2012-09-14 16:54:20 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $data is identical for the specified types )
2012-09-08 11:40:02 -05:00
* @ param array $data Data specific for this type that will be inserted
*/
2012-09-08 12:28:58 -05:00
public function add_notifications ( $item_type , $data )
2012-09-08 10:49:58 -05:00
{
2012-09-14 16:54:20 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
$this -> add_notifications ( $type , $data );
}
return ;
}
2012-09-08 12:28:58 -05:00
$item_type_class_name = $this -> get_item_type_class_name ( $item_type );
$item_id = $item_type_class_name :: get_item_id ( $data );
2012-09-08 10:49:58 -05:00
// find out which users want to receive this type of notification
2012-09-08 16:02:32 -05:00
$notify_users = $item_type_class_name :: find_users_for_notification ( $this -> phpbb_container , $data );
2012-09-08 10:49:58 -05:00
2012-09-14 14:55:14 -05:00
$this -> add_notifications_for_users ( $item_type , $data , $notify_users );
}
/**
* Add a notification for specific users
*
2012-09-14 16:54:20 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $data is identical for the specified types )
2012-09-14 14:55:14 -05:00
* @ param array $data Data specific for this type that will be inserted
* @ param array $notify_users User list to notify
*/
public function add_notifications_for_users ( $item_type , $data , $notify_users )
{
2012-09-14 16:54:20 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
2012-09-15 14:33:15 -05:00
$this -> add_notifications_for_users ( $type , $data , $notify_users );
2012-09-14 16:54:20 -05:00
}
return ;
}
2012-09-14 14:55:14 -05:00
$item_type_class_name = $this -> get_item_type_class_name ( $item_type );
$item_id = $item_type_class_name :: get_item_id ( $data );
$user_ids = array ();
$notification_objects = $notification_methods = array ();
$new_rows = array ();
2012-09-15 13:51:02 -05:00
// Never send notifications to the anonymous user!
unset ( $notify_users [ ANONYMOUS ]);
2012-09-09 10:19:46 -05:00
2012-09-08 12:28:58 -05:00
// Make sure not to send new notifications to users who've already been notified about this item
// This may happen when an item was added, but now new users are able to see the item
2012-09-14 14:55:14 -05:00
// todo Users should not receive notifications from multiple events from the same item (ex: for a topic reply with a quote including your username)
// Probably should be handled within each type?
2012-09-09 17:23:32 -05:00
$sql = ' SELECT user_id
FROM ' . NOTIFICATIONS_TABLE . "
2012-09-08 12:28:58 -05:00
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
AND item_id = " . (int) $item_id ;
$result = $this -> db -> sql_query ( $sql );
while ( $row = $this -> db -> sql_fetchrow ( $result ))
{
unset ( $notify_users [ $row [ 'user_id' ]]);
}
$this -> db -> sql_freeresult ( $result );
2012-09-08 16:02:32 -05:00
if ( ! sizeof ( $notify_users ))
{
return ;
}
2012-09-08 11:40:02 -05:00
// Go through each user so we can insert a row in the DB and then notify them by their desired means
foreach ( $notify_users as $user => $methods )
{
2012-09-08 12:28:58 -05:00
$notification = new $item_type_class_name ( $this -> phpbb_container );
2012-09-08 10:49:58 -05:00
2012-09-08 11:40:02 -05:00
$notification -> user_id = ( int ) $user ;
2012-09-08 10:49:58 -05:00
2012-09-09 10:19:46 -05:00
// Store the creation array in our new rows that will be inserted later
2012-09-08 10:49:58 -05:00
$new_rows [] = $notification -> create_insert_array ( $data );
2012-09-09 10:19:46 -05:00
// Users are needed to send notifications
$user_ids = array_merge ( $user_ids , $notification -> users_to_query ());
2012-09-08 11:40:02 -05:00
foreach ( $methods as $method )
2012-09-08 10:49:58 -05:00
{
2012-09-08 11:40:02 -05:00
// setup the notification methods and add the notification to the queue
2012-09-09 14:55:40 -05:00
if ( $method ) // blank means we just insert it as a notification, but do not notify them by any other means
2012-09-08 10:49:58 -05:00
{
2012-09-08 16:12:20 -05:00
if ( ! isset ( $notification_methods [ $method ]))
2012-09-08 11:40:02 -05:00
{
2012-09-08 16:12:20 -05:00
$method_class_name = 'phpbb_notifications_method_' . $method ;
$notification_methods [ $method ] = new $method_class_name ( $this -> phpbb_container );
2012-09-08 11:40:02 -05:00
}
2012-09-09 10:19:46 -05:00
2012-09-08 16:12:20 -05:00
$notification_methods [ $method ] -> add_to_queue ( $notification );
2012-09-08 11:40:02 -05:00
}
2012-09-08 10:49:58 -05:00
}
}
// insert into the db
$this -> db -> sql_multi_insert ( NOTIFICATIONS_TABLE , $new_rows );
2012-09-09 10:19:46 -05:00
// We need to load all of the users to send notifications
$this -> load_users ( $user_ids );
2012-09-08 10:49:58 -05:00
// run the queue for each method to send notifications
2012-09-08 11:40:02 -05:00
foreach ( $notification_methods as $method )
2012-09-08 10:49:58 -05:00
{
2012-09-09 10:36:22 -05:00
$method -> notify ();
2012-09-08 10:49:58 -05:00
}
}
2012-09-08 11:40:02 -05:00
/**
* Update a notification
*
2012-09-14 16:54:20 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $data is identical for the specified types )
2012-09-08 11:40:02 -05:00
* @ param array $data Data specific for this type that will be updated
*/
2012-09-09 10:19:46 -05:00
public function update_notifications ( $item_type , $data )
2012-09-08 10:49:58 -05:00
{
2012-09-14 16:54:20 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
2012-09-15 14:33:15 -05:00
$this -> update_notifications ( $type , $data );
2012-09-14 16:54:20 -05:00
}
return ;
}
2012-09-08 12:28:58 -05:00
$item_type_class_name = $this -> get_item_type_class_name ( $item_type );
2012-09-08 10:49:58 -05:00
2012-09-14 14:55:14 -05:00
// Allow the notifications class to over-ride the update_notifications functionality
if ( method_exists ( $item_type_class_name , 'update_notifications' ))
{
// Return False to over-ride the rest of the update
if ( $item_type_class_name :: update_notifications ( $this -> phpbb_container , $data ) === false )
{
return ;
}
}
2012-09-09 10:19:46 -05:00
$item_id = $item_type_class_name :: get_item_id ( $data );
2012-09-08 12:28:58 -05:00
$notification = new $item_type_class_name ( $this -> phpbb_container );
2012-09-08 11:40:02 -05:00
$update_array = $notification -> create_update_array ( $data );
2012-09-08 10:49:58 -05:00
$sql = 'UPDATE ' . NOTIFICATIONS_TABLE . '
2012-09-08 11:40:02 -05:00
SET ' . $this->db->sql_build_array(' UPDATE ' , $update_array ) . "
2012-09-08 12:28:58 -05:00
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
AND item_id = " . (int) $item_id ;
$this -> db -> sql_query ( $sql );
}
/**
* Delete a notification
*
2012-09-15 14:33:15 -05:00
* @ param string | array $item_type Type identifier or array of item types ( only acceptable if the $item_id is identical for the specified types )
2012-09-09 10:19:46 -05:00
* @ param int | array $item_id Identifier within the type ( or array of ids )
2012-09-08 12:28:58 -05:00
* @ param array $data Data specific for this type that will be updated
*/
public function delete_notifications ( $item_type , $item_id )
{
2012-09-15 14:33:15 -05:00
if ( is_array ( $item_type ))
{
foreach ( $item_type as $type )
{
$this -> delete_notifications ( $type , $item_id );
}
return ;
}
2012-09-14 15:59:13 -05:00
$this -> get_item_type_class_name ( $item_type );
2012-09-08 12:28:58 -05:00
$sql = 'DELETE FROM ' . NOTIFICATIONS_TABLE . "
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
2012-09-09 10:19:46 -05:00
AND " . (is_array( $item_id ) ? $this->db ->sql_in_set('item_id', $item_id ) : 'item_id = ' . (int) $item_id );
2012-09-08 11:40:02 -05:00
$this -> db -> sql_query ( $sql );
}
2012-09-08 10:49:58 -05:00
2012-09-15 14:33:15 -05:00
/*
2012-09-09 17:20:39 -05:00
public function add_subscription ( $item_type , $item_id , $method = '' )
{
$this -> get_item_type_class_name ( $item_type );
$sql = 'INSERT INTO ' . USER_NOTIFICATIONS_TABLE . ' ' .
$this -> db -> sql_build_array ( 'INSERT' , array (
'item_type' => $item_type ,
'item_id' => ( int ) $item_id ,
'user_id' => $this -> phpbb_container -> get ( 'user' ) -> data [ 'user_id' ],
'method' => $method ,
));
$this -> db -> sql_query ( $sql );
}
2012-09-15 14:33:15 -05:00
*/
2012-09-09 17:20:39 -05:00
2012-09-09 10:19:46 -05:00
/**
* Load user helper
*
* @ param array $user_ids
*/
public function load_users ( $user_ids )
{
// Load the users
$user_ids = array_unique ( $user_ids );
// Do not load users we already have in $this->users
$user_ids = array_diff ( $user_ids , array_keys ( $this -> users ));
if ( sizeof ( $user_ids ))
{
2012-09-09 17:23:32 -05:00
$sql = ' SELECT *
FROM ' . USERS_TABLE . '
2012-09-09 10:19:46 -05:00
WHERE ' . $this->db->sql_in_set(' user_id ' , $user_ids );
$result = $this -> db -> sql_query ( $sql );
while ( $row = $this -> db -> sql_fetchrow ( $result ))
{
$this -> users [ $row [ 'user_id' ]] = $row ;
}
$this -> db -> sql_freeresult ( $result );
}
}
/**
* Get a user row from our users cache
*
* @ param int $user_id
* @ return array
*/
public function get_user ( $user_id )
{
return $this -> users [ $user_id ];
}
2012-09-08 11:40:02 -05:00
/**
2012-09-08 12:28:58 -05:00
* Helper to get the notifications item type class name and clean it if unsafe
2012-09-08 11:40:02 -05:00
*/
2012-09-08 12:28:58 -05:00
private function get_item_type_class_name ( & $item_type , $safe = false )
2012-09-08 11:40:02 -05:00
{
if ( ! $safe )
2012-09-08 10:49:58 -05:00
{
2012-09-15 13:51:02 -05:00
$item_type = preg_replace ( '#[^a-z_]#' , '' , $item_type );
2012-09-08 10:49:58 -05:00
}
2012-09-08 11:40:02 -05:00
2012-09-08 12:28:58 -05:00
return 'phpbb_notifications_type_' . $item_type ;
2012-09-08 10:49:58 -05:00
}
}