mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
* Notifications: aggregate messages by app:id to return only latest one, also added a cut-off date of 30 days and cleaning older messages
This commit is contained in:
parent
20c93a0c7a
commit
c5d3bb89ab
@ -15,7 +15,8 @@ use EGroupware\Api;
|
||||
/**
|
||||
* Ajax methods for notifications
|
||||
*/
|
||||
class notifications_ajax {
|
||||
class notifications_ajax
|
||||
{
|
||||
/**
|
||||
* Appname
|
||||
*/
|
||||
@ -36,6 +37,11 @@ class notifications_ajax {
|
||||
*/
|
||||
const _type = 'base';
|
||||
|
||||
/**
|
||||
* Do NOT consider notifications older than this
|
||||
*/
|
||||
const CUT_OFF_DATE = '-30days';
|
||||
|
||||
/**
|
||||
* holds account object for user to notify
|
||||
*
|
||||
@ -64,22 +70,6 @@ class notifications_ajax {
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* holds the users session data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $session_data;
|
||||
|
||||
/**
|
||||
* holds the users session data defaults
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $session_data_defaults = array(
|
||||
'notified_mail_uids' => array(),
|
||||
);
|
||||
|
||||
/**
|
||||
* the xml response object
|
||||
*
|
||||
@ -98,15 +88,11 @@ class notifications_ajax {
|
||||
* constructor
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
public function __construct()
|
||||
{
|
||||
$this->response = Api\Json\Response::get();
|
||||
$this->recipient = (object)$GLOBALS['egw']->accounts->read($GLOBALS['egw_info']['user']['account_id']);
|
||||
|
||||
$this->config = (object)Api\Config::read(self::_appname);
|
||||
|
||||
$prefs = new Api\Preferences($this->recipient->account_id);
|
||||
$this->preferences = $prefs->read();
|
||||
|
||||
$this->db = $GLOBALS['egw']->db;
|
||||
|
||||
$this->isPushServer = Api\Cache::getInstance('notifications', 'isPushServer', function ()
|
||||
@ -147,18 +133,9 @@ class notifications_ajax {
|
||||
*
|
||||
* @param array $notifymessages one or multiple notify_id(s)
|
||||
*/
|
||||
public function delete_message($notifymessages)
|
||||
public function delete_message(array $notifymessages)
|
||||
{
|
||||
$notify_ids = $this->fetch_notify_ids($notifymessages);
|
||||
if (!empty($notify_ids))
|
||||
{
|
||||
$this->db->delete(self::_notification_table,array(
|
||||
'notify_id' => $notify_ids,
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_type' => self::_type
|
||||
),__LINE__,__FILE__,self::_appname);
|
||||
}
|
||||
$this->response->data(['deleted'=>$notify_ids]);
|
||||
$this->update($notifymessages, null); // null = delete
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,80 +149,95 @@ class notifications_ajax {
|
||||
* this status has been used more specifically for browser type
|
||||
* of notifications.
|
||||
*/
|
||||
public function update_status($notifymessages, $status = "SEEN")
|
||||
public function update_status(array $notifymessages, $status = "SEEN")
|
||||
{
|
||||
$notify_ids = $this->fetch_notify_ids($notifymessages);
|
||||
if (!empty($notify_ids))
|
||||
{
|
||||
$this->db->update(self::_notification_table,array('notify_status' => $status),array(
|
||||
'notify_id' => $notify_ids,
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_type' => self::_type
|
||||
),__LINE__,__FILE__,self::_appname);
|
||||
}
|
||||
$this->update($notifymessages, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets all relevant notify ids based on given notify message data
|
||||
* @param $notifymessages
|
||||
* Update or delete the given notification messages, incl. not explicitly mentioned ones with same app:id
|
||||
*
|
||||
* @param array $notifymessages
|
||||
* @param string|null $status use null to delete
|
||||
* @return array
|
||||
*/
|
||||
public function fetch_notify_ids ($notifymessages)
|
||||
protected function update(array $notifymessages, $status='SEEN')
|
||||
{
|
||||
$notify_ids = [];
|
||||
|
||||
$notify_ids = $app_ids = [];
|
||||
foreach ($notifymessages as $data)
|
||||
{
|
||||
if (is_array($data) && $data['id'])
|
||||
if (is_array($data) && !empty($data['id']))
|
||||
{
|
||||
array_push($notify_ids, (string)$data['id']);
|
||||
if (is_array($data['data'])) $notify_ids = array_unique(array_merge($notify_ids, $this->search_in_notify_data($data['data']['id'], $data['data']['app'])));
|
||||
if (is_array($data['data'] ?? null) && !empty($data['data']['id']))
|
||||
{
|
||||
$app_ids[$data['data']['app']][$data['data']['id']] = $data['data']['id'];
|
||||
}
|
||||
$notify_ids[] = $data['id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
array_push($notify_ids, (string)$data);
|
||||
$notify_ids[] = $data;
|
||||
}
|
||||
|
||||
}
|
||||
return $notify_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all notify_ids relevant to the entry
|
||||
* @param $_id
|
||||
* @param $_appname
|
||||
* @return array
|
||||
*/
|
||||
public function search_in_notify_data($_id, $_appname)
|
||||
{
|
||||
$ret = [];
|
||||
if ($_id && $_appname)
|
||||
{
|
||||
$cut_off = $this->db->quote(Api\DateTime::to(self::CUT_OFF_DATE, Api\DateTime::DATABASE));
|
||||
try {
|
||||
// mariaDB supported query
|
||||
$ret = $this->db->select(self::_notification_table, 'notify_id', array(
|
||||
// MariaDB code using JSON_EXTRACT()
|
||||
foreach($app_ids as $app => $ids)
|
||||
{
|
||||
$where = [
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_type' => self::_type,
|
||||
'notify_data->"$.appname"' => $_appname,
|
||||
'notify_data->"$.data.id"' => $_id
|
||||
),
|
||||
__LINE__,__FILE__,0 ,'ORDER BY notify_id DESC',self::_appname);
|
||||
"JSON_EXTRACT(notify_data, '$.appname') = ".$this->db->quote($app),
|
||||
"JSON_EXTRACT(notify_data, '$.data.id') IN (".implode(',', array_map([$this->db, 'quote'], array_unique($ids))).')',
|
||||
'notify_created > '.$cut_off,
|
||||
];
|
||||
if (isset($status))
|
||||
{
|
||||
$this->db->update(self::_notification_table, ['notify_status' => $status], $where, __LINE__, __FILE__, self::_appname);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->delete(self::_notification_table, $where, __LINE__, __FILE__, self::_appname);
|
||||
}
|
||||
}
|
||||
}
|
||||
// other DBs
|
||||
catch (Api\Db\Exception $e) {
|
||||
// do it manual for all other DB
|
||||
foreach($this->db->select(self::_notification_table, '*', array(
|
||||
foreach($this->db->select(self::_notification_table, 'notify_id,notify_data', [
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_type' => self::_type,
|
||||
'notify_created > '.$cut_off,
|
||||
"notify_data <> '[]'", // does not return NULL or '[]' rows
|
||||
]) as $row)
|
||||
{
|
||||
if (($data = json_decode($row['notify_data'], true)) &&
|
||||
isset($data['data']['id']) && in_array($data['data']['id'], $app_ids[$data['appname']] ?? []))
|
||||
{
|
||||
$notify_ids[] = $row['notify_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$where = [
|
||||
'notify_id' => array_unique($notify_ids),
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_type' => self::_type
|
||||
),
|
||||
__LINE__,__FILE__,0 ,'ORDER BY notify_id DESC',self::_appname) as $row)
|
||||
];
|
||||
if (isset($status))
|
||||
{
|
||||
$data = json_decode($row['notify_data'], true);
|
||||
if ($data['appname'] == $_appname && $data['data']['id'] == $_id) $ret[] = $row['notify_id'];
|
||||
$this->db->update(self::_notification_table, ['notify_status' => $status], $where, __LINE__, __FILE__, self::_appname);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->delete(self::_notification_table, $where, __LINE__, __FILE__, self::_appname);
|
||||
}
|
||||
|
||||
// cleanup messages older than our cut-off-date
|
||||
$this->db->delete(self::_notification_table, [
|
||||
'notification_created <= '.$cut_off,
|
||||
'notify_type' => self::_type
|
||||
], __LINE__, __FILE__, self::_appname);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets all egwpopup notifications for calling user
|
||||
*
|
||||
@ -257,30 +249,4 @@ class notifications_ajax {
|
||||
$this->response->apply('app.notifications.append', array($entries['rows']??[], $browserNotify, $entries['total']??0));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* restores the users session data for notifications
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
private function restore_session_data() {
|
||||
$session_data = Api\Cache::getSession(self::_appname, 'session_data');
|
||||
if(is_array($session_data)) {
|
||||
$this->session_data = $session_data;
|
||||
} else {
|
||||
$this->session_data = $this->session_data_defaults;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* saves the users session data for notifications
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
private function save_session_data() {
|
||||
Api\Cache::setSession(self::_appname, 'session_data', $this->session_data);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -21,8 +21,8 @@ use EGroupware\Api;
|
||||
* out the table to look if there is a notificaton for this
|
||||
* client. The second stage is done in class.notifications_ajax.inc.php
|
||||
*/
|
||||
class notifications_popup implements notifications_iface {
|
||||
|
||||
class notifications_popup implements notifications_iface
|
||||
{
|
||||
/**
|
||||
* Appname
|
||||
*/
|
||||
@ -120,13 +120,14 @@ class notifications_popup implements notifications_iface {
|
||||
* @param array $_user_sessions
|
||||
* @param array $_data
|
||||
*/
|
||||
private function save($_message, $_data) {
|
||||
private function save($_message, $_data)
|
||||
{
|
||||
$result = $this->db->insert( self::_notification_table, array(
|
||||
'account_id' => $this->recipient->account_id,
|
||||
'notify_message' => $_message,
|
||||
'notify_type' => self::_type,
|
||||
'notify_data' => is_array($_data) ? json_encode($_data) : NULL,
|
||||
'notify_created' => Api\DateTime::user2server('now'),
|
||||
'notify_data' => $_data && is_array($_data) ? json_encode($_data) : NULL,
|
||||
'notify_created' => new Api\DateTime(),
|
||||
), false,__LINE__,__FILE__,self::_appname);
|
||||
if ($result === false) throw new Exception("Can't save notification into SQL table");
|
||||
$push = new Api\Json\Push($this->recipient->account_id);
|
||||
@ -136,28 +137,39 @@ class notifications_popup implements notifications_iface {
|
||||
|
||||
|
||||
/**
|
||||
* read all notification messages for given recipient
|
||||
* Read the 100 most recent notification messages for given recipient
|
||||
*
|
||||
* We use a cut-off-date of 30day, not returning anything older!
|
||||
*
|
||||
* @param $_account_id
|
||||
* @param int $num_rows
|
||||
* @return array
|
||||
*/
|
||||
public static function read($_account_id)
|
||||
public static function read($_account_id, int $num_rows=100)
|
||||
{
|
||||
if (!$_account_id) return [];
|
||||
|
||||
$rs = $GLOBALS['egw']->db->select(self::_notification_table, '*', array(
|
||||
/** @var Api\Db $db */
|
||||
$db = $GLOBALS['egw']->db;
|
||||
|
||||
$result = [];
|
||||
if (($total = $db->select(self::_notification_table, 'COUNT(*)', [
|
||||
'account_id' => $_account_id,
|
||||
'notify_type' => self::_type
|
||||
),
|
||||
__LINE__,__FILE__,0 ,'ORDER BY notify_id DESC',self::_appname, 100);
|
||||
// Fetch the total
|
||||
$total = $GLOBALS['egw']->db->select(self::_notification_table, 'COUNT(*)', array(
|
||||
'notify_type' => self::_type,
|
||||
'notify_created > '.($cut_off=$db->quote(Api\DateTime::to(notifications_ajax::CUT_OFF_DATE, Api\DateTime::DATABASE))),
|
||||
], __LINE__, __FILE__, false, '', self::_appname)->fetchColumn()))
|
||||
{
|
||||
$n = 0;
|
||||
$chunk_size = 150;
|
||||
do
|
||||
{
|
||||
$notification = null;
|
||||
foreach ($rs=$db->select(self::_notification_table, '*', [
|
||||
'account_id' => $_account_id,
|
||||
'notify_type' => self::_type
|
||||
),
|
||||
__LINE__,__FILE__,0 ,'',self::_appname)->fetchColumn();
|
||||
$result = array();
|
||||
if ($rs->NumRows() > 0) {
|
||||
foreach ($rs as $notification) {
|
||||
'notify_type' => self::_type,
|
||||
'notify_created > ' . $cut_off,
|
||||
], __LINE__, __FILE__, $n, 'ORDER BY notify_id DESC', self::_appname, $chunk_size) as $notification)
|
||||
{
|
||||
$actions = null;
|
||||
$data = json_decode($notification['notify_data'], true);
|
||||
if (!empty($data['appname']) && !empty($data['data']))
|
||||
@ -168,7 +180,7 @@ class notifications_popup implements notifications_iface {
|
||||
), $data['appname'], true);
|
||||
$actions = $_actions[$data['appname']];
|
||||
}
|
||||
$result[] = array(
|
||||
$data = [
|
||||
'id' => $notification['notify_id'],
|
||||
'message' => $notification['notify_message'],
|
||||
'status' => $notification['notify_status'],
|
||||
@ -176,10 +188,32 @@ class notifications_popup implements notifications_iface {
|
||||
'current' => new Api\DateTime('now'),
|
||||
'actions' => is_array($actions) ? $actions : NULL,
|
||||
'extra_data' => $data['data'] ?? [],
|
||||
);
|
||||
|
||||
];
|
||||
// aggregate by app:id reporting only the newest entry
|
||||
if (!empty($data['extra_data']['id']))
|
||||
{
|
||||
if (!isset($result[$id = $data['extra_data']['app'] . ':' . $data['extra_data']['id']]))
|
||||
{
|
||||
$result[$id] = $data;
|
||||
}
|
||||
return ['rows' => $result, 'total'=> $total];
|
||||
else
|
||||
{
|
||||
$total--;
|
||||
/* in case we want to show all
|
||||
$result['id']['others'][] = $data;
|
||||
*/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[] = $data;
|
||||
}
|
||||
}
|
||||
$n += $chunk_size;
|
||||
}
|
||||
while(!$notification || count($result) < min($num_rows, $total));
|
||||
|
||||
return ['rows' => array_values($result), 'total'=> $total];
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +307,8 @@ class notifications_popup implements notifications_iface {
|
||||
*
|
||||
* @param settings array with keys account_id and new_owner (new_owner is optional)
|
||||
*/
|
||||
public static function deleteaccount($settings) {
|
||||
public static function deleteaccount($settings)
|
||||
{
|
||||
$GLOBALS['egw']->db->delete( self::_notification_table, array(
|
||||
'account_id' => $settings['account_id']
|
||||
),__LINE__,__FILE__,self::_appname);
|
||||
|
Loading…
Reference in New Issue
Block a user