<?php /** * Chat-Benachrichtigungen * * - Cronjob läuft alle 15 Minuten * - Cronjob prüft ob es Chat-Nachrichten gibt die seit 30 Minuten ungelesen sind. * Benutzer wird dann per Mail über ungelesene Chat-Nachrichten informiert. */ if(!class_exists('app_t2')) { class app_t2 extends ApplicationCore { public $DB; public $user; } } $DEBUG = 0; if(empty($app) || !class_exists('ApplicationCore') || !($app instanceof ApplicationCore)) { $app = new app_t2(); } if(empty($app->Conf)) { $conf = new Config(); $app->Conf = $conf; } if(empty($app->DB) || empty($app->DB->connection)) { $app->DB = new DB($app->Conf->WFdbhost, $app->Conf->WFdbname, $app->Conf->WFdbuser, $app->Conf->WFdbpass, $app, $app->Conf->WFdbport); } if(empty($app->erp)) { if(class_exists('erpAPICustom')) { $erp = new erpAPICustom($app); } else { $erp = new erpAPI($app); } $app->erp = $erp; } // Alle Benutzer ermitteln die seit 30 Minuten ungelesene private Nachrichten haben // u.kalender_ausblenden = "Im Kalender/Chat ausblenden"; außerdem keine Chat-Benachrichtigung empfangen $usersWithUnreadPrivateMessages = $app->DB->SelectArr( 'SELECT c.user_to AS id, MAX(c.zeitstempel) AS message_date, COUNT(c.id) AS message_count FROM `user` AS u INNER JOIN `chat` AS c ON c.user_to = u.id AND c.user_to <> 0 LEFT JOIN `chat_gelesen` AS g ON c.id = g.message WHERE g.id IS NULL AND u.activ = 1 AND u.kalender_ausblenden != 1 AND DATE_ADD(c.zeitstempel, INTERVAL 30 MINUTE) < NOW() GROUP BY c.user_to' ); // Alle Benutzer ermitteln die seit 30 Minuten ungelesene öffentliche Nachrichten haben // u.kalender_ausblenden = "Im Kalender/Chat ausblenden"; außerdem keine Chat-Benachrichtigung empfangen $usersWithUnreadPublicMessages = $app->DB->SelectArr( 'SELECT u.id, MAX(c.zeitstempel) AS message_date, COUNT(c.id) AS message_count FROM `user` AS u INNER JOIN `chat` AS c ON c.zeitstempel > u.logdatei AND c.user_to = 0 LEFT JOIN `chat_gelesen` AS g ON c.id = g.message AND g.user = u.id WHERE g.id IS NULL AND u.activ = 1 AND u.kalender_ausblenden != 1 AND DATE_ADD(c.zeitstempel, INTERVAL 30 MINUTE) < NOW() GROUP BY u.id' ); // Benutzer zusammenführen $usersWithUnreadMessages = combineUserResult( $usersWithUnreadPrivateMessages, $usersWithUnreadPublicMessages ); if(empty($usersWithUnreadMessages)){ return; } foreach ($usersWithUnreadMessages as $user) { $previousDate = (int)getUserCacheValue($user['id']); $currentDate = max((int)$user['private_date'], (int)$user['public_date']); // Datum gleich > User hat bereits Benachrichtigung bekommen + keine neuen Nachrichten vorhanden > Benachrichtigung NICHT senden // Datum ungleich > User hat bereits Benachrichtigung bekommen + Neue Nachrichten vorhanden > Benachrichtigung senden // Kein Datum hinterlegt > User hat noch nie Benachrichtigung bekommen > Benachrichtigung senden // if(empty($previousDate) || $previousDate !== $currentDate){ sendNotificationMail($user['id'], $user['private_count'], $user['public_count']); setUserCacheValue($user['id'], $currentDate); // } } /***************************************** ENDE: Ab hier nur Funktionen ******************************************/ /** * Timestamp der letzten ungelesenen Chat-Nachricht abrufen, zu der eine Mail-Benachrichtigung versendet wurde * * Soll verhindern dass Mail-Benachrichtigungen mehrfach versendet werden. * * @param int $userId * * @return string|false */ function getUserCacheValue($userId) { global $app; $result = $app->DB->Select( "SELECT k.value FROM userkonfiguration AS k WHERE k.name = 'chat_unread_message_date' AND k.user = '{$userId}' LIMIT 1" ); if(empty($result)){ return false; } return $result; } /** * Timestamp wegspeichern, der letzten ungelesenen Chat-Nachricht zu der eine Mail-Benachrichtigung versendet wurde * * Soll verhindern dass Mail-Benachrichtigungen mehrfach versendet werden. * * @param int $userId * @param string $cacheValue * * @return void */ function setUserCacheValue($userId, $cacheValue) { global $app; $userConfigId = (int)$app->DB->Select( "SELECT k.id FROM userkonfiguration AS k WHERE k.name = 'chat_unread_message_date' AND k.user = '{$userId}' LIMIT 1" ); if($userConfigId > 0){ $app->DB->Update( "UPDATE userkonfiguration SET `value` = '{$cacheValue}' WHERE `user` = '{$userId}' AND `name` = 'chat_unread_message_date' LIMIT 1" ); }else{ $app->DB->Insert( "INSERT INTO userkonfiguration (`user`, `name`, `value`) VALUES ('{$userId}', 'chat_unread_message_date', '{$cacheValue}')" ); } } /** * @param array $private * @param array $public * * @return array */ function combineUserResult($privates = [], $publics = []) { if (empty($privates)) { $privates = []; } if (empty($publics)) { $publics = []; } $result = []; foreach ($privates as $user) { $userId = $user['id']; $result[$userId]['id'] = $userId; $result[$userId]['private_date'] = strtotime($user['message_date']); $result[$userId]['private_count'] = $user['message_count']; } foreach ($publics as $user) { $userId = $user['id']; $result[$userId]['id'] = $userId; $result[$userId]['public_date'] = strtotime($user['message_date']); $result[$userId]['public_count'] = $user['message_count']; } return $result; } /** * Mail-Benachrichtung senden * * @param int $userId * @param array $privateMessages * @param array $publicMessages * * @return bool */ function sendNotificationMail($userId, $privateMessages, $publicMessages) { global $app; if((int)$userId === 0){ return false; } if((int)$publicMessages === 0 && (int)$privateMessages === 0){ return false; } $toMail = $app->DB->Select( "SELECT a.email FROM `user` AS u INNER JOIN adresse a ON a.id = u.adresse WHERE u.id = '{$userId}' AND u.activ = 1 LIMIT 1" ); $toName = $app->DB->Select( "SELECT a.name FROM `user` AS u INNER JOIN adresse a ON a.id = u.adresse WHERE u.id = '{$userId}' AND u.activ = 1 LIMIT 1" ); if(empty($toMail)){ return false; } $fromMail = $app->erp->GetFirmaMail(); $fromName = $app->erp->GetFirmaName(); $subject = 'Ungelesene Chat-Nachrichten'; $totalMessageCount = (int)$privateMessages + (int)$publicMessages; $messages = GetNewestMessagesForUser($userId); if (empty($messages)) { return; } /* // Profilbilder einbinden $userFromIds = array_unique(array_column($messages, 'user_from')); foreach ($userFromIds as $userFromId) { $profileImage = GetProfilImage($userFromId); $app->mail->AddEmbeddedImage($profileImage['path'], $profileImage['cid'], $profileImage['name']); }*/ // $text = GetHtmlMessage($toName, $totalMessageCount, $messages); ?!? -> IF we want to use formatted mails, we will have to make use of templates // Very lean implementation: $text = "Neue Chat-Nachrichten vorhanden.</br></br>"; foreach ($messages as $message) { $zeitstempel = strtotime($message['zeitstempel']); $message['zeitstempel'] = 'am ' . date('d.m.Y', $zeitstempel) . ' um ' . date('H:i', $zeitstempel) .' Uhr'; $text .= "\"".$message['message']."\"</br>"; if (empty($message['user_to'])) { $text .= "<i>(öffentlich)</i></br>"; } $text .= "<i>".$message['user_from_name']." ".$message['zeitstempel']."</i></br></br>"; $text = htmlspecialchars($text); } // function MailSend($from,$from_name,$to,$to_name,$betreff,$text,$files="",$projekt="",$signature=true,$cc="",$bcc="", $system = false) $app->erp->MailSend($fromMail,$fromName,$toMail,$toName,$subject,$text,"","",false,"","", true); $app->erp->LogFile("Mailed ".$subject." to ".$toMail); } /** * Chat-Nachrichten für Mail ermitteln * * Benötigt wird nur die neueste Nachricht pro Absender. * Und die neueste Nachricht aus dem öffentlichen Raum. * * @param int $userId * * @return array */ function GetNewestMessagesForUser($userId) { global $app; $lastUnreadMessageTimestamp = getUserCacheValue($userId); if (empty($lastUnreadMessageTimestamp)) { $lastUnreadMessageTimestamp = time(); } $privateMessages = $app->DB->SelectArr( 'SELECT c.user_to, c.user_from, a.name AS user_from_name, c.message, c.zeitstempel FROM chat AS c INNER JOIN `user` AS u ON c.user_from = u.id INNER JOIN `adresse` AS a ON u.adresse = a.id INNER JOIN ( SELECT MAX(c.id) AS message_id FROM chat AS c LEFT JOIN `chat_gelesen` AS g ON c.id = g.message WHERE g.id IS NULL AND c.user_to <> 0 GROUP BY c.user_from, c.user_to ) AS newest_messages ON c.id = newest_messages.message_id WHERE c.user_to = ' . (int)$userId . ' AND u.activ = 1 AND UNIX_TIMESTAMP(c.zeitstempel) > ' . $lastUnreadMessageTimestamp . ' GROUP BY c.user_from' ); $publicMessages = $app->DB->SelectArr( 'SELECT c.user_to, c.user_from, a.name AS user_from_name, c.message, c.zeitstempel FROM chat AS c INNER JOIN `user` AS u ON c.user_from = u.id INNER JOIN `adresse` AS a ON u.adresse = a.id INNER JOIN ( SELECT MAX(c.id) AS message_id FROM chat AS c WHERE c.user_to = 0 GROUP BY c.user_to ) AS newest_messages ON c.id = newest_messages.message_id LEFT JOIN `chat_gelesen` AS g ON c.id = g.message AND g.user = u.id AND u.id = ' . (int)$userId . ' WHERE u.activ = 1 AND g.id IS NULL AND UNIX_TIMESTAMP(c.zeitstempel) > ' . $lastUnreadMessageTimestamp ); if (empty($privateMessages)) { $privateMessages = []; } if (empty($publicMessages)) { $publicMessages = []; } return array_merge($privateMessages, $publicMessages); } /** * Profilbild pro User ermitteln * * @param int $userId * * @return array */ function GetProfilImage($userId) { global $app; if((int)$userId > 0){ $adresse = (int)$app->DB->Select( "SELECT a.id FROM user u LEFT JOIN adresse a ON a.id = u.adresse LEFT JOIN adresse_rolle ar ON ar.adresse = a.id WHERE u.activ = 1 AND u.kalender_ausblenden != 1 AND ar.subjekt = 'Mitarbeiter' AND (ar.bis = '0000-00-00' OR ar.bis <= NOW()) AND u.id = '{$userId}' AND ((u.hwtoken <> 4 ) OR u.hwtoken IS NULL) LIMIT 1" ); if($adresse > 0){ $dateiversion = (int)$app->DB->Select( "SELECT dv.id FROM datei_stichwoerter ds INNER JOIN datei d ON ds.datei = d.id INNER JOIN datei_version dv ON dv.datei = d.id WHERE d.geloescht = 0 AND objekt LIKE 'Adressen' AND parameter = '{$adresse}' AND subjekt LIKE 'Profilbild' ORDER by dv.id DESC LIMIT 1" ); if($dateiversion > 0){ $userdata = isset($app->Conf->WFuserdata) ? $app->Conf->WFuserdata : str_replace('index.php', '', $_SERVER['SCRIPT_FILENAME']) . '../userdata'; $path = $userdata . '/dms/' . $app->Conf->WFdbname; $cachefolder = $path . '/cache'; $cachefolder = $app->erp->GetDMSPath($dateiversion . '_100_100', $cachefolder, true); if(file_exists($cachefolder . '/' . $dateiversion . '_100_100')){ $type = mime_content_type($cachefolder . '/' . $dateiversion . '_100_100'); switch ($type) { case 'image/jpg': case 'image/jpeg': return [ 'cid' => 'user_' . $userId, 'name' => 'user_' . $userId . '.jpg', 'path' => $cachefolder . '/' . $dateiversion . '_100_100', ]; case 'image/png': return [ 'cid' => 'user_' . $userId, 'name' => 'user_' . $userId . '.png', 'path' => $cachefolder . '/' . $dateiversion . '_100_100', ]; case 'image/gif': return [ 'cid' => 'user_' . $userId, 'name' => 'user_' . $userId . '.gif', 'path' => $cachefolder . '/' . $dateiversion . '_100_100', ]; case 'application/pdf': return [ 'cid' => 'user_' . $userId, 'name' => 'user_' . $userId . '.png', 'path' => dirname(__DIR__) . '/themes/new/images/pdf.svg', ]; } } } } } return [ 'cid' => 'user_' . $userId, 'name' => 'user_' . $userId . '.png', 'path' => dirname(__DIR__) . '/www/themes/new/images/profil.png', ]; } /** * HTML-Nachrichteninhalt zusammenstellen * * @param string $receipientName * @param int $messageTotalCount * @param array $messages * * @return string HTML-Template */ function GetHtmlMessage($receipientName, $messageTotalCount, $messages = []) { $template = <<<HTML <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>[OpenXE] Ungelesene Chat-Nachrichten</title> <style type="text/css"> body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; width: 100%; max-width: 100%; font-size: 17px; line-height: 24px; color: #48494B; background: #F5F5F5; } h1, h2, h3, h4 { color: #42B8C4; margin-bottom: 12px; line-height: 26px; } p, ul, ul li { font-size: 17px; margin: 0; margin-bottom: 16px; line-height: 24px; } ul { margin-bottom: 24px; } ul li { margin-bottom: 8px; } hr { margin: 1.5rem 0; width: 100%; border: none; border-bottom: 1px solid #ECECEC; } a, a:link, a:visited, a:active, a:hover { font-weight: bold; color: #48494B; text-decoration: none; word-break: break-word; } a:active, a:hover { text-decoration: underline; } body { width: 100% !important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; margin: 0 auto; padding: 0; } </style> </head> <body style="background: #F5F5F5; color: #48494B; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 17px; line-height: 24px; max-width: 100%; width: 100% !important; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; margin: 0 auto; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; line-height: 24px; margin: 0; padding: 0; width: 100%; font-size: 17px; color: #48494B; background: #F5F5F5;"> <tr> <td valign="top" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; border-collapse: collapse;"> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> <tr> <td valign="bottom" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; border-collapse: collapse; padding: 20px 15px 10px 15px;"> <div style="text-align: center;"> <a href="https://xentral.biz/" style="color: #42B8C4; font-weight: bold; text-decoration: none; word-break: break-word;"> <img src="cid:logo" width="197" height="33" style="-ms-interpolation-mode: bicubic; outline: none; text-decoration: none; border: none;"></a> </div> </td> </tr> </table> </td> </tr> <tr> <td valign="top" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; border-collapse: collapse;"> <table cellpadding="32" cellspacing="0" border="0" align="center" style="max-width: 600px; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: white; border-radius: 0.5rem; margin: 0 auto; margin-bottom: 1rem;"> <tr> <td width="546" valign="top" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; border-collapse: collapse;"> <div style="max-width: 600px; margin: 0 auto;"> <h2 style="color: #0E8394; line-height: 30px; margin: 0; margin-bottom: 12px;">Hallo {$receipientName}!</h2> <p style="font-size: 17px; line-height: 24px; margin: 0; margin-bottom: 16px;"> Es gibt {$messageTotalCount} neue Chat-Nachrichten im <strong>Xentral ERP</strong>. </p> </div> HTML; $privateHeaderViewed = false; foreach ($messages as $message) { $zeitstempel = strtotime($message['zeitstempel']); $message['zeitstempel'] = 'vom ' . date('d.m.Y', $zeitstempel) . ' um ' . date('H:i', $zeitstempel) .' Uhr'; $template .= '<hr style="border: none; border-bottom: 1px solid #ECECEC; margin: 1.5rem 0; width: 100%;">'; if ((int)$message['user_to'] === 0) { $template .= '<h4 style="color: #A2C55A; line-height: 18px; font-size: 18px; margin: 0; margin-bottom: 12px; ">Neueste öffentliche Nachricht</h4>'; } if ((int)$message['user_to'] > 0 && $privateHeaderViewed === false) { $template .= '<h4 style="color: #F69E06; line-height: 18px; font-size: 18px; margin: 0; margin-bottom: 12px; ">Ungelesene private Nachrichten</h4>'; $privateHeaderViewed = true; } $template .= '<div style="margin: 0 0 10px 0; width: 100%; word-break: break-word; clear: left; font-size: 15px; line-height: 18px; color: #48494B; min-height: 36px;">' . '<img src="cid:user_' . $message['user_from'] . '" style="-ms-interpolation-mode: bicubic; outline: none; text-decoration: none; border: none; float: left; margin-right: 8px; border-radius: 4px; display: inline-block; width: 40px; height: 40px;">' . '<div style="padding-left: 48px;">'. '<div>'. '<strong>'. $message['user_from_name'] . '</strong> '. '<small style="color:#999;">' . $message['zeitstempel'] . '</small>'. '</div>'. '<div style="max-width: 488px; word-wrap: break-word;overflow-wrap: break-word; word-break: break-word;">'.htmlspecialchars($message['message']).'</div>'. '</div>'. '</div>'; } $template .= '</td></tr></table>' . '</td></tr></table>' . '</body></html>'; return $template; }