private static function flushNotificationBuffer($doGrouping = true)
 {
     if (!empty(self::$buffer)) {
         $sites = self::getSitePair();
         $byUser = array();
         foreach (self::$buffer as $i => $message) {
             if (is_array($message['TO_USER_IDS'])) {
                 foreach ($message['TO_USER_IDS'] as $userId) {
                     // determine notify event here, if it was not given
                     if ((string) $message['NOTIFY_EVENT'] == '') {
                         $notifyEvent = 'manage';
                         if ($message['ADDITIONAL_DATA']['TASK_ASSIGNED_TO'] !== null) {
                             if ($userId == $message['ADDITIONAL_DATA']['TASK_ASSIGNED_TO']) {
                                 $notifyEvent = 'task_assigned';
                             }
                         }
                     } else {
                         $notifyEvent = $message['NOTIFY_EVENT'];
                     }
                     $byUser[$userId][$message['TASK_ID']] = array_merge($message, array('NOTIFY_EVENT' => $notifyEvent));
                 }
             }
         }
         $users = self::getUsers(array_keys($byUser));
         foreach ($byUser as $userId => $messages) {
             if (!isset($users[$userId])) {
                 continue;
             }
             $unGroupped = array();
             if (count($messages) > 1 && $doGrouping) {
                 // send for each action type, notification type and author separately
                 $deepGrouping = array();
                 foreach ($messages as $taskId => $message) {
                     // we do not group entities that differ from 'TASK' and NOTIFY_EVENTS that differ from 'manage'
                     if ($message['ENTITY_CODE'] != 'TASK' || $message['NOTIFY_EVENT'] != 'manage') {
                         $unGroupped[$taskId] = $message;
                         continue;
                     }
                     // if type is unknown, let it be "update"
                     $type = (string) $message['EVENT_DATA']['ACTION'] !== '' ? $message['EVENT_DATA']['ACTION'] : 'TASK_UPDATE';
                     if ($type != 'TASK_ADD' && $type != 'TASK_UPDATE' && $type != 'TASK_DELETE' && $type != 'TASK_STATUS_CHANGED_MESSAGE') {
                         // unknown action type. nothing to report about
                         continue;
                     }
                     $fromUserId = $message['FROM_USER_ID'];
                     if ((string) $fromUserId == '') {
                         continue;
                     }
                     $deepGrouping[$type][$fromUserId][$message['NOTIFY_EVENT']][] = $taskId;
                 }
                 if (!empty($deepGrouping)) {
                     foreach ($deepGrouping as $type => $byAuthor) {
                         foreach ($byAuthor as $authorId => $byEvent) {
                             foreach ($byEvent as $event => $taskIds) {
                                 $path = CTaskNotifications::getNotificationPathMultiple($users[$userId], $taskIds, true);
                                 $instantTemplate = self::getGenderMessage($authorId, 'TASKS_TASKS_' . $type . '_MESSAGE');
                                 $emailTemplate = self::getGenderMessage($authorId, 'TASKS_TASKS_' . $type . '_MESSAGE_EMAIL');
                                 $pushTemplate = self::getGenderMessage($authorId, 'TASKS_TASKS_' . $type . '_MESSAGE_PUSH');
                                 $instant = self::placeLinkAnchor($instantTemplate, $path, 'BBCODE');
                                 $email = self::placeLinkAnchor($emailTemplate, $path, 'EMAIL');
                                 $push = self::placeLinkAnchor('#USER_NAME# ' . $pushTemplate, $path, 'NONE');
                                 $tag = static::formatImNotificationTag($userId, $taskIds, 'TASKS');
                                 $arMessageFields = array("TO_USER_ID" => $userId, "FROM_USER_ID" => $authorId, "NOTIFY_TYPE" => IM_NOTIFY_FROM, "NOTIFY_MODULE" => 'tasks', "NOTIFY_EVENT" => $event, "NOTIFY_MESSAGE" => $instant, "NOTIFY_MESSAGE_OUT" => $email, "NOTIFY_TAG" => $tag, "PUSH_MESSAGE" => substr($push, 0, self::PUSH_MESSAGE_MAX_LENGTH));
                                 CIMNotify::Add($arMessageFields);
                             }
                         }
                     }
                 }
             } else {
                 $unGroupped = $messages;
             }
             // send each message separately
             foreach ($unGroupped as $taskId => $message) {
                 $pathToTask = self::GetNotificationPath($users[$userId], $taskId, true, $sites);
                 $pathToTask = self::addParameters($pathToTask, $message['ADDITIONAL_DATA']['TASK_URL']);
                 $message['ENTITY_CODE'] = ToUpper($message['ENTITY_CODE']);
                 $message['MESSAGE']['INSTANT'] = self::placeLinkAnchor($message['MESSAGE']['INSTANT'], $pathToTask, 'BBCODE');
                 $message['MESSAGE']['EMAIL'] = self::placeLinkAnchor($message['MESSAGE']['EMAIL'], $pathToTask, 'EMAIL');
                 if ((string) $message['MESSAGE']['PUSH'] != '') {
                     $message['MESSAGE']['PUSH'] = self::placeLinkAnchor($message['MESSAGE']['PUSH'], $pathToTask, 'NONE');
                 }
                 $message['TO_USER_IDS'] = array($userId);
                 // message callbacks here
                 if (is_callable($message['CALLBACK']['BEFORE_SEND'])) {
                     $message = call_user_func_array($message['CALLBACK']['BEFORE_SEND'], array($message));
                 }
                 // event process here
                 if (!static::fireMessageEvent($message)) {
                     continue;
                 }
                 $userId = $message['TO_USER_IDS'][0];
                 // it may have changed on event
                 // make IM parameters
                 // todo make tag format more sutable
                 $entityIds = array();
                 if ('COMMENT' == $message['ENTITY_CODE']) {
                     $entityIds = array(intval($message['EVENT_DATA']['MESSAGE_ID']));
                 }
                 $tag = static::formatImNotificationTag($userId, array($taskId), $message['ENTITY_CODE'], $entityIds);
                 $arMessageFields = array("TO_USER_ID" => $userId, "FROM_USER_ID" => $message['FROM_USER_ID'], "NOTIFY_TYPE" => IM_NOTIFY_FROM, "NOTIFY_MODULE" => "tasks", "NOTIFY_EVENT" => $message['NOTIFY_EVENT'], "NOTIFY_TAG" => $tag, "NOTIFY_MESSAGE" => $message['MESSAGE']['INSTANT'], "NOTIFY_MESSAGE_OUT" => $message['MESSAGE']['EMAIL']);
                 if ((string) $message['ADDITIONAL_DATA']['NOTIFY_ANSWER']) {
                     // enabling notify answer for desktop
                     $arMessageFields['NOTIFY_ANSWER'] = 'Y';
                 }
                 if ((string) $message['MESSAGE']['PUSH'] != '') {
                     // add push message
                     $arMessageFields['PUSH_MESSAGE'] = self::placeLinkAnchor($message['MESSAGE']['PUSH'], $pathToTask, 'NONE');
                     // user should be able to open the task window to see the changes ...
                     // see /mobile/install/components/bitrix/mobile.rtc/templates/.default/script.js for handling details
                     $arMessageFields['PUSH_PARAMS'] = array('ACTION' => 'tasks', 'TAG' => $tag);
                     if ((string) $message['ADDITIONAL_DATA']['NOTIFY_ANSWER']) {
                         // ... and open an answer dialog in mobile
                         $arMessageFields['PUSH_PARAMS'] = array_merge($arMessageFields['PUSH_PARAMS'], array('CATEGORY' => 'ANSWER', 'URL' => SITE_DIR . 'mobile/ajax.php?mobile_action=task_answer', 'PARAMS' => array('TASK_ID' => $taskId)));
                     }
                 }
                 CIMNotify::Add($arMessageFields);
             }
         }
     }
     self::$buffer = array();
 }