From 9134a0bc512d7de45fcf645e5ce9b9c3e11bd406 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Fri, 12 Mar 2021 10:22:09 +0100 Subject: [PATCH] * Notifications: fix notification sub messages (grouped entries) would still show up even though their parent message once been deleted --- .../inc/class.notifications_ajax.inc.php | 112 ++++++++++++------ .../inc/class.notifications_popup.inc.php | 24 ++-- notifications/js/notificationajaxpopup.js | 57 +++++---- 3 files changed, 119 insertions(+), 74 deletions(-) diff --git a/notifications/inc/class.notifications_ajax.inc.php b/notifications/inc/class.notifications_ajax.inc.php index 0930a8a125..577da8ea09 100644 --- a/notifications/inc/class.notifications_ajax.inc.php +++ b/notifications/inc/class.notifications_ajax.inc.php @@ -145,11 +145,12 @@ class notifications_ajax { /** * Remove given notification id(s) from the table * - * @param array|int $notify_ids one or multiple notify_id(s) + * @param array $notifymessages one or multiple notify_id(s) */ - public function delete_message($notify_ids) + public function delete_message($notifymessages) { - if ($notify_ids) + $notify_ids = $this->fetch_notify_ids($notifymessages); + if (!empty($notify_ids)) { $this->db->delete(self::_notification_table,array( 'notify_id' => $notify_ids, @@ -157,12 +158,13 @@ class notifications_ajax { 'notify_type' => self::_type ),__LINE__,__FILE__,self::_appname); } + $this->response->data(['deleted'=>$notify_ids]); } /** * Method to update message(s) status * - * @param int|array $notify_ids one or more notify_id(s) + * @param array $notifymessages one or more notify_id(s) * @param string $status = SEEN, status of message: * - SEEN: message has been seen * - UNSEEN: message has not been seen @@ -170,9 +172,10 @@ class notifications_ajax { * this status has been used more specifically for browser type * of notifications. */ - public function update_status($notify_ids,$status = "SEEN") + public function update_status($notifymessages, $status = "SEEN") { - if ($notify_ids) + $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, @@ -182,43 +185,76 @@ class notifications_ajax { } } + /** + * gets all relevant notify ids based on given notify message data + * @param $notifymessages + * @return array + */ + public function fetch_notify_ids ($notifymessages) + { + $notify_ids = []; + + foreach ($notifymessages as $data) + { + if (is_array($data) && $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']))); + } + else + { + array_push($notify_ids, (string)$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) + { + try { + // mariaDB supported query + $ret = $this->db->select(self::_notification_table, 'notify_id', array( + '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); + } + catch (Api\Db\Exception $e) { + // do it manual for all other DB + foreach($this->db->select(self::_notification_table, '*', array( + 'account_id' => $this->recipient->account_id, + 'notify_type' => self::_type + ), + __LINE__,__FILE__,0 ,'ORDER BY notify_id DESC',self::_appname) as $row) + { + $data = json_decode($row['notify_data'], true); + if ($data['appname'] == $_appname && $data['data']['id'] == $_id) $ret[] = $row['notify_id']; + } + } + } + return $ret; + } /** * gets all egwpopup notifications for calling user * * @return boolean true or false */ - private function get_egwpopup($browserNotify = false) { - $rs = $this->db->select(self::_notification_table, '*', array( - 'account_id' => $this->recipient->account_id, - 'notify_type' => self::_type - ), - __LINE__,__FILE__,0 ,'ORDER BY notify_id DESC',self::_appname, 100); - $result = array(); - if ($rs->NumRows() > 0) { - foreach ($rs as $notification) { - $actions = null; - $data = json_decode($notification['notify_data'], true); - if ($data['appname'] && $data['data']) - { - $_actions = Api\Hooks::process (array( - 'location' => 'notifications_actions', - 'data' => $data['data'] - ), $data['appname'], true); - $actions = $_actions[$data['appname']]; - } - $result[] = array( - 'id' => $notification['notify_id'], - 'message' => $notification['notify_message'], - 'status' => $notification['notify_status'], - 'created' => Api\DateTime::to($notification['notify_created']), - 'current' => new DateTime(), - 'actions' => is_array($actions)?$actions:NULL, - 'extra_data' => ($data['data'] ? $data['data'] : array()) - ); - - } - $this->response->apply('app.notifications.append', array($result, $browserNotify)); - } + private function get_egwpopup($browserNotify = false) + { + $entries = notifications_popup::read($this->recipient->account_id); + $this->response->apply('app.notifications.append', array($entries['rows'], $browserNotify, $entries['total'])); return true; } diff --git a/notifications/inc/class.notifications_popup.inc.php b/notifications/inc/class.notifications_popup.inc.php index 007f280cfb..3452e5ec6a 100644 --- a/notifications/inc/class.notifications_popup.inc.php +++ b/notifications/inc/class.notifications_popup.inc.php @@ -130,21 +130,31 @@ class notifications_popup implements notifications_iface { ), 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); - $push->call('app.notifications.append', $this->read()); + $entries = self::read($this->recipient->account_id); + $push->call('app.notifications.append', $entries['rows'], null, $entries['total']); } /** - * read all notification messages for current recipient - * @return type + * read all notification messages for given recipient + * @param $_account_id + * @return array */ - private function read() + public static function read($_account_id) { - $rs = $this->db->select(self::_notification_table, '*', array( - 'account_id' => $this->recipient->account_id, + if (!$_account_id) return []; + + $rs = $GLOBALS['egw']->db->select(self::_notification_table, '*', array( + '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( + 'account_id' => $_account_id, + 'notify_type' => self::_type + ), + __LINE__,__FILE__,0 ,'',self::_appname)->fetchColumn(); $result = array(); if ($rs->NumRows() > 0) { foreach ($rs as $notification) { @@ -169,7 +179,7 @@ class notifications_popup implements notifications_iface { ); } - return $result; + return ['rows' => $result, 'total'=> $total]; } } diff --git a/notifications/js/notificationajaxpopup.js b/notifications/js/notificationajaxpopup.js index 355134e515..62e6e93d6b 100644 --- a/notifications/js/notificationajaxpopup.js +++ b/notifications/js/notificationajaxpopup.js @@ -92,7 +92,8 @@ this.run_notifications(); this.filter = ''; - + // total number of notifications + this.total = 0; }; notifications.prototype.run_notifications = function () @@ -516,14 +517,9 @@ _event.stopPropagation(); var egwpopup_message = _node[0]; var id = egwpopup_message[0].id.replace(/egwpopup_message_/ig,''); - var ids = [id]; - if (notifymessages[id]['children']) - { - ids = ids.concat(Object.keys(notifymessages[id]['children'])); - } if (notifymessages[id]['status'] !='SEEN') { - var request = egw.json("notifications.notifications_ajax.update_status", [ids, "SEEN"]); + var request = egw.json("notifications.notifications_ajax.update_status", [[notifymessages[id]], "SEEN"]); request.sendRequest(true); this.update_message_status(id, "SEEN"); if (notifymessages[id]['extra_data']['onSeenAction']) @@ -537,9 +533,7 @@ notifications.prototype.mark_all_seen = function() { if (!notifymessages || Object.keys(notifymessages).length == 0) return false; - var ids = Object.keys(notifymessages); - ids = ids.concat(this.findAllChildrenIds()); - egw.json("notifications.notifications_ajax.update_status", [ids, "SEEN"]).sendRequest(true); + egw.json("notifications.notifications_ajax.update_status", [notifymessages, "SEEN"]).sendRequest(true); for (var id in notifymessages) { this.update_message_status(id, "SEEN"); @@ -568,12 +562,13 @@ notifications.prototype.delete_all = function () { if (!notifymessages || Object.entries(notifymessages).length == 0) return false; - var ids = Object.keys(notifymessages); - ids = ids.concat(this.findAllChildrenIds()); - egw.json("notifications.notifications_ajax.delete_message", [ids]).sendRequest(true); + var self = this; + egw.json("notifications.notifications_ajax.delete_message", [notifymessages], function(_data){ + if (_data && _data['deleted']) self.total -= Object.keys(_data['deleted']).length; + self.counterUpdate(); + }).sendRequest(true); notifymessages = {}; jQuery("#egwpopup_list").empty(); - this.counterUpdate(); egw.loading_prompt('popup_notifications', false); }; @@ -584,12 +579,11 @@ _event.stopPropagation(); var egwpopup_message = _node[0]; var id = egwpopup_message[0].id.replace(/egwpopup_message_/ig,''); - var ids = [id]; - if (notifymessages[id]['children']) - { - ids = ids.concat(Object.keys(notifymessages[id]['children'])); - } - var request = egw.json("notifications.notifications_ajax.delete_message", [ids]); + var self = this; + var request = egw.json("notifications.notifications_ajax.delete_message", [[notifymessages[id]]],function(_data){ + if (_data && _data['deleted']) self.total -= Object.keys(_data['deleted']).length; + self.counterUpdate(); + }); request.sendRequest(true); var nextNode = egwpopup_message.next(); var keepLoadingPrompt = false; @@ -604,8 +598,6 @@ if (keepLoadingPrompt && !egwIsMobile()) egw.loading_prompt('popup_notifications', true); egwpopup_message.remove(); this.bell("inactive"); - this.counterUpdate(); - }; /** @@ -661,16 +653,18 @@ * @param _rowData * @param _browser_notify */ - notifications.prototype.append = function(_rawData, _browser_notify) { + notifications.prototype.append = function(_rawData, _browser_notify, _total) { var hasUnseen = []; + _rawData = _rawData || []; // Dont process the data if they're the same as it could get very expensive to // proccess html their content. if (_currentRawData.length>0 && _currentRawData.length == _rawData.length) return; - _currentRawData = _rawData; - let old_notifymessages = notifymessages; + _currentRawData = _rawData || []; + var old_notifymessages = notifymessages; notifymessages = {}; var browser_notify = _browser_notify || this.check_browser_notify(); + this.total = _total || 0; for (var i=0; i < _rawData.length; i++) { var data = this.getData(_rawData[i]['message'], _rawData[i]['extra_data']); @@ -699,7 +693,8 @@ status: _rawData[i]['status'], created: _rawData[i]['created'], current: _rawData[i]['current'], - extra_data: _rawData[i]['extra_data'] + extra_data: _rawData[i]['extra_data'], + id: _rawData[i]['id'] }; if (_rawData[i]['actions'] && _rawData[i]['actions'].length > 0) notifymessages[_rawData[i]['id']]['data']['actions'] = _rawData[i]['actions']; // Notification API @@ -714,7 +709,7 @@ // notification id var id = this.tag.split(":"); // delete the message - var request = egw.json("notifications.notifications_ajax.update_status", [id[1], 'DISPLAYED']); + var request = egw.json("notifications.notifications_ajax.update_status", [[id[1]], 'DISPLAYED']); request.sendRequest(); }, onclick:function(e){ @@ -740,12 +735,12 @@ } if (!_rawData[i]['status']) { - egw.json("notifications.notifications_ajax.update_status", [_rawData[i]['id'], 'DISPLAYED']); + egw.json("notifications.notifications_ajax.update_status", [[_rawData[i]['id']], 'DISPLAYED']); hasUnseen.push(_rawData[i]['id']); } } - let egwpopup = document.getElementById('egwpopup'); + var egwpopup = document.getElementById('egwpopup'); switch(egw.preference('egwpopup_verbosity', 'notifications')) { case 'low': @@ -854,6 +849,10 @@ var counter = 0; var apps = {}; + // set total number + document.getElementById("egwpopup_header").childNodes[0].textContent = (egw.lang("Notifications")+" ("+this.total+")"); + document.getElementById('topmenu_info_notifications').title = egw.lang('total')+":"+this.total; + for (var id in notifymessages) { if (typeof apps[notifymessages[id]['extra_data']['app']] == 'undefined')